Skip to content

Commit 0ba0fd3

Browse files
author
Johan Broberg
committed
Formatting fixes
1 parent 356697e commit 0ba0fd3

File tree

1 file changed

+107
-91
lines changed

1 file changed

+107
-91
lines changed

generate_dependency_diagram.py

Lines changed: 107 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
class PackageInfo:
1616
"""Information about a package and its dependencies."""
17-
17+
1818
def __init__(self, name: str, package_type: str, path: Path):
1919
self.name = name
2020
self.package_type = package_type
@@ -24,199 +24,215 @@ def __init__(self, name: str, package_type: str, path: Path):
2424

2525
def read_pyproject_toml(path: Path) -> Dict:
2626
"""Read and parse a pyproject.toml file."""
27-
with open(path, 'rb') as f:
27+
with open(path, "rb") as f:
2828
return tomllib.load(f)
2929

3030

3131
def extract_dependencies(pyproject_data: Dict, package_names: Set[str]) -> Set[str]:
3232
"""Extract internal package dependencies from pyproject.toml data."""
3333
dependencies = set()
34-
35-
if 'project' in pyproject_data and 'dependencies' in pyproject_data['project']:
36-
for dep in pyproject_data['project']['dependencies']:
34+
35+
if "project" in pyproject_data and "dependencies" in pyproject_data["project"]:
36+
for dep in pyproject_data["project"]["dependencies"]:
3737
# Extract package name (before any version specifier)
3838
# Use regex to handle multiple version specifiers (e.g., "package>=1.0,<2.0")
39-
dep_name = re.split(r'[><=!~]', dep)[0].strip()
40-
39+
dep_name = re.split(r"[><=!~]", dep)[0].strip()
40+
4141
# Only include if it's one of our internal packages
4242
if dep_name in package_names:
4343
dependencies.add(dep_name)
44-
44+
4545
return dependencies
4646

4747

4848
def generate_mermaid_diagram(packages: List[PackageInfo]) -> str:
4949
"""Generate a Mermaid diagram from package information."""
50-
50+
5151
# Color scheme based on package types
5252
colors = {
53-
'Notifications': {'fill': '#ffcdd2', 'stroke': '#c62828', 'color': "#280505"}, # Red
54-
'Runtime': {'fill': '#bbdefb', 'stroke': '#1565c0', 'color': "#0d1a26"}, # Blue
55-
'Observability': {'fill': '#c8e6c9', 'stroke': '#2e7d32', 'color': "#142a14"}, # Green
56-
'Observability Extensions': {'fill': '#e8f5e9', 'stroke': '#66bb6a', 'color': "#1f3d1f"}, # Light Green
57-
'Tooling': {'fill': '#ffe0b2', 'stroke': '#e65100', 'color': "#331a00"}, # Orange
58-
'Tooling Extensions': {'fill': '#fff3e0', 'stroke': '#fb8c00', 'color': "#4d2600"}, # Light Orange
53+
"Notifications": {"fill": "#ffcdd2", "stroke": "#c62828", "color": "#280505"}, # Red
54+
"Runtime": {"fill": "#bbdefb", "stroke": "#1565c0", "color": "#0d1a26"}, # Blue
55+
"Observability": {"fill": "#c8e6c9", "stroke": "#2e7d32", "color": "#142a14"}, # Green
56+
"Observability Extensions": {
57+
"fill": "#e8f5e9",
58+
"stroke": "#66bb6a",
59+
"color": "#1f3d1f",
60+
}, # Light Green
61+
"Tooling": {"fill": "#ffe0b2", "stroke": "#e65100", "color": "#331a00"}, # Orange
62+
"Tooling Extensions": {
63+
"fill": "#fff3e0",
64+
"stroke": "#fb8c00",
65+
"color": "#4d2600",
66+
}, # Light Orange
5967
}
60-
61-
lines = ['```mermaid', 'graph LR']
62-
lines.append(' %% Package Nodes')
63-
68+
69+
lines = ["```mermaid", "graph LR"]
70+
lines.append(" %% Package Nodes")
71+
6472
# Create node definitions with shortened names for display
6573
node_map = {}
6674
for pkg in packages:
6775
# Create a short identifier for the node
68-
node_id = pkg.name.replace('-', '_')
76+
node_id = pkg.name.replace("-", "_")
6977
node_map[pkg.name] = node_id
70-
78+
7179
# Create display name (remove microsoft-agents-a365- prefix for cleaner display)
7280
display_name = pkg.name
7381
lines.append(f' {node_id}["{display_name}"]')
74-
75-
lines.append('')
76-
lines.append(' %% Dependencies')
77-
82+
83+
lines.append("")
84+
lines.append(" %% Dependencies")
85+
7886
# Add dependency edges
7987
for pkg in packages:
8088
if pkg.dependencies:
8189
source_id = node_map[pkg.name]
8290
for dep_name in sorted(pkg.dependencies):
8391
if dep_name in node_map:
8492
target_id = node_map[dep_name]
85-
lines.append(f' {source_id} --> {target_id}')
86-
87-
lines.append('')
88-
lines.append(' %% Styling')
89-
93+
lines.append(f" {source_id} --> {target_id}")
94+
95+
lines.append("")
96+
lines.append(" %% Styling")
97+
9098
# Group packages by type for styling
9199
packages_by_type: Dict[str, List[str]] = {}
92100
for pkg in packages:
93101
if pkg.package_type not in packages_by_type:
94102
packages_by_type[pkg.package_type] = []
95103
packages_by_type[pkg.package_type].append(node_map[pkg.name])
96-
104+
97105
# Apply styles
98106
for pkg_type, color_spec in colors.items():
99107
if pkg_type in packages_by_type:
100-
class_name = pkg_type.lower().replace(' ', '_')
101-
lines.append(f' classDef {class_name} fill:{color_spec["fill"]},stroke:{color_spec["stroke"]},color:{color_spec["color"]},stroke-width:2px')
102-
node_list = ','.join(packages_by_type[pkg_type])
103-
lines.append(f' class {node_list} {class_name}')
104-
105-
lines.append('```')
106-
107-
return '\n'.join(lines)
108+
class_name = pkg_type.lower().replace(" ", "_")
109+
lines.append(
110+
f" classDef {class_name} fill:{color_spec['fill']},stroke:{color_spec['stroke']},color:{color_spec['color']},stroke-width:2px"
111+
)
112+
node_list = ",".join(packages_by_type[pkg_type])
113+
lines.append(f" class {node_list} {class_name}")
114+
115+
lines.append("```")
116+
117+
return "\n".join(lines)
108118

109119

110120
def determine_package_type(package_name: str) -> str:
111121
"""Determine the package type based on package name."""
112-
if 'notifications' in package_name:
113-
return 'Notifications'
114-
elif 'runtime' in package_name:
115-
return 'Runtime'
116-
elif 'observability-extensions' in package_name:
117-
return 'Observability Extensions'
118-
elif 'observability' in package_name:
119-
return 'Observability'
120-
elif 'tooling-extensions' in package_name:
121-
return 'Tooling Extensions'
122-
elif 'tooling' in package_name:
123-
return 'Tooling'
122+
if "notifications" in package_name:
123+
return "Notifications"
124+
elif "runtime" in package_name:
125+
return "Runtime"
126+
elif "observability-extensions" in package_name:
127+
return "Observability Extensions"
128+
elif "observability" in package_name:
129+
return "Observability"
130+
elif "tooling-extensions" in package_name:
131+
return "Tooling Extensions"
132+
elif "tooling" in package_name:
133+
return "Tooling"
124134
else:
125-
return 'Other'
135+
return "Other"
126136

127137

128138
def main():
129139
"""Main function to generate the dependency diagram."""
130-
140+
131141
# Get repository root
132142
repo_root = Path(__file__).parent
133-
143+
134144
# Read workspace members from root pyproject.toml
135-
root_pyproject_path = repo_root / 'pyproject.toml'
145+
root_pyproject_path = repo_root / "pyproject.toml"
136146
if not root_pyproject_path.exists():
137147
print(f"Error: {root_pyproject_path} not found")
138148
return
139-
149+
140150
root_pyproject = read_pyproject_toml(root_pyproject_path)
141-
151+
142152
# Extract workspace members
143153
workspace_members = []
144-
if 'tool' in root_pyproject and 'uv' in root_pyproject['tool']:
145-
if 'workspace' in root_pyproject['tool']['uv']:
146-
workspace_members = root_pyproject['tool']['uv']['workspace'].get('members', [])
147-
154+
if "tool" in root_pyproject and "uv" in root_pyproject["tool"]:
155+
if "workspace" in root_pyproject["tool"]["uv"]:
156+
workspace_members = root_pyproject["tool"]["uv"]["workspace"].get("members", [])
157+
148158
if not workspace_members:
149159
print("Error: No workspace members found in pyproject.toml")
150160
return
151-
161+
152162
print(f"Found {len(workspace_members)} workspace members")
153-
163+
154164
# Build package configs from workspace members
155165
package_configs = []
156166
for member_path in workspace_members:
157167
# Determine package type from path
158168
pkg_type = determine_package_type(member_path)
159169
package_configs.append((member_path, pkg_type))
160-
170+
161171
# Collect all package names first and cache pyproject data
162172
all_package_names = set()
163173
packages: List[PackageInfo] = []
164174
pyproject_data_cache: Dict[str, Dict] = {}
165-
175+
166176
for path_str, pkg_type in package_configs:
167-
pyproject_path = repo_root / path_str / 'pyproject.toml'
168-
177+
pyproject_path = repo_root / path_str / "pyproject.toml"
178+
169179
if not pyproject_path.exists():
170180
print(f"Warning: {pyproject_path} not found")
171181
continue
172-
182+
173183
pyproject_data = read_pyproject_toml(pyproject_path)
174-
if 'project' not in pyproject_data or 'name' not in pyproject_data['project']:
184+
if "project" not in pyproject_data or "name" not in pyproject_data["project"]:
175185
print(f"Warning: {pyproject_path} is missing project.name field")
176186
continue
177-
pkg_name = pyproject_data['project']['name']
187+
pkg_name = pyproject_data["project"]["name"]
178188
all_package_names.add(pkg_name)
179-
189+
180190
# Cache the pyproject data to avoid reading the file again
181191
pyproject_data_cache[pkg_name] = pyproject_data
182-
192+
183193
pkg_info = PackageInfo(pkg_name, pkg_type, pyproject_path)
184194
packages.append(pkg_info)
185-
195+
186196
# Extract dependencies for each package using cached data
187197
for pkg in packages:
188198
pyproject_data = pyproject_data_cache[pkg.name]
189199
pkg.dependencies = extract_dependencies(pyproject_data, all_package_names)
190-
200+
191201
# Generate Mermaid diagram
192202
diagram = generate_mermaid_diagram(packages)
193-
203+
194204
# Write to markdown file
195-
output_path = repo_root / 'DEPENDENCIES.md'
196-
with open(output_path, 'w', encoding='utf-8') as f:
197-
f.write('# Agent 365 SDK Python Package Dependencies\n\n')
198-
f.write('This diagram shows the internal dependencies between Agent 365 SDK Python packages.\n\n')
205+
output_path = repo_root / "DEPENDENCIES.md"
206+
with open(output_path, "w", encoding="utf-8") as f:
207+
f.write("# Agent 365 SDK Python Package Dependencies\n\n")
208+
f.write(
209+
"This diagram shows the internal dependencies between Agent 365 SDK Python packages.\n\n"
210+
)
199211
f.write(diagram)
200-
f.write('\n\n')
201-
f.write('## Package Types\n\n')
202-
f.write('- **Notifications** (Red): Notification and messaging extensions\n')
203-
f.write('- **Runtime** (Blue): Core runtime components\n')
204-
f.write('- **Observability** (Green): Telemetry and monitoring core\n')
205-
f.write('- **Observability Extensions** (Light Green): Framework-specific observability integrations\n')
206-
f.write('- **Tooling** (Orange): Agent tooling SDK core\n')
207-
f.write('- **Tooling Extensions** (Light Orange): Framework-specific tooling integrations\n')
208-
212+
f.write("\n\n")
213+
f.write("## Package Types\n\n")
214+
f.write("- **Notifications** (Red): Notification and messaging extensions\n")
215+
f.write("- **Runtime** (Blue): Core runtime components\n")
216+
f.write("- **Observability** (Green): Telemetry and monitoring core\n")
217+
f.write(
218+
"- **Observability Extensions** (Light Green): Framework-specific observability integrations\n"
219+
)
220+
f.write("- **Tooling** (Orange): Agent tooling SDK core\n")
221+
f.write(
222+
"- **Tooling Extensions** (Light Orange): Framework-specific tooling integrations\n"
223+
)
224+
209225
print(f"Dependency diagram generated successfully: {output_path}")
210-
226+
211227
# Print summary
212228
print(f"\nAnalyzed {len(packages)} packages:")
213229
for pkg in sorted(packages, key=lambda p: p.name):
214230
if pkg.dependencies:
215-
deps = ', '.join(sorted(dep for dep in pkg.dependencies))
231+
deps = ", ".join(sorted(dep for dep in pkg.dependencies))
216232
print(f" {pkg.name}{deps}")
217233
else:
218234
print(f" {pkg.name} (no internal dependencies)")
219235

220236

221-
if __name__ == '__main__':
237+
if __name__ == "__main__":
222238
main()

0 commit comments

Comments
 (0)