sourcecode 1.4.0__py3-none-any.whl → 1.6.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.
sourcecode/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.4.0"
3
+ __version__ = "1.6.0"
@@ -182,8 +182,19 @@ class ArchitectureAnalyzer:
182
182
  ddd_result = self._detect_ddd(sm.file_paths)
183
183
  if ddd_result is not None:
184
184
  ddd_pattern, ddd_layers, ddd_contexts, ddd_layer_names = ddd_result
185
- domains_for_ddd = self._cluster_domains(filtered) if len(filtered) >= 2 else []
186
185
  module_files = self._build_ddd_module_files(sm.file_paths, ddd_contexts)
186
+ # Use DDD bounded context names as domains so --architecture shows each
187
+ # context as a distinct domain instead of collapsing all files under
188
+ # the Maven path segment (e.g. "java").
189
+ domains_for_ddd = [
190
+ ArchitectureDomain(
191
+ name=n,
192
+ files=module_files.get(n, []),
193
+ role="DDD bounded context",
194
+ confidence="high",
195
+ )
196
+ for n in ddd_contexts
197
+ ]
187
198
  bc_list = [
188
199
  BoundedContext(name=n, modules=module_files.get(n, []), confidence="high")
189
200
  for n in ddd_contexts
@@ -1172,6 +1172,51 @@ def _detect_role(path: str, contract: FileContract) -> str:
1172
1172
  return "util"
1173
1173
 
1174
1174
 
1175
+ # ---------------------------------------------------------------------------
1176
+ # MyBatis XML mapper extractor
1177
+ # ---------------------------------------------------------------------------
1178
+
1179
+ def _extract_mybatis_xml(rel_path: str, source: str) -> FileContract:
1180
+ """Extract namespace and SQL operations from a MyBatis *Mapper.xml file."""
1181
+ import re as _re
1182
+ from xml.etree import ElementTree
1183
+
1184
+ _NS_STRIP = _re.compile(r"\{[^}]+\}")
1185
+ _SQL_OPS = frozenset({"select", "insert", "update", "delete"})
1186
+
1187
+ exports: list[ExportRecord] = []
1188
+ namespace: str | None = None
1189
+
1190
+ try:
1191
+ root_elem = ElementTree.fromstring(source.encode("utf-8"))
1192
+ namespace = root_elem.get("namespace") or None
1193
+ for elem in root_elem:
1194
+ tag = _NS_STRIP.sub("", elem.tag).lower()
1195
+ if tag in _SQL_OPS:
1196
+ op_id = (elem.get("id") or "").strip()
1197
+ if op_id:
1198
+ # type_ref carries select/insert/update/delete for the serializer
1199
+ exports.append(ExportRecord(kind="query", name=op_id, type_ref=tag))
1200
+ except Exception:
1201
+ return FileContract(
1202
+ path=rel_path,
1203
+ language="mybatis-xml",
1204
+ role="mybatis-mapper",
1205
+ extraction_method="heuristic",
1206
+ limitations=["xml_parse_error: failed to parse mapper XML"],
1207
+ )
1208
+
1209
+ deps = [f"namespace:{namespace}"] if namespace else []
1210
+ return FileContract(
1211
+ path=rel_path,
1212
+ language="mybatis-xml",
1213
+ role="mybatis-mapper",
1214
+ exports=exports,
1215
+ dependencies=deps,
1216
+ extraction_method="heuristic",
1217
+ )
1218
+
1219
+
1175
1220
  # ---------------------------------------------------------------------------
1176
1221
  # AstExtractor public class
1177
1222
  # ---------------------------------------------------------------------------
@@ -1191,6 +1236,16 @@ class AstExtractor:
1191
1236
  return self._ts_ok
1192
1237
 
1193
1238
  def extract(self, path: Path, root: Optional[Path] = None) -> Optional[FileContract]:
1239
+ # MyBatis mapper XML — handled before the language map lookup so .xml
1240
+ # files are only processed when they match the mapper naming convention.
1241
+ if path.suffix.lower() == ".xml" and path.name.endswith("Mapper.xml"):
1242
+ try:
1243
+ source = path.read_text(encoding="utf-8", errors="replace")
1244
+ except OSError:
1245
+ return None
1246
+ rel_path = str(path.relative_to(root)).replace("\\", "/") if root else path.name
1247
+ return _extract_mybatis_xml(rel_path, source)
1248
+
1194
1249
  ext = path.suffix.lower()
1195
1250
  language = _LANGUAGE_MAP.get(ext)
1196
1251
  if language is None:
sourcecode/cli.py CHANGED
@@ -1399,12 +1399,6 @@ def main(
1399
1399
  ))
1400
1400
  sm = _replace(sm, pipeline_trace=_trace.build_trace())
1401
1401
 
1402
- # P3-B: Auto-switch to centrality ranking when DDD layout detected
1403
- if (rank_by == "relevance"
1404
- and sm.architecture is not None
1405
- and sm.architecture.pattern == "ddd"):
1406
- rank_by = "centrality"
1407
-
1408
1402
  # Contract pipeline — runs for mode=contract|standard|deep|hybrid (skip for raw)
1409
1403
  _is_contract_mode = mode in ("contract", "standard")
1410
1404
  _pipeline_error = False
@@ -219,9 +219,18 @@ class ContractPipeline:
219
219
  fname = Path(pn).name
220
220
  return any(fname.startswith(pat) or f".{pat.strip('.')}" in fname for pat in _TEST_PATTERNS)
221
221
 
222
+ def _is_extractable(p: str) -> bool:
223
+ suf = Path(p).suffix.lower()
224
+ if suf in _SRC_EXTENSIONS:
225
+ return True
226
+ # MyBatis mapper XML files — only *Mapper.xml, not all XML
227
+ if suf == ".xml" and p.endswith("Mapper.xml"):
228
+ return True
229
+ return False
230
+
222
231
  src_paths = [
223
232
  p for p in file_paths
224
- if Path(p).suffix.lower() in _SRC_EXTENSIONS
233
+ if _is_extractable(p)
225
234
  and not scorer.is_noise(p)
226
235
  and (symbol is not None or changed_only or not _is_test(p))
227
236
  ]
@@ -231,10 +240,18 @@ class ContractPipeline:
231
240
 
232
241
  # Apply max_files cap — bypass when symbol search to ensure defining files are found.
233
242
  # A symbol query over a large repo needs all files; result set is small after filtering.
243
+ # MyBatis Mapper.xml contracts rank below Java files on path score alone (.xml has no
244
+ # suffix boost). Give them the same priority slot as entry_points so they survive the cap.
245
+ def _is_priority(p: str) -> bool:
246
+ if p in entry_paths:
247
+ return True
248
+ name = p.rsplit("/", 1)[-1]
249
+ return name.lower().endswith("mapper.xml")
250
+
234
251
  if symbol is None and len(src_paths) > self.max_files:
235
252
  src_paths = sorted(
236
253
  src_paths,
237
- key=lambda p: (p in entry_paths, scorer.score(p)),
254
+ key=lambda p: (_is_priority(p), scorer.score(p)),
238
255
  reverse=True,
239
256
  )[:self.max_files]
240
257
 
@@ -353,7 +370,9 @@ class ContractPipeline:
353
370
 
354
371
  def _rank(self, contracts: list[FileContract], rank_by: RankStrategy) -> list[FileContract]:
355
372
  if rank_by == "centrality":
356
- return sorted(contracts, key=lambda c: (-(c.fan_in + c.fan_out), c.path))
373
+ # Entrypoints (REST controllers, main classes) surface first even in centrality mode:
374
+ # they have low fan_in (not imported) but are the primary API surface.
375
+ return sorted(contracts, key=lambda c: (-c.is_entrypoint, -(c.fan_in + c.fan_out), c.path))
357
376
  if rank_by == "git-churn":
358
377
  return sorted(contracts, key=lambda c: (-c.is_changed, -c.relevance_score, c.path))
359
378
  # Default: relevance — path breaks ties deterministically
@@ -1191,6 +1191,18 @@ class DependencyAnalyzer:
1191
1191
  limitations: list[str] = []
1192
1192
  if not records:
1193
1193
  limitations.append("java: pom.xml sin dependencias parseables (puede usar BOM o propiedades)")
1194
+
1195
+ # Warn when Spring Boot BOM manages transitive deps — they can't be resolved statically.
1196
+ parent_artifact_local = (
1197
+ root_elem.findtext(f"{ns}parent/{ns}artifactId") or ""
1198
+ ).strip() if parent_elem is not None else ""
1199
+ if parent_artifact_local == "spring-boot-starter-parent" and parent_version:
1200
+ limitations.append(
1201
+ f"spring_boot_bom_detected: transitive deps managed by Spring Boot BOM "
1202
+ f"v{parent_version}, not resolved statically. "
1203
+ "Run 'mvn dependency:tree' for the full transitive tree."
1204
+ )
1205
+
1194
1206
  return records, limitations
1195
1207
 
1196
1208
  def _analyze_gradle(self, root: Path) -> tuple[list[DependencyRecord], list[str]]:
@@ -18,7 +18,7 @@ _NS_TAG_RE = re.compile(r"\{[^}]+\}")
18
18
 
19
19
  _MAX_FILE_SIZE = 256 * 1024 # 256 KB
20
20
  _MAX_JAVA_ENTRY_SCAN = 1000
21
- _MAX_ANNOTATION_ENTRY_POINTS = 500
21
+ _MAX_ANNOTATION_ENTRY_POINTS = 1000
22
22
 
23
23
  _REST_CONTROLLER_RE = re.compile(r'@RestController\b')
24
24
  _MVC_CONTROLLER_RE = re.compile(r'@Controller\b')
@@ -225,6 +225,12 @@ class JavaDetector(AbstractDetector):
225
225
  all_paths = flatten_file_tree(context.file_tree)
226
226
  all_java = [p for p in all_paths if p.endswith(".java")]
227
227
 
228
+ # Augment with a direct scan of standard Java source roots for Controller-named
229
+ # files that the depth-limited file_tree scanner may have missed.
230
+ # DDD layouts place REST controllers at depth 10+ (e.g.
231
+ # src/main/java/com/org/app/ddd/domain/infraestructure/rest/XxxRestController.java).
232
+ self._augment_deep_java_controllers(context, all_java)
233
+
228
234
  # 1. @SpringBootApplication entry: Application.java / Main.java by name
229
235
  app_candidates = [
230
236
  p for p in all_java
@@ -236,10 +242,12 @@ class JavaDetector(AbstractDetector):
236
242
  ]
237
243
 
238
244
  # 2. Annotation-based scan: @RestController, @WebFilter, FilterRegistrationBean
239
- scan_candidates = [
240
- p for p in all_java
241
- if "/test/" not in p and "/tests/" not in p
242
- ][:_MAX_JAVA_ENTRY_SCAN]
245
+ # Prioritize Controller-named files so all REST controllers are detected
246
+ # even in large codebases where total files > _MAX_JAVA_ENTRY_SCAN.
247
+ _non_test = [p for p in all_java if "/test/" not in p and "/tests/" not in p]
248
+ _ctrl_files = [p for p in _non_test if "Controller" in p]
249
+ _other_files = [p for p in _non_test if "Controller" not in p]
250
+ scan_candidates = _ctrl_files + _other_files[:max(0, _MAX_JAVA_ENTRY_SCAN - len(_ctrl_files))]
243
251
 
244
252
  annotation_eps: list[EntryPoint] = []
245
253
  for rel_path in scan_candidates:
@@ -268,6 +276,40 @@ class JavaDetector(AbstractDetector):
268
276
  unique_eps.append(ep)
269
277
  return unique_eps
270
278
 
279
+ def _augment_deep_java_controllers(self, context: DetectionContext, all_java: list[str]) -> None:
280
+ """Scan standard Java source roots for *Controller*.java files not in all_java.
281
+
282
+ The depth-limited file_tree scanner misses files at depth >= max_depth.
283
+ DDD layouts place REST controllers deep (e.g. depth 10+), so we supplement
284
+ with a direct filesystem walk scoped to the standard Maven/Gradle source root.
285
+ """
286
+ import os as _os
287
+ existing = set(all_java)
288
+ # Standard Java source root candidates (Maven first, then Gradle/other)
289
+ _SRC_ROOTS = ("src/main/java", "src/main/kotlin", "src/java", "src")
290
+ for src_root_name in _SRC_ROOTS:
291
+ src_root = context.root / src_root_name
292
+ if not src_root.is_dir():
293
+ continue
294
+ try:
295
+ for dirpath, _dirs, filenames in _os.walk(str(src_root)):
296
+ for fname in filenames:
297
+ if "Controller" not in fname or not fname.endswith(".java"):
298
+ continue
299
+ full = Path(dirpath) / fname
300
+ if full.is_symlink():
301
+ continue
302
+ try:
303
+ rel = str(full.relative_to(context.root)).replace("\\", "/")
304
+ if rel not in existing:
305
+ all_java.append(rel)
306
+ existing.add(rel)
307
+ except ValueError:
308
+ pass
309
+ except OSError:
310
+ pass
311
+ return # use only first matching source root
312
+
271
313
  def _scan_java_file_for_entry_points(self, abs_path: Path, rel_path: str) -> list[EntryPoint]:
272
314
  try:
273
315
  if abs_path.stat().st_size > _MAX_FILE_SIZE:
sourcecode/serializer.py CHANGED
@@ -1110,6 +1110,10 @@ def standard_view(sm: SourceMap, *, include_tree: bool = False) -> dict[str, Any
1110
1110
 
1111
1111
  if sm.semantic_summary is not None and sm.semantic_summary.requested:
1112
1112
  result["semantic_summary"] = asdict(sm.semantic_summary)
1113
+ # Backward compat: also emit hotspots at top level (moved to semantic_summary in v1.5.0).
1114
+ # Consumers reading d["hotspots"] directly still work.
1115
+ if sm.semantic_summary.hotspots:
1116
+ result["hotspots"] = sm.semantic_summary.hotspots[:10]
1113
1117
  # Defensive filter: never emit objects with null required fields.
1114
1118
  # A null entry in these arrays is worse than a shorter array — it causes
1115
1119
  # agents to misinterpret the analysis as valid when it is not.
@@ -1439,10 +1443,31 @@ def _serialize_contract_java(c: Any) -> dict[str, Any]:
1439
1443
  return item
1440
1444
 
1441
1445
 
1446
+ def _serialize_contract_mybatis_xml(c: Any) -> dict[str, Any]:
1447
+ """Serialize a MyBatis *Mapper.xml contract."""
1448
+ item: dict[str, Any] = {"path": c.path, "language": "mybatis-xml"}
1449
+ # Extract namespace stored as "namespace:<fqn>" in dependencies
1450
+ for dep in (c.dependencies or []):
1451
+ if dep.startswith("namespace:"):
1452
+ item["namespace"] = dep[len("namespace:"):]
1453
+ break
1454
+ exports_out: list[dict] = []
1455
+ for e in c.exports:
1456
+ entry: dict = {"kind": e.kind, "name": e.name}
1457
+ if getattr(e, "type_ref", None):
1458
+ entry["type"] = e.type_ref
1459
+ exports_out.append(entry)
1460
+ if exports_out:
1461
+ item["exports"] = exports_out
1462
+ return item
1463
+
1464
+
1442
1465
  def _serialize_contract_minimal(c: Any) -> dict[str, Any]:
1443
1466
  """Serialize one FileContract to minimal format."""
1444
1467
  if getattr(c, "language", None) == "java":
1445
1468
  return _serialize_contract_java(c)
1469
+ if getattr(c, "language", None) == "mybatis-xml":
1470
+ return _serialize_contract_mybatis_xml(c)
1446
1471
  item: dict[str, Any] = {"path": c.path, "role": c.role}
1447
1472
 
1448
1473
  if c.is_changed:
@@ -1560,6 +1585,11 @@ def _contract_view_standard(
1560
1585
  if contracts:
1561
1586
  serialized: list[dict[str, Any]] = []
1562
1587
  for c in contracts:
1588
+ if getattr(c, "language", None) == "mybatis-xml":
1589
+ item = _serialize_contract_mybatis_xml(c)
1590
+ item["relevance_score"] = round(c.relevance_score, 3)
1591
+ serialized.append(item)
1592
+ continue
1563
1593
  item: dict[str, Any] = {
1564
1594
  "path": c.path,
1565
1595
  "language": c.language,
@@ -1609,7 +1639,7 @@ def _contract_view_standard(
1609
1639
  item["ranking_reasons"] = non_trivial
1610
1640
  item["method"] = c.extraction_method
1611
1641
  serialized.append(item)
1612
- result["file_contracts"] = serialized
1642
+ result["contracts"] = serialized
1613
1643
 
1614
1644
  # Optional analysis sections (deep mode or when analyzers ran)
1615
1645
  if include_optional:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.4.0
3
+ Version: 1.6.0
4
4
  Summary: Deterministic codebase context for AI coding agents
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -1,18 +1,18 @@
1
- sourcecode/__init__.py,sha256=7vIUDLA50NqbZpGWjbWgBBlC-d6NEDyVgAmqGi5Qy1I,102
1
+ sourcecode/__init__.py,sha256=s3ubr020DVq1rHjxHihNLq1cecOGqF6x7czXl3I96Sk,102
2
2
  sourcecode/adaptive_scanner.py,sha256=6dh34C2qZXyRbw-8xBhbEwDdXanM6CRFRWayVoYITnA,10190
3
- sourcecode/architecture_analyzer.py,sha256=qzDW3_lQv__czQ-qs6AqEEoMvTfhfp7M7kNslPuQy7A,32128
3
+ sourcecode/architecture_analyzer.py,sha256=oPmGPf9_p6y8Z7SIGHEu57nVYwxycIovDzBvlHl-l5k,32557
4
4
  sourcecode/architecture_summary.py,sha256=J9yoLgh8wXwIRrT6q6JooB6PekivbOEYpJz4BUXdalk,20545
5
- sourcecode/ast_extractor.py,sha256=L_MbLz22TdF_P21-nSSxSG9hZcMdtUjRKmPTWFx5NmU,47732
5
+ sourcecode/ast_extractor.py,sha256=XgrZg2DcWcUm9r87cRG3KGO7IK2TIL_N-CvhSbUmmh4,49901
6
6
  sourcecode/classifier.py,sha256=GKTMN8qKZX7ponSwDJfN08RrasI4CVpq1_gFBgEopps,7093
7
- sourcecode/cli.py,sha256=_oYoP4_AAFGw0gg-FBGn_DKh7y49xmwuL6D_Bafl2w4,74497
7
+ sourcecode/cli.py,sha256=91GUbfLS86RMTurkhoMWFUPqby30AEgzkLDIvJui9Fc,74262
8
8
  sourcecode/code_notes_analyzer.py,sha256=rRd8bFYV0krjlxxQV0wenwE9K7pVpUQSR7KvSvUQKw4,9226
9
9
  sourcecode/confidence_analyzer.py,sha256=HcaewB2pZaZ_hfKrZWtr_yPMY2-CxS1zzTUD7c4argc,13188
10
10
  sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
11
11
  sourcecode/context_summarizer.py,sha256=CiQrfBEzun949bWvmLabWoj2HhPn6Lw62ofqnsy0FlQ,6503
12
12
  sourcecode/contract_model.py,sha256=nRxJKPMs1VHwFTa8AVXhGmaLjti3Lr2sjHDpWgv1bfE,3917
13
- sourcecode/contract_pipeline.py,sha256=mjI1MgFebZW6uQAei0aheW7VPmZntH5eVXiyqkE2N9w,26332
13
+ sourcecode/contract_pipeline.py,sha256=GX-AyUxULywnGHYpmVlMDGsiHEsdegGUHfLVn7Ffvis,27238
14
14
  sourcecode/coverage_parser.py,sha256=q0LeZJaX1bnntLu-ImksdBsMlpsVmk_iUfSaB4eaJGo,19702
15
- sourcecode/dependency_analyzer.py,sha256=LGEcpbpPl7cO4-TjPO02XAJRwiAhE1ad0QQDIjAqmEA,55595
15
+ sourcecode/dependency_analyzer.py,sha256=p4ljXhkcGBbFlhaZuPrsjOVjDXaKLTg0Gor2p4qFPP0,56208
16
16
  sourcecode/doc_analyzer.py,sha256=a1CIClCNmfYM3ku4bdgwHQpmb6Js4wdJZ1V5EYLo04I,24345
17
17
  sourcecode/entrypoint_classifier.py,sha256=gvKgl0f5T8ol1r4JMmkeqGHuZTfZJiOwFOWdc7EYwYw,4061
18
18
  sourcecode/env_analyzer.py,sha256=GxCidahAAIptTdDFIlVB6URd4HBnBlIX_SqUov3MBRQ,22076
@@ -29,7 +29,7 @@ sourcecode/runtime_classifier.py,sha256=zWX3r3HCKHc-qtIobErOa8aKMmaoPYREtJKvPcBG
29
29
  sourcecode/scanner.py,sha256=aM3h9-DCQ3xKpeHpHYdo2vX6T5P95HA_YwZbkAVNwmo,8288
30
30
  sourcecode/schema.py,sha256=rNxpDUgOfvJDzz6DKitL-5_0UAh0YomNwTsSSe8PafY,24066
31
31
  sourcecode/semantic_analyzer.py,sha256=12TwXYkYbDcBdu0heX_EmfPM2EkO8a_r5osf0SaeQbs,88956
32
- sourcecode/serializer.py,sha256=qqqH84ensw31P6ElqivPUwmYtb6AR7KTENM2nFJSde4,68403
32
+ sourcecode/serializer.py,sha256=6dHvWc-iqqCjpu9-FcAXqtpKEO4mO3AZVzWT7Tphajo,69736
33
33
  sourcecode/summarizer.py,sha256=ZuzIdm3t8A-d5MuQL0TSNLrd-L0IQIuguIxeNXMNJf8,16070
34
34
  sourcecode/tree_utils.py,sha256=Fj9OIuUksBvgibNd3feog0sMDjVypJzPexp5lvMoYWI,1424
35
35
  sourcecode/workspace.py,sha256=X_6NmNnitvT3_38V-JDChydo_sR68s249hLFlrQskU0,8271
@@ -42,7 +42,7 @@ sourcecode/detectors/elixir.py,sha256=jCpvt5Yi6jvplc80ovRtWh17q-11ZGo9qX7o8b57TJ
42
42
  sourcecode/detectors/go.py,sha256=2r66uRQfeTWsqxr4HDhT6vExZErby0t46QXLHVBRv9w,2782
43
43
  sourcecode/detectors/heuristic.py,sha256=bCqqgbHavl4Sse3dqT8mwmo1wAdgeJr7VyXOmfClLKo,3387
44
44
  sourcecode/detectors/hybrid.py,sha256=IGFRUVsAZ1ooRlFdznCeJAV6vy1yVDx-VyghvLtddXc,9101
45
- sourcecode/detectors/java.py,sha256=acRCelw-SdxaPsJsBJEGybFTqe1esPpgqKibB9xbJoI,16277
45
+ sourcecode/detectors/java.py,sha256=XAnVOmq7yXgO_4UGlIFIVgNRtyWZQEVsDDu3sa5mTsk,18669
46
46
  sourcecode/detectors/jvm_ext.py,sha256=EgHJ5W8EE-ZTN9V607mVzohyKgZE8Mc2jCi-DF8RAZU,2616
47
47
  sourcecode/detectors/nodejs.py,sha256=7fsyAmrGkkguX6U80HUQpIe9MRaYyi_A7zbaRtmFmGc,13097
48
48
  sourcecode/detectors/parsers.py,sha256=ugPg8yNUf0Ai1gA7Fnn6wAkYGFjTxRodSP3IeViYJJ4,2290
@@ -60,8 +60,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
60
60
  sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
61
61
  sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
62
62
  sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
63
- sourcecode-1.4.0.dist-info/METADATA,sha256=vVglc6X8WawZuI4yHs6aWszUHNLtVowyFpCRRSA4INM,20411
64
- sourcecode-1.4.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
65
- sourcecode-1.4.0.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
66
- sourcecode-1.4.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
67
- sourcecode-1.4.0.dist-info/RECORD,,
63
+ sourcecode-1.6.0.dist-info/METADATA,sha256=hB2-2Dm1fOj4fkPULr9PEH3aSLhD7ni4cPDxGAjmgB0,20411
64
+ sourcecode-1.6.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
65
+ sourcecode-1.6.0.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
66
+ sourcecode-1.6.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
67
+ sourcecode-1.6.0.dist-info/RECORD,,