sourcecode 2.5.0__py3-none-any.whl → 2.5.2__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
@@ -4,4 +4,4 @@ ASK Engine is the product. ``ask`` is the canonical CLI command; ``sourcecode``
4
4
  the legacy compatibility alias and the Python/PyPI package name. See
5
5
  docs/PRODUCT_IDENTITY.md (normative)."""
6
6
 
7
- __version__ = "2.5.0"
7
+ __version__ = "2.5.2"
@@ -1489,6 +1489,16 @@ def _method_body(raw_lines: list[str], start_idx: int) -> str:
1489
1489
  return "\n".join(out)
1490
1490
 
1491
1491
 
1492
+ # Intra-class call-site matcher: a bare `name(` (not preceded by a word char or '.')
1493
+ # OR an explicit `this.name(`. Group 1 = bare callee, group 2 = this-qualified callee.
1494
+ # Encodes the SAME two match forms the old per-sibling regex used, so the derived edge
1495
+ # set is byte-identical — but as ONE alternation scanned once per body instead of once
1496
+ # per sibling name (see _intra_class_call_edges perf note).
1497
+ _INTRA_CALL_RE = re.compile(
1498
+ r'(?<![\w.])([A-Za-z_]\w*)\s*\(' r'|\bthis\s*\.\s*([A-Za-z_]\w*)\s*\('
1499
+ )
1500
+
1501
+
1492
1502
  def _intra_class_call_edges(symbols: list[SymbolRecord], source: str) -> list[RelationEdge]:
1493
1503
  """Method-level `calls` edges for intra-class invocations.
1494
1504
 
@@ -1528,13 +1538,20 @@ def _intra_class_call_edges(symbols: list[SymbolRecord], source: str) -> list[Re
1528
1538
  if not body:
1529
1539
  continue
1530
1540
  body = _STRING_LITERAL_RE.sub('', _strip_java_comments(body))
1531
- for name, fqns in sib.items():
1541
+ # PERF: one pass over the body collects every bare / this-qualified callee name,
1542
+ # then intersect with the sibling index. The old code ran a fresh regex search
1543
+ # per sibling name — O(siblings × body_len) per method, i.e. quadratic in method
1544
+ # count, which made large method-dense classes (generated parsers, 4k-line test
1545
+ # classes) pathological (alfresco cold scan ~220s). This is O(body_len). The
1546
+ # edge set is identical: _INTRA_CALL_RE matches the same two call forms.
1547
+ called: set[str] = set()
1548
+ for m in _INTRA_CALL_RE.finditer(body):
1549
+ called.add(m.group(1) or m.group(2))
1550
+ for name in sorted(called): # sorted → deterministic edge order
1532
1551
  if name in _CALL_KEYWORDS:
1533
1552
  continue
1534
- # bare `name(` (not preceded by word char or '.') OR explicit `this.name(`
1535
- pat = (r'(?<![\w.])' + re.escape(name) + r'\s*\('
1536
- + r'|\bthis\s*\.\s*' + re.escape(name) + r'\s*\(')
1537
- if not re.search(pat, body):
1553
+ fqns = sib.get(name)
1554
+ if not fqns:
1538
1555
  continue
1539
1556
  for tgt in fqns:
1540
1557
  if tgt == caller.symbol:
@@ -1967,6 +1984,22 @@ def _extract_class_type_refs(symbols: list[SymbolRecord]) -> dict[str, list[dict
1967
1984
  return out
1968
1985
 
1969
1986
 
1987
+ _LEADING_ANNOT_RE = re.compile(r'^\s*@\w+(?:\s*\([^()]*\))?\s*')
1988
+
1989
+
1990
+ def _strip_leading_annotations(line: str) -> str:
1991
+ """Strip leading inline annotations from a declaration line so the residual
1992
+ declaration is still detected (`@Autowired private Foo f;` -> `private Foo f;`).
1993
+ A pure annotation line (`@Override`, `@GetMapping("/x")`) strips to empty.
1994
+ Nested-paren annotation args (rare on fields) are left intact — the residual
1995
+ just won't match a decl regex, which is safe (a miss, never a wrong entry)."""
1996
+ prev: Optional[str] = None
1997
+ while line != prev:
1998
+ prev = line
1999
+ line = _LEADING_ANNOT_RE.sub('', line, count=1)
2000
+ return line.strip()
2001
+
2002
+
1970
2003
  def _extract_field_types(source: str) -> dict[str, dict[str, str]]:
1971
2004
  """{class_fqn -> {field_name -> declared_type_simple_name}} for EVERY field
1972
2005
  (annotated or plain), the fact that binds an invocation receiver to a typed
@@ -1994,8 +2027,17 @@ def _extract_field_types(source: str) -> dict[str, dict[str, str]]:
1994
2027
  if "*/" not in stripped:
1995
2028
  in_block = True
1996
2029
  continue
1997
- if not stripped or stripped.startswith("//") or stripped.startswith("*") or stripped.startswith("@"):
2030
+ if not stripped or stripped.startswith("//") or stripped.startswith("*"):
1998
2031
  continue
2032
+ if stripped.startswith("@"):
2033
+ # A field/type decl may carry leading inline annotations
2034
+ # (`@Autowired private Foo f;`). Strip them so the residual declaration is
2035
+ # still detected; a pure annotation line strips to empty and is skipped as
2036
+ # before. Brace counting below runs on the stripped residual, so annotation
2037
+ # string args containing braces (`@GetMapping("/a/{id}")`) never skew depth.
2038
+ stripped = _strip_leading_annotations(stripped)
2039
+ if not stripped:
2040
+ continue
1999
2041
  if stripped.startswith("package "):
2000
2042
  package = stripped[8:].rstrip(";").strip()
2001
2043
  continue
@@ -4351,6 +4393,84 @@ def extract_file_ir(
4351
4393
  return _assemble(symbols, relations, changed_symbols, spring_summary, route_diffs)
4352
4394
 
4353
4395
 
4396
+ def _receiver_typed_call_edges(
4397
+ body_facts: dict[str, list[dict]],
4398
+ field_types: dict[str, dict[str, str]],
4399
+ symbols: list[SymbolRecord],
4400
+ ) -> list[RelationEdge]:
4401
+ """Method-level `calls` edges for instance invocations through a typed field
4402
+ receiver (`field.method(...)`).
4403
+
4404
+ The per-file scans in `_build_relations` resolve intra-class (`m()`/`this.m()`)
4405
+ and static (`Type.method()`, uppercase receiver) calls, but a call through a
4406
+ field-injected or plainly-typed dependency goes through a lowercase receiver
4407
+ variable (`nominasCalculoService.aplicarMejora(...)`) and matched none of them.
4408
+ A service reached only via an injected sibling therefore had NO inbound `calls`
4409
+ edge: impact-chain found 0 callers and reported a false-confident "no blast
4410
+ radius / low risk isolated change" — a dangerous false negative.
4411
+
4412
+ Joins facts already extracted (no new parse — Prime Directive):
4413
+ - invocation atom (owner method → {receiver, callee}) [body_facts]
4414
+ - declared type of `receiver` on the owner's class [field_types]
4415
+ - target type resolved to a SINGLE in-repo class FQN [symbols]
4416
+ - callee resolved to that class's method(s) of that name [symbols]
4417
+
4418
+ Emits `owner_method --calls--> target_class#callee` (every overload of the name —
4419
+ arity is not resolved by name alone, matching `_intra_class_call_edges`). Anything
4420
+ unresolved — a local variable (no field entry), a JDK/library type, an ambiguous
4421
+ simple name, or a callee the target class does not declare — yields nothing: an
4422
+ unresolved fact stays unresolved rather than being guessed (INV honesty). Runs as
4423
+ a repo-wide post-pass because the target type + its methods live in other files.
4424
+ """
4425
+ if not body_facts or not field_types:
4426
+ return []
4427
+
4428
+ # type simple name → in-repo class FQN(s); class FQN → {method name → [method FQN]}
4429
+ type_simple_to_fqn: dict[str, list[str]] = {}
4430
+ class_methods: dict[str, dict[str, list[str]]] = {}
4431
+ for s in symbols:
4432
+ if s.type in ("class", "interface", "enum", "record") and "#" not in s.symbol:
4433
+ type_simple_to_fqn.setdefault(s.symbol.rsplit(".", 1)[-1], []).append(s.symbol)
4434
+ elif s.symbol_kind == "method" and "#" in s.symbol:
4435
+ cls = _enclosing_class(s.symbol)
4436
+ name = s.symbol.rsplit("#", 1)[1]
4437
+ class_methods.setdefault(cls, {}).setdefault(name, []).append(s.symbol)
4438
+
4439
+ edges: list[RelationEdge] = []
4440
+ for owner_fqn in sorted(body_facts):
4441
+ owner_cls = _enclosing_class(owner_fqn)
4442
+ fmap = field_types.get(owner_cls)
4443
+ if not fmap:
4444
+ continue
4445
+ for atom in body_facts[owner_fqn]:
4446
+ if atom.get("fact") != "invocation":
4447
+ continue
4448
+ receiver, callee = atom.get("receiver"), atom.get("callee")
4449
+ if not receiver or receiver == "this" or not callee:
4450
+ continue
4451
+ tsimple = fmap.get(receiver)
4452
+ if not tsimple:
4453
+ continue # not a typed field (local / unknown)
4454
+ tfqns = type_simple_to_fqn.get(tsimple)
4455
+ if not tfqns or len(tfqns) != 1:
4456
+ continue # unresolved / ambiguous type → skip (honest)
4457
+ target_cls = tfqns[0]
4458
+ if target_cls == owner_cls:
4459
+ continue # intra-class handled by _intra_class_call_edges
4460
+ target_methods = class_methods.get(target_cls, {}).get(callee)
4461
+ if not target_methods:
4462
+ continue # callee not declared on the target type
4463
+ for mfqn in target_methods:
4464
+ edges.append(RelationEdge(
4465
+ from_symbol=owner_fqn,
4466
+ to_symbol=mfqn,
4467
+ type="calls",
4468
+ confidence="medium",
4469
+ evidence={"type": "method_call", "value": f"{receiver}.{callee}(...)"},
4470
+ ))
4471
+ return edges
4472
+
4473
+
4354
4474
  def build_repo_ir(
4355
4475
  file_paths: list[str],
4356
4476
  root: Path,
@@ -4495,6 +4615,17 @@ def build_repo_ir(
4495
4615
  _annotation_reference_edges(_ann_ref_sources, all_symbols, all_relations, _same_pkg_map)
4496
4616
  )
4497
4617
 
4618
+ # Instance calls through a typed field receiver (`field.method(...)`). The per-file
4619
+ # scans resolve intra-class and static (`Type.method()`) calls but never a call
4620
+ # through a lowercase field-injected dependency, so a service reached only via an
4621
+ # injected sibling had 0 inbound `calls` edges and impact-chain reported a
4622
+ # false-confident "no blast radius". Repo-wide post-pass (target type + methods are
4623
+ # cross-file). Gated on body facts + field types being present (emit_body_facts).
4624
+ if emit_body_facts:
4625
+ all_relations.extend(
4626
+ _receiver_typed_call_edges(_all_body_facts, _all_field_types, all_symbols)
4627
+ )
4628
+
4498
4629
  spring_summary = _build_spring_summary(all_symbols)
4499
4630
 
4500
4631
  # Deduplicate relations
@@ -443,6 +443,7 @@ def _bfs_callers(
443
443
  reverse_graph: dict[str, dict[str, list[str]]],
444
444
  max_depth: int,
445
445
  impl_graph: object | None = None,
446
+ method_scoped: bool = False,
446
447
  ) -> tuple[list[str], list[str], bool]:
447
448
  """BFS from seed FQNs through reverse_graph.
448
449
 
@@ -481,8 +482,22 @@ def _bfs_callers(
481
482
  # into direct_callers and inflating the blast radius ~12×. Exclude any caller
482
483
  # whose owning class is one of the seed classes.
483
484
  seed_classes: set[str] = {normalize_owner_fqn(s) for s in seed_fqns}
485
+ seed_set: set[str] = set(seed_fqns)
484
486
  self_excluded: int = 0
485
487
 
488
+ # P0-IMPACT-01 fix: distinguish self-recursion from same-class sibling calls.
489
+ # For a METHOD-scoped query ("Foo#doWork"), a *different* method on the same class
490
+ # (e.g. Foo#callWork → Foo#doWork) is a legitimate caller — the only true
491
+ # "self-reference" is the seed method invoking itself (recursion). The v1.70.0
492
+ # whole-class exclusion is correct ONLY for CLASS-scoped queries, where a class's
493
+ # own members are members, not external callers. Applying it to method queries
494
+ # silently discarded the intra-class caller edges built by _intra_class_call_edges,
495
+ # producing a false "0 callers / isolated" for private helpers.
496
+ def _is_self_reference(caller: str) -> bool:
497
+ if method_scoped:
498
+ return caller in seed_set # only genuine recursion of a seed method
499
+ return normalize_owner_fqn(caller) in seed_classes
500
+
486
501
  # BUG-004: index class FQN → list of method-level keys in reverse_graph.
487
502
  # Callers of Foo#doWork are stored under reverse_graph["Foo#doWork"], never
488
503
  # under reverse_graph["Foo"]. Without this index, BFS silently terminates
@@ -519,7 +534,7 @@ def _bfs_callers(
519
534
  for etype, fqn_list in _edges_for(seed):
520
535
  if etype not in _SKIP_EDGE_TYPES:
521
536
  unique_direct_callers.update(
522
- c for c in fqn_list if normalize_owner_fqn(c) not in seed_classes
537
+ c for c in fqn_list if not _is_self_reference(c)
523
538
  )
524
539
 
525
540
  effective_depth = 1 if len(unique_direct_callers) > _BFS_CALLER_CAP else max_depth
@@ -534,7 +549,8 @@ def _bfs_callers(
534
549
  if caller in visited:
535
550
  return
536
551
  # BUG #2: a member of a seed class is not an external caller — drop it.
537
- if normalize_owner_fqn(caller) in seed_classes:
552
+ # (Method-scoped queries drop only true self-recursion; see _is_self_reference.)
553
+ if _is_self_reference(caller):
538
554
  visited.add(caller)
539
555
  self_excluded += 1
540
556
  return
@@ -855,6 +871,22 @@ class ImpactOrchestrator:
855
871
  original_seed_classes: set[str] = {_class_of(s) for s in seed_fqns}
856
872
  # Subtype classes surfaced to the caller as the impacted implementation set.
857
873
  subtype_classes_added: set[str] = set()
874
+
875
+ # Method-scoped query (e.g. "Impl#foo"): impl/interface expansion must preserve
876
+ # the queried method's identity. A change to `foo` does not affect callers of a
877
+ # sibling method `bar`, so the expanded SEED symbols are restricted to the same
878
+ # method name(s). Class-level queries (a class node with no '#' in the seed)
879
+ # expand to all members, as before. This bound became observable once precise
880
+ # `field.method(...)` call edges exist: without it a method query inherited the
881
+ # callers of every sibling method on the interface (blast-radius inflation).
882
+ _queried_methods: set[str] = {s.rsplit("#", 1)[1] for s in seed_fqns if "#" in s}
883
+ _method_scoped = bool(_queried_methods) and all("#" in s for s in seed_fqns)
884
+
885
+ def _member_in_scope(sym: str) -> bool:
886
+ if not _method_scoped:
887
+ return True
888
+ return "#" in sym and sym.rsplit("#", 1)[1] in _queried_methods
889
+
858
890
  if impl_graph is not None:
859
891
  seed_classes_ch001 = {_class_of(s) for s in seed_fqns}
860
892
  impl_seeds: list[str] = []
@@ -867,7 +899,8 @@ class ImpactOrchestrator:
867
899
  continue
868
900
  subtype_classes_added.add(impl_class)
869
901
  for sym in cir.symbols:
870
- if _class_of(sym) == impl_class and sym not in set(seed_fqns):
902
+ if (_class_of(sym) == impl_class and sym not in set(seed_fqns)
903
+ and _member_in_scope(sym)):
871
904
  impl_seeds.append(sym)
872
905
  if impl_seeds:
873
906
  seed_fqns = list(dict.fromkeys(seed_fqns + impl_seeds))
@@ -896,7 +929,8 @@ class ImpactOrchestrator:
896
929
  continue
897
930
  iface_classes_added.add(iface_class)
898
931
  for sym in cir.symbols:
899
- if _class_of(sym) == iface_class and sym not in set(seed_fqns):
932
+ if (_class_of(sym) == iface_class and sym not in set(seed_fqns)
933
+ and _member_in_scope(sym)):
900
934
  iface_seeds.append(sym)
901
935
  if iface_seeds:
902
936
  seed_fqns = list(dict.fromkeys(seed_fqns + iface_seeds))
@@ -909,14 +943,22 @@ class ImpactOrchestrator:
909
943
 
910
944
  # ── 2. BFS through reverse graph ─────────────────────────────────
911
945
  direct_callers, indirect_callers, truncated, self_excluded = _bfs_callers(
912
- seed_fqns, cir.reverse_graph, depth, impl_graph=impl_graph
946
+ seed_fqns, cir.reverse_graph, depth, impl_graph=impl_graph,
947
+ method_scoped=_method_scoped,
913
948
  )
914
949
  if self_excluded:
915
- warnings.append(
916
- f"Self-referential exclusion (BUG #2): {self_excluded} member(s) of the "
917
- f"analyzed class were dropped from callers — a class's own methods are "
918
- f"members, not external callers (they do not count toward blast radius)."
919
- )
950
+ if _method_scoped:
951
+ warnings.append(
952
+ f"Self-recursion excluded: {self_excluded} recursive self-call(s) of the "
953
+ f"queried method were dropped from callers (a method is not its own "
954
+ f"caller). Same-class sibling methods ARE counted as callers."
955
+ )
956
+ else:
957
+ warnings.append(
958
+ f"Self-referential exclusion (BUG #2): {self_excluded} member(s) of the "
959
+ f"analyzed class were dropped from callers — a class's own methods are "
960
+ f"members, not external callers (they do not count toward blast radius)."
961
+ )
920
962
  if truncated:
921
963
  warnings.append(
922
964
  "Hub-class guard active: symbol has > 500 direct callers — "
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 2.5.0
3
+ Version: 2.5.2
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  License-File: LICENSE
6
6
  Keywords: agents,ai,codebase,context,developer-tools,llm
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
42
42
 
43
43
  **Context · Impact · Migration · Architecture · Review — everything from one structural model.**
44
44
 
45
- ![Version](https://img.shields.io/badge/version-2.5.0-blue)
45
+ ![Version](https://img.shields.io/badge/version-2.5.1-blue)
46
46
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
47
47
 
48
48
  > **ASK Engine** is the product. The CLI command is **`ask`**. The legacy **`sourcecode`**
@@ -84,7 +84,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
84
84
  # pip / pipx
85
85
  pipx install sourcecode # or: pip install sourcecode
86
86
 
87
- ask version # ask 2.5.0
87
+ ask version # ask 2.5.1
88
88
  ```
89
89
 
90
90
  > **Package vs. command.** The install package is named `sourcecode` this release
@@ -1,4 +1,4 @@
1
- sourcecode/__init__.py,sha256=wPl09nh67L2BWCogYMSS9J9rk7fRv2rZc1D97h9pLNU,308
1
+ sourcecode/__init__.py,sha256=nbtWf3A6IYQQ1rNky7L2moDkYZ2Uy7Nj9Mb5R7f2-KQ,308
2
2
  sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
3
3
  sourcecode/archetype.py,sha256=d7yoN6Tj4OBj-VgSMPEKg4WF9H8FypnZ5A6AkUh5oIE,34484
4
4
  sourcecode/architecture_analyzer.py,sha256=6_wC4TYuBYxaqckZS0I1vBSMRPeH2vzlDB48gXwOOd4,46627
@@ -55,7 +55,7 @@ sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
55
55
  sourcecode/relevance_scorer.py,sha256=0AgEt4KrV73nioMqBgjhGjtY7L2C7L7cSyKtj3IKcrw,9408
56
56
  sourcecode/rename_refactor.py,sha256=h6dNFlB9aZ_3q6heeHBkgXQeXaT03nvPSsYH6P8qxFg,12965
57
57
  sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
58
- sourcecode/repository_ir.py,sha256=YhsefuP7tyn5VyoWwBmqu4nIZw4MJ7y0wzt9K1xviqY,284202
58
+ sourcecode/repository_ir.py,sha256=bG81ARrPBaPZoGqGT9bXviGCHZ27D7zGli8KShn9-cI,291158
59
59
  sourcecode/ris.py,sha256=aG2D4159gtpg968yD3PylYbIXhGFOwIlFBI0DSr84yY,20412
60
60
  sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
61
61
  sourcecode/scanner.py,sha256=WdOQ78mMzjR1NjmKTlbxdgwinnCTfAhxCVLBEFQiFHU,8899
@@ -69,7 +69,7 @@ sourcecode/semantic_services.py,sha256=AO_p7PRfjc-AUh6ThvLMfm4iUGi0issL_frCf8sxG
69
69
  sourcecode/serializer.py,sha256=FmQo7eojA_z3ptLW-Vgze5mrEJ6FabcZqiwbQWWTgFI,130056
70
70
  sourcecode/spring_event_topology.py,sha256=5_ON_21Le5zbG-1GRc5GLIi5HJfy_QjcXLVPC5WeUGQ,18055
71
71
  sourcecode/spring_findings.py,sha256=EX7kLZLN74CFyR9iZPm3CI115BfFKNd4WRPuErNljZM,5729
72
- sourcecode/spring_impact.py,sha256=1Eu1vkdwTVsw92iBEJDe_i_FaSf4uGH9Rb0eSgIAAZc,57542
72
+ sourcecode/spring_impact.py,sha256=tjTxKAtmAvBqDVSSsC4ttCzbLaO55Wg4FJdOd_8MtO0,60077
73
73
  sourcecode/spring_model.py,sha256=zOAgFmrRbG4a6KLm1TJl55aWMyPNsz3OS3FSczqPG6A,16594
74
74
  sourcecode/spring_security_audit.py,sha256=Rk-aSohezdc7YDYbSoJquVnwpkDB8ty1BCD-4Hc4R5A,22832
75
75
  sourcecode/spring_semantic.py,sha256=jteQ1PkY9ArFJv0embg_jBIdbOxqrk9mQ2Xz8OF_FKA,14214
@@ -136,8 +136,8 @@ sourcecode/telemetry/consent.py,sha256=LIAO9ohJZF8OuZwM4u1VWtALlYfTCCKq4wV3Vwc7i
136
136
  sourcecode/telemetry/events.py,sha256=LtzYfaX9Ilckj5PTvAcTpDa9mLqDsYPDUiDkRa58piY,2580
137
137
  sourcecode/telemetry/filters.py,sha256=NHa5T-6DaZduQPFuC34jOqHWQgSizM-Ygq8aZ4j19ng,5834
138
138
  sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
139
- sourcecode-2.5.0.dist-info/METADATA,sha256=OGJ148O3ZpmqUj2Ogxv125FILq6ReUC6PmuYyP9t5fs,10851
140
- sourcecode-2.5.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
141
- sourcecode-2.5.0.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
142
- sourcecode-2.5.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
143
- sourcecode-2.5.0.dist-info/RECORD,,
139
+ sourcecode-2.5.2.dist-info/METADATA,sha256=cOmLhyx7sqYDB9m4eU6MkDuV4hfXzK68a2cUC_-N490,10851
140
+ sourcecode-2.5.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
141
+ sourcecode-2.5.2.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
142
+ sourcecode-2.5.2.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
143
+ sourcecode-2.5.2.dist-info/RECORD,,