structurize 2.18.2__py3-none-any.whl → 2.20.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,432 @@
1
+ """
2
+ Central dependency version resolver for Avrotize code generation templates.
3
+
4
+ This module provides functions to read dependency versions from central project files
5
+ located in avrotize/dependencies/{language}/{runtime_version}/. These files are
6
+ monitored by Dependabot, which proposes updates when new versions are available.
7
+
8
+ Supported languages:
9
+ - cs: C# (.NET) - reads from dependencies.csproj
10
+ - java: Java - reads from pom.xml
11
+ - py: Python - reads from requirements.txt
12
+ - ts: TypeScript - reads from package.json
13
+ - go: Go - reads from go.mod
14
+ - rust: Rust - reads from Cargo.toml
15
+ - cpp: C++ - reads from vcpkg.json
16
+ """
17
+
18
+ import json
19
+ import os
20
+ import re
21
+ import xml.etree.ElementTree as ET
22
+ from typing import Optional
23
+
24
+
25
+ def get_dependency(language: str, runtime_version: str, dependency_name: str) -> str:
26
+ """
27
+ Get the dependency declaration for a specific language and dependency.
28
+
29
+ Args:
30
+ language: Language identifier ('cs', 'java', 'py/python', 'ts/typescript', 'go', 'rust', 'cpp')
31
+ runtime_version: Runtime version identifier (e.g., 'net90', 'jdk21', 'py312')
32
+ dependency_name: The dependency identifier. For Java, can use 'groupId:artifactId'
33
+ syntax to disambiguate (e.g., 'org.junit.jupiter:junit-jupiter')
34
+
35
+ Returns:
36
+ The dependency declaration string in the appropriate format for the language.
37
+
38
+ Raises:
39
+ ValueError: If the language is unsupported or dependency is not found.
40
+ """
41
+ # Determine the path to the central dependency file
42
+ deps_dir = os.path.join(os.path.dirname(__file__), 'dependencies')
43
+
44
+ # Normalize language aliases
45
+ lang = language.lower()
46
+ if lang in ('python', 'py'):
47
+ lang = 'py'
48
+ elif lang in ('typescript', 'ts'):
49
+ lang = 'ts'
50
+ elif lang in ('csharp', 'c#'):
51
+ lang = 'cs'
52
+
53
+ if lang == 'cs':
54
+ master_project_path = os.path.join(deps_dir, f'cs/{runtime_version}/dependencies.csproj')
55
+ elif lang == 'java':
56
+ master_project_path = os.path.join(deps_dir, f'java/{runtime_version}/pom.xml')
57
+ elif lang == 'py':
58
+ master_project_path = os.path.join(deps_dir, f'python/{runtime_version}/requirements.txt')
59
+ elif lang == 'ts':
60
+ master_project_path = os.path.join(deps_dir, f'typescript/{runtime_version}/package.json')
61
+ elif lang == 'go':
62
+ master_project_path = os.path.join(deps_dir, f'go/{runtime_version}/go.mod')
63
+ elif lang == 'rust':
64
+ master_project_path = os.path.join(deps_dir, f'rust/{runtime_version}/Cargo.toml')
65
+ elif lang == 'cpp':
66
+ master_project_path = os.path.join(deps_dir, f'cpp/{runtime_version}/vcpkg.json')
67
+ else:
68
+ raise ValueError(f"Unsupported language: {language}")
69
+
70
+ if not os.path.exists(master_project_path):
71
+ raise ValueError(f"Dependency file not found: {master_project_path}")
72
+
73
+ if lang == 'cs':
74
+ return _get_nuget_dependency(master_project_path, dependency_name)
75
+ elif lang == 'java':
76
+ return _get_maven_dependency(master_project_path, dependency_name)
77
+ elif lang == 'py':
78
+ return _get_python_dependency(master_project_path, dependency_name)
79
+ elif lang == 'ts':
80
+ return _get_npm_dependency(master_project_path, dependency_name)
81
+ elif lang == 'go':
82
+ return _get_go_dependency(master_project_path, dependency_name)
83
+ elif lang == 'rust':
84
+ return _get_cargo_dependency(master_project_path, dependency_name)
85
+ elif lang == 'cpp':
86
+ return _get_vcpkg_dependency(master_project_path, dependency_name)
87
+
88
+ raise ValueError(f"Unsupported language: {language}")
89
+
90
+
91
+ def get_dependency_version(language: str, runtime_version: str, dependency_name: str) -> str:
92
+ """
93
+ Get just the version string for a dependency.
94
+
95
+ Args:
96
+ language: Language identifier (cs, java, py/python, ts/typescript, go, rust, cpp)
97
+ runtime_version: Runtime version identifier
98
+ dependency_name: The dependency identifier
99
+
100
+ Returns:
101
+ The version string only (e.g., '1.12.0', '2.18.2')
102
+ """
103
+ deps_dir = os.path.join(os.path.dirname(__file__), 'dependencies')
104
+
105
+ # Normalize language aliases
106
+ lang = language.lower()
107
+ if lang in ('python', 'py'):
108
+ lang = 'py'
109
+ elif lang in ('typescript', 'ts'):
110
+ lang = 'ts'
111
+ elif lang in ('csharp', 'c#'):
112
+ lang = 'cs'
113
+
114
+ if lang == 'cs':
115
+ master_project_path = os.path.join(deps_dir, f'cs/{runtime_version}/dependencies.csproj')
116
+ return _get_nuget_version(master_project_path, dependency_name)
117
+ elif lang == 'java':
118
+ master_project_path = os.path.join(deps_dir, f'java/{runtime_version}/pom.xml')
119
+ return _get_maven_version(master_project_path, dependency_name)
120
+ elif lang == 'py':
121
+ master_project_path = os.path.join(deps_dir, f'python/{runtime_version}/requirements.txt')
122
+ return _get_python_version(master_project_path, dependency_name)
123
+ elif lang == 'ts':
124
+ master_project_path = os.path.join(deps_dir, f'typescript/{runtime_version}/package.json')
125
+ return _get_npm_version(master_project_path, dependency_name)
126
+ elif lang == 'go':
127
+ master_project_path = os.path.join(deps_dir, f'go/{runtime_version}/go.mod')
128
+ return _get_go_version(master_project_path, dependency_name)
129
+ elif lang == 'rust':
130
+ master_project_path = os.path.join(deps_dir, f'rust/{runtime_version}/Cargo.toml')
131
+ return _get_cargo_version(master_project_path, dependency_name)
132
+ elif lang == 'cpp':
133
+ master_project_path = os.path.join(deps_dir, f'cpp/{runtime_version}/vcpkg.json')
134
+ return _get_vcpkg_version(master_project_path, dependency_name)
135
+
136
+ raise ValueError(f"Unsupported language: {language}")
137
+
138
+
139
+ def _get_nuget_dependency(project_path: str, package_name: str) -> str:
140
+ """Get NuGet PackageReference XML element."""
141
+ tree = ET.parse(project_path)
142
+ root = tree.getroot()
143
+
144
+ for package in root.findall(".//PackageReference"):
145
+ if package.get('Include') == package_name:
146
+ return ET.tostring(package, encoding='unicode').strip()
147
+
148
+ raise ValueError(f"Dependency '{package_name}' not found in {project_path}")
149
+
150
+
151
+ def _get_nuget_version(project_path: str, package_name: str) -> str:
152
+ """Get NuGet package version."""
153
+ tree = ET.parse(project_path)
154
+ root = tree.getroot()
155
+
156
+ for package in root.findall(".//PackageReference"):
157
+ if package.get('Include') == package_name:
158
+ return package.get('Version', '')
159
+
160
+ raise ValueError(f"Dependency '{package_name}' not found in {project_path}")
161
+
162
+
163
+ def _get_maven_dependency(project_path: str, dependency_name: str) -> str:
164
+ """Get Maven dependency XML element."""
165
+ tree = ET.parse(project_path)
166
+ root = tree.getroot()
167
+
168
+ # Support groupId:artifactId syntax for disambiguation
169
+ if ':' in dependency_name:
170
+ target_group_id, target_artifact_id = dependency_name.split(':', 1)
171
+ else:
172
+ target_group_id = None
173
+ target_artifact_id = dependency_name
174
+
175
+ for dependency in root.findall(".//dependency"):
176
+ artifact_id = dependency.find('artifactId')
177
+ group_id = dependency.find('groupId')
178
+
179
+ if artifact_id is not None and artifact_id.text == target_artifact_id:
180
+ # If groupId was specified, verify it matches
181
+ if target_group_id is not None:
182
+ if group_id is None or group_id.text != target_group_id:
183
+ continue
184
+ return ET.tostring(dependency, encoding='unicode').strip()
185
+
186
+ raise ValueError(f"Dependency '{dependency_name}' not found in {project_path}")
187
+
188
+
189
+ def _get_maven_version(project_path: str, dependency_name: str) -> str:
190
+ """Get Maven dependency version."""
191
+ tree = ET.parse(project_path)
192
+ root = tree.getroot()
193
+
194
+ if ':' in dependency_name:
195
+ target_group_id, target_artifact_id = dependency_name.split(':', 1)
196
+ else:
197
+ target_group_id = None
198
+ target_artifact_id = dependency_name
199
+
200
+ for dependency in root.findall(".//dependency"):
201
+ artifact_id = dependency.find('artifactId')
202
+ group_id = dependency.find('groupId')
203
+
204
+ if artifact_id is not None and artifact_id.text == target_artifact_id:
205
+ if target_group_id is not None:
206
+ if group_id is None or group_id.text != target_group_id:
207
+ continue
208
+ version_elem = dependency.find('version')
209
+ if version_elem is not None and version_elem.text:
210
+ return version_elem.text
211
+
212
+ raise ValueError(f"Dependency '{dependency_name}' not found in {project_path}")
213
+
214
+
215
+ def _get_python_dependency(project_path: str, package_name: str) -> str:
216
+ """Get Python dependency from requirements.txt."""
217
+ with open(project_path, 'r', encoding='utf-8') as f:
218
+ content = f.read()
219
+
220
+ for line in content.split('\n'):
221
+ line = line.strip()
222
+ if not line or line.startswith('#'):
223
+ continue
224
+
225
+ # Match package name with version specifier
226
+ match = re.match(r'^([a-zA-Z0-9_-]+)\s*([>=<!=]+.*)$', line)
227
+ if match:
228
+ pkg_name = match.group(1)
229
+ version_spec = match.group(2)
230
+ if pkg_name == package_name:
231
+ return f"{pkg_name}{version_spec}"
232
+
233
+ raise ValueError(f"Dependency '{package_name}' not found in {project_path}")
234
+
235
+
236
+ def _get_python_version(project_path: str, package_name: str) -> str:
237
+ """Get Python package version."""
238
+ with open(project_path, 'r', encoding='utf-8') as f:
239
+ content = f.read()
240
+
241
+ for line in content.split('\n'):
242
+ line = line.strip()
243
+ if not line or line.startswith('#'):
244
+ continue
245
+
246
+ match = re.match(r'^([a-zA-Z0-9_-]+)\s*[>=<!=]+\s*(.+)$', line)
247
+ if match:
248
+ pkg_name = match.group(1)
249
+ version = match.group(2).strip()
250
+ if pkg_name == package_name:
251
+ return version
252
+
253
+ raise ValueError(f"Dependency '{package_name}' not found in {project_path}")
254
+
255
+
256
+ def _get_npm_dependency(project_path: str, package_name: str) -> str:
257
+ """Get npm dependency as JSON key-value pair."""
258
+ with open(project_path, 'r', encoding='utf-8') as f:
259
+ package_data = json.load(f)
260
+
261
+ dependencies = package_data.get('dependencies', {})
262
+ dev_dependencies = package_data.get('devDependencies', {})
263
+
264
+ if package_name in dependencies:
265
+ version = dependencies[package_name]
266
+ return f'"{package_name}": "{version}"'
267
+
268
+ if package_name in dev_dependencies:
269
+ version = dev_dependencies[package_name]
270
+ return f'"{package_name}": "{version}"'
271
+
272
+ raise ValueError(f"Dependency '{package_name}' not found in {project_path}")
273
+
274
+
275
+ def _get_npm_version(project_path: str, package_name: str) -> str:
276
+ """Get npm package version."""
277
+ with open(project_path, 'r', encoding='utf-8') as f:
278
+ package_data = json.load(f)
279
+
280
+ dependencies = package_data.get('dependencies', {})
281
+ dev_dependencies = package_data.get('devDependencies', {})
282
+
283
+ if package_name in dependencies:
284
+ return dependencies[package_name].lstrip('^~')
285
+
286
+ if package_name in dev_dependencies:
287
+ return dev_dependencies[package_name].lstrip('^~')
288
+
289
+ raise ValueError(f"Dependency '{package_name}' not found in {project_path}")
290
+
291
+
292
+ def _get_go_dependency(project_path: str, module_name: str) -> str:
293
+ """Get Go module dependency line."""
294
+ with open(project_path, 'r', encoding='utf-8') as f:
295
+ content = f.read()
296
+
297
+ # Parse require block
298
+ require_pattern = re.compile(r'require\s*\(\s*([\s\S]*?)\s*\)', re.MULTILINE)
299
+ require_match = require_pattern.search(content)
300
+
301
+ if require_match:
302
+ require_block = require_match.group(1)
303
+ for line in require_block.strip().split('\n'):
304
+ line = line.strip()
305
+ if not line or line.startswith('//'):
306
+ continue
307
+ parts = line.split()
308
+ if len(parts) >= 2:
309
+ mod_path = parts[0]
310
+ version = parts[1]
311
+ if mod_path == module_name:
312
+ return f"\t{mod_path} {version}"
313
+
314
+ raise ValueError(f"Dependency '{module_name}' not found in {project_path}")
315
+
316
+
317
+ def _get_go_version(project_path: str, module_name: str) -> str:
318
+ """Get Go module version."""
319
+ with open(project_path, 'r', encoding='utf-8') as f:
320
+ content = f.read()
321
+
322
+ require_pattern = re.compile(r'require\s*\(\s*([\s\S]*?)\s*\)', re.MULTILINE)
323
+ require_match = require_pattern.search(content)
324
+
325
+ if require_match:
326
+ require_block = require_match.group(1)
327
+ for line in require_block.strip().split('\n'):
328
+ line = line.strip()
329
+ if not line or line.startswith('//'):
330
+ continue
331
+ parts = line.split()
332
+ if len(parts) >= 2:
333
+ mod_path = parts[0]
334
+ version = parts[1]
335
+ if mod_path == module_name:
336
+ return version.lstrip('v')
337
+
338
+ raise ValueError(f"Dependency '{module_name}' not found in {project_path}")
339
+
340
+
341
+ def _get_cargo_dependency(project_path: str, crate_name: str) -> str:
342
+ """Get Rust/Cargo dependency as TOML line."""
343
+ with open(project_path, 'r', encoding='utf-8') as f:
344
+ content = f.read()
345
+
346
+ # Look for dependencies in [dependencies] section
347
+ in_deps_section = False
348
+ for line in content.split('\n'):
349
+ line_stripped = line.strip()
350
+
351
+ if line_stripped == '[dependencies]':
352
+ in_deps_section = True
353
+ continue
354
+ elif line_stripped.startswith('[') and line_stripped.endswith(']'):
355
+ in_deps_section = False
356
+ continue
357
+
358
+ if in_deps_section and line_stripped:
359
+ # Match simple version: crate = "version"
360
+ simple_match = re.match(rf'^{re.escape(crate_name)}\s*=\s*"([^"]+)"', line_stripped)
361
+ if simple_match:
362
+ return line_stripped
363
+
364
+ # Match complex version: crate = { version = "...", ... }
365
+ complex_match = re.match(rf'^{re.escape(crate_name)}\s*=\s*\{{.*\}}', line_stripped)
366
+ if complex_match:
367
+ return line_stripped
368
+
369
+ raise ValueError(f"Dependency '{crate_name}' not found in {project_path}")
370
+
371
+
372
+ def _get_cargo_version(project_path: str, crate_name: str) -> str:
373
+ """Get Rust/Cargo crate version."""
374
+ with open(project_path, 'r', encoding='utf-8') as f:
375
+ content = f.read()
376
+
377
+ in_deps_section = False
378
+ for line in content.split('\n'):
379
+ line_stripped = line.strip()
380
+
381
+ if line_stripped == '[dependencies]':
382
+ in_deps_section = True
383
+ continue
384
+ elif line_stripped.startswith('[') and line_stripped.endswith(']'):
385
+ in_deps_section = False
386
+ continue
387
+
388
+ if in_deps_section and line_stripped:
389
+ # Match simple version
390
+ simple_match = re.match(rf'^{re.escape(crate_name)}\s*=\s*"([^"]+)"', line_stripped)
391
+ if simple_match:
392
+ return simple_match.group(1)
393
+
394
+ # Match complex version with version key
395
+ version_match = re.search(rf'^{re.escape(crate_name)}\s*=\s*\{{.*version\s*=\s*"([^"]+)"', line_stripped)
396
+ if version_match:
397
+ return version_match.group(1)
398
+
399
+ raise ValueError(f"Dependency '{crate_name}' not found in {project_path}")
400
+
401
+
402
+ def _get_vcpkg_dependency(project_path: str, package_name: str) -> str:
403
+ """Get vcpkg dependency as JSON object."""
404
+ with open(project_path, 'r', encoding='utf-8') as f:
405
+ vcpkg_data = json.load(f)
406
+
407
+ dependencies = vcpkg_data.get('dependencies', [])
408
+ for dep in dependencies:
409
+ if isinstance(dep, dict):
410
+ if dep.get('name') == package_name:
411
+ return json.dumps(dep, indent=2)
412
+ elif isinstance(dep, str) and dep == package_name:
413
+ return f'"{package_name}"'
414
+
415
+ raise ValueError(f"Dependency '{package_name}' not found in {project_path}")
416
+
417
+
418
+ def _get_vcpkg_version(project_path: str, package_name: str) -> str:
419
+ """Get vcpkg package version."""
420
+ with open(project_path, 'r', encoding='utf-8') as f:
421
+ vcpkg_data = json.load(f)
422
+
423
+ dependencies = vcpkg_data.get('dependencies', [])
424
+ for dep in dependencies:
425
+ if isinstance(dep, dict):
426
+ if dep.get('name') == package_name:
427
+ # Try various version keys
428
+ for version_key in ['version>=', 'version', 'version>']:
429
+ if version_key in dep:
430
+ return dep[version_key]
431
+
432
+ raise ValueError(f"Dependency '{package_name}' not found in {project_path}")