sourcecode 1.31.2__py3-none-any.whl → 1.31.4__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 +1 -1
- sourcecode/cli.py +96 -34
- sourcecode/prepare_context.py +67 -4
- {sourcecode-1.31.2.dist-info → sourcecode-1.31.4.dist-info}/METADATA +3 -3
- {sourcecode-1.31.2.dist-info → sourcecode-1.31.4.dist-info}/RECORD +8 -8
- {sourcecode-1.31.2.dist-info → sourcecode-1.31.4.dist-info}/WHEEL +0 -0
- {sourcecode-1.31.2.dist-info → sourcecode-1.31.4.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.31.2.dist-info → sourcecode-1.31.4.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/cli.py
CHANGED
|
@@ -1849,6 +1849,11 @@ def prepare_context_cmd(
|
|
|
1849
1849
|
"--include-config",
|
|
1850
1850
|
help="(generate-tests) Include tooling config files (*.conf.js, .eslintrc*, etc.) in test_gaps. Excluded by default.",
|
|
1851
1851
|
),
|
|
1852
|
+
all_gaps: bool = typer.Option(
|
|
1853
|
+
False,
|
|
1854
|
+
"--all",
|
|
1855
|
+
help="(generate-tests) Return the full test_gaps list without truncating to top 20.",
|
|
1856
|
+
),
|
|
1852
1857
|
) -> None:
|
|
1853
1858
|
"""Task-specific context for AI coding agents.
|
|
1854
1859
|
|
|
@@ -1930,7 +1935,7 @@ def prepare_context_cmd(
|
|
|
1930
1935
|
_sys.stderr.flush()
|
|
1931
1936
|
_t0 = _time.perf_counter()
|
|
1932
1937
|
try:
|
|
1933
|
-
output = builder.build(task, since=since, symptom=symptom, fast=fast, include_config=include_config)
|
|
1938
|
+
output = builder.build(task, since=since, symptom=symptom, fast=fast, include_config=include_config, all_gaps=all_gaps)
|
|
1934
1939
|
finally:
|
|
1935
1940
|
_progress.finish()
|
|
1936
1941
|
_t_total = (_time.perf_counter() - _t0) * 1000
|
|
@@ -1957,8 +1962,8 @@ def prepare_context_cmd(
|
|
|
1957
1962
|
},
|
|
1958
1963
|
"explain": {
|
|
1959
1964
|
"project_summary": True, "architecture_summary": True,
|
|
1960
|
-
"relevant_files":
|
|
1961
|
-
"gaps":
|
|
1965
|
+
"relevant_files": False, "key_dependencies": True,
|
|
1966
|
+
"gaps": False, "confidence": True,
|
|
1962
1967
|
"suspected_areas": False, "improvement_opportunities": False,
|
|
1963
1968
|
"test_gaps": False, "code_notes_summary": False,
|
|
1964
1969
|
"changed_files": False, "affected_entry_points": False,
|
|
@@ -2467,6 +2472,11 @@ def _extract_java_endpoints(root: "Path") -> "dict[str, Any]":
|
|
|
2467
2472
|
_CLASS_PATH_RE = _re.compile(
|
|
2468
2473
|
r'@RequestMapping\s*\(\s*(?:value\s*=\s*)?["\']([^"\']+)["\']',
|
|
2469
2474
|
)
|
|
2475
|
+
# Handles array syntax: @RequestMapping({"/v1/foo", "/v1/bar"})
|
|
2476
|
+
_CLASS_ARRAY_PATH_RE = _re.compile(r'@RequestMapping\s*\(\s*\{([^}]*)\}')
|
|
2477
|
+
_QUOTED_STR_RE = _re.compile(r'"([^"]+)"')
|
|
2478
|
+
_REQUEST_METHOD_VERB_RE = _re.compile(r'method\s*=\s*RequestMethod\.([A-Z]+)')
|
|
2479
|
+
_VALUE_PATH_RE = _re.compile(r'value\s*=\s*"([^"]+)"')
|
|
2470
2480
|
|
|
2471
2481
|
_HTTP_METHOD_MAP = {
|
|
2472
2482
|
"Get": "GET", "Post": "POST", "Put": "PUT",
|
|
@@ -2502,27 +2512,66 @@ def _extract_java_endpoints(root: "Path") -> "dict[str, Any]":
|
|
|
2502
2512
|
cls_m = _CLASS_RE.search(content)
|
|
2503
2513
|
class_name = cls_m.group(1) if cls_m else java_file.stem
|
|
2504
2514
|
|
|
2505
|
-
# Extract class-level base path
|
|
2506
|
-
class_base = ""
|
|
2515
|
+
# Extract class-level base path and locate class body start
|
|
2507
2516
|
lines = content.splitlines()
|
|
2517
|
+
|
|
2518
|
+
# First pass: find class/interface declaration line index
|
|
2519
|
+
class_body_start = 0
|
|
2508
2520
|
for i, line in enumerate(lines):
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2521
|
+
stripped_l = line.strip()
|
|
2522
|
+
if (not stripped_l.startswith("//") and not stripped_l.startswith("*")
|
|
2523
|
+
and ("class " in stripped_l or "interface " in stripped_l)
|
|
2524
|
+
and _CLASS_RE.search(stripped_l)):
|
|
2525
|
+
class_body_start = i + 1
|
|
2526
|
+
break
|
|
2527
|
+
|
|
2528
|
+
# Second pass: extract class-level @RequestMapping path (only before class body).
|
|
2529
|
+
# Supports both single-string and array syntax (Bug #1B).
|
|
2530
|
+
search_end = class_body_start if class_body_start else len(lines)
|
|
2531
|
+
class_bases: list[str] = [""] # default: no prefix
|
|
2532
|
+
for i in range(search_end):
|
|
2533
|
+
sl = lines[i].strip()
|
|
2534
|
+
if sl.startswith("//") or sl.startswith("*"):
|
|
2535
|
+
continue
|
|
2536
|
+
if "@RequestMapping" in lines[i]:
|
|
2537
|
+
# Cap block to search_end so method annotations inside the class body
|
|
2538
|
+
# cannot be matched as the class-level prefix (Bug #1B).
|
|
2539
|
+
block = "\n".join(lines[max(0, i - 1): min(i + 5, search_end + 1)])
|
|
2512
2540
|
if "class " in block or "interface " in block:
|
|
2513
2541
|
path_m = _CLASS_PATH_RE.search(block)
|
|
2514
2542
|
if path_m:
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2543
|
+
class_bases = [path_m.group(1).rstrip("/")]
|
|
2544
|
+
else:
|
|
2545
|
+
arr_m = _CLASS_ARRAY_PATH_RE.search(block)
|
|
2546
|
+
if arr_m:
|
|
2547
|
+
paths = _QUOTED_STR_RE.findall(arr_m.group(1))
|
|
2548
|
+
if paths:
|
|
2549
|
+
class_bases = [p.rstrip("/") for p in paths]
|
|
2550
|
+
break
|
|
2551
|
+
|
|
2552
|
+
# Extract method-level endpoints starting from inside class body.
|
|
2553
|
+
# Bug #1A fix: track block comments and skip // lines before annotation checks.
|
|
2520
2554
|
pending_annotations: list[tuple[str, str]] = [] # (http_verb, path_suffix)
|
|
2521
2555
|
pending_filtro: Optional[str] = None
|
|
2556
|
+
in_block_comment = False
|
|
2522
2557
|
|
|
2523
|
-
for i
|
|
2558
|
+
for i in range(class_body_start, len(lines)):
|
|
2559
|
+
line = lines[i]
|
|
2524
2560
|
stripped = line.strip()
|
|
2525
2561
|
|
|
2562
|
+
# Block comment state tracking (Bug #1A)
|
|
2563
|
+
if in_block_comment:
|
|
2564
|
+
if "*/" in stripped:
|
|
2565
|
+
in_block_comment = False
|
|
2566
|
+
continue
|
|
2567
|
+
if stripped.startswith("/*"):
|
|
2568
|
+
if "*/" not in stripped:
|
|
2569
|
+
in_block_comment = True
|
|
2570
|
+
continue
|
|
2571
|
+
# Skip line comments (Bug #1A)
|
|
2572
|
+
if stripped.startswith("//"):
|
|
2573
|
+
continue
|
|
2574
|
+
|
|
2526
2575
|
# Check for @M3FiltroSeguridad
|
|
2527
2576
|
fm = _FILTRO_RE.search(stripped)
|
|
2528
2577
|
if fm:
|
|
@@ -2535,6 +2584,19 @@ def _extract_java_endpoints(root: "Path") -> "dict[str, Any]":
|
|
|
2535
2584
|
verb_key = hm.group(1)
|
|
2536
2585
|
http_verb = _HTTP_METHOD_MAP.get(verb_key, "GET")
|
|
2537
2586
|
path_suffix = (hm.group(2) or "").strip()
|
|
2587
|
+
|
|
2588
|
+
# For @RequestMapping: resolve HTTP method from method= attribute
|
|
2589
|
+
if verb_key == "Request":
|
|
2590
|
+
annotation_block = "\n".join(lines[i:min(i + 5, len(lines))])
|
|
2591
|
+
vm = _REQUEST_METHOD_VERB_RE.search(annotation_block)
|
|
2592
|
+
if vm:
|
|
2593
|
+
http_verb = vm.group(1)
|
|
2594
|
+
# If path not captured (method= precedes value=), try value= extraction
|
|
2595
|
+
if not path_suffix:
|
|
2596
|
+
vp = _VALUE_PATH_RE.search(annotation_block)
|
|
2597
|
+
if vp:
|
|
2598
|
+
path_suffix = vp.group(1)
|
|
2599
|
+
|
|
2538
2600
|
pending_annotations.append((http_verb, path_suffix))
|
|
2539
2601
|
continue
|
|
2540
2602
|
|
|
@@ -2544,30 +2606,30 @@ def _extract_java_endpoints(root: "Path") -> "dict[str, Any]":
|
|
|
2544
2606
|
handler = mm.group(1) if mm else ""
|
|
2545
2607
|
if handler and not handler.startswith("class"):
|
|
2546
2608
|
for http_verb, path_suffix in pending_annotations:
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
seen
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2609
|
+
for _cb in class_bases: # Bug #1B: one endpoint per class prefix
|
|
2610
|
+
full_path = (_cb + "/" + path_suffix).replace("//", "/").rstrip("/") or "/"
|
|
2611
|
+
if not full_path.startswith("/"):
|
|
2612
|
+
full_path = "/" + full_path
|
|
2613
|
+
key = (class_name, handler, http_verb, _cb)
|
|
2614
|
+
if key not in seen:
|
|
2615
|
+
seen.add(key)
|
|
2616
|
+
entry: dict[str, Any] = {
|
|
2617
|
+
"method": http_verb,
|
|
2618
|
+
"path": full_path,
|
|
2619
|
+
"controller": class_name,
|
|
2620
|
+
"handler": handler,
|
|
2621
|
+
}
|
|
2622
|
+
if pending_filtro:
|
|
2623
|
+
entry["required_permission"] = pending_filtro
|
|
2624
|
+
endpoints.append(entry)
|
|
2562
2625
|
pending_annotations = []
|
|
2563
2626
|
pending_filtro = None
|
|
2564
2627
|
continue
|
|
2565
2628
|
|
|
2566
|
-
# Non-annotation, non-method line — reset
|
|
2567
|
-
if stripped
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
pending_filtro = None
|
|
2629
|
+
# Non-annotation, non-method line — reset on closing brace
|
|
2630
|
+
if stripped == "}":
|
|
2631
|
+
pending_annotations = []
|
|
2632
|
+
pending_filtro = None
|
|
2571
2633
|
|
|
2572
2634
|
endpoints.sort(key=lambda e: (e.get("controller", ""), e.get("path", "")))
|
|
2573
2635
|
undocumented = sum(1 for e in endpoints if "required_permission" not in e)
|
sourcecode/prepare_context.py
CHANGED
|
@@ -721,7 +721,7 @@ class TaskContextBuilder:
|
|
|
721
721
|
def __init__(self, root: Path) -> None:
|
|
722
722
|
self.root = root
|
|
723
723
|
|
|
724
|
-
def build(self, task_name: str, *, since: Optional[str] = None, symptom: Optional[str] = None, fast: bool = False, include_config: bool = False) -> TaskOutput:
|
|
724
|
+
def build(self, task_name: str, *, since: Optional[str] = None, symptom: Optional[str] = None, fast: bool = False, include_config: bool = False, all_gaps: bool = False) -> TaskOutput:
|
|
725
725
|
if task_name not in TASKS:
|
|
726
726
|
raise ValueError(
|
|
727
727
|
f"Unknown task '{task_name}'. Available: {', '.join(TASKS)}"
|
|
@@ -1763,14 +1763,18 @@ class TaskContextBuilder:
|
|
|
1763
1763
|
"_rank": _pub_count + _ann_count * 2,
|
|
1764
1764
|
})
|
|
1765
1765
|
|
|
1766
|
-
_java_candidates.sort(
|
|
1766
|
+
_java_candidates.sort(
|
|
1767
|
+
key=lambda x: -(x["public_method_count"] * (1.5 if x["has_spring_annotations"] else 1.0))
|
|
1768
|
+
)
|
|
1769
|
+
_top = _java_candidates if all_gaps else _java_candidates[:20]
|
|
1767
1770
|
test_gaps = [
|
|
1768
1771
|
{
|
|
1769
1772
|
"path": c["path"],
|
|
1770
1773
|
"public_method_count": c["public_method_count"],
|
|
1771
1774
|
"has_spring_annotations": c["has_spring_annotations"],
|
|
1775
|
+
"rank_score": round(c["public_method_count"] * (1.5 if c["has_spring_annotations"] else 1.0), 1),
|
|
1772
1776
|
}
|
|
1773
|
-
for c in
|
|
1777
|
+
for c in _top
|
|
1774
1778
|
]
|
|
1775
1779
|
else:
|
|
1776
1780
|
# Non-Java algorithm (unchanged)
|
|
@@ -2206,8 +2210,67 @@ class TaskContextBuilder:
|
|
|
2206
2210
|
"refactor": max(15, min(30, _repo_size // 120)),
|
|
2207
2211
|
}
|
|
2208
2212
|
_budget = _task_budget.get(task_name, 15)
|
|
2209
|
-
_selected = _ctx.select_subgraph(_ns, contracts=[], budget=_budget, min_score=0.15)
|
|
2213
|
+
_selected = list(_ctx.select_subgraph(_ns, contracts=[], budget=_budget, min_score=0.15))
|
|
2210
2214
|
_rf_map = {path: rf for _, path, rf in scored}
|
|
2215
|
+
|
|
2216
|
+
# Bug #3: onboard must cover ≥3 arch layers (controllers/services/domain/repositories).
|
|
2217
|
+
if task_name == "onboard":
|
|
2218
|
+
def _arch_layer(p: str) -> str:
|
|
2219
|
+
n = Path(p).name.lower()
|
|
2220
|
+
if "controller" in n:
|
|
2221
|
+
return "controllers"
|
|
2222
|
+
if "repository" in n or "mapper" in n or "dao" in n:
|
|
2223
|
+
return "repositories"
|
|
2224
|
+
if "service" in n:
|
|
2225
|
+
return "services"
|
|
2226
|
+
pn = p.replace("\\", "/")
|
|
2227
|
+
if "entity" in n or "/entity/" in pn or "/domain/" in pn or "/model/" in pn:
|
|
2228
|
+
return "domain"
|
|
2229
|
+
return "other"
|
|
2230
|
+
|
|
2231
|
+
_REQUIRED = {"controllers", "services", "repositories", "domain"}
|
|
2232
|
+
_covered = {_arch_layer(p) for p in _selected} & _REQUIRED
|
|
2233
|
+
_missing = _REQUIRED - _covered
|
|
2234
|
+
if len(_covered) < 3 and _missing:
|
|
2235
|
+
_sel_set = set(_selected)
|
|
2236
|
+
# First pass: inject from already-scored files
|
|
2237
|
+
for _, _p, _ in sorted(scored, key=lambda x: -x[0]):
|
|
2238
|
+
if len(_covered) >= 3:
|
|
2239
|
+
break
|
|
2240
|
+
if _p in _sel_set or _p not in _rf_map:
|
|
2241
|
+
continue
|
|
2242
|
+
_layer = _arch_layer(_p)
|
|
2243
|
+
if _layer in _missing:
|
|
2244
|
+
_selected.append(_p)
|
|
2245
|
+
_sel_set.add(_p)
|
|
2246
|
+
_covered.add(_layer)
|
|
2247
|
+
_missing.discard(_layer)
|
|
2248
|
+
# Second pass: fallback scan of all_paths when scored files
|
|
2249
|
+
# don't cover enough layers (e.g. all Java files scored ≤ 0
|
|
2250
|
+
# due to auxiliary/example package detection).
|
|
2251
|
+
if len(_covered) < 3 and _missing:
|
|
2252
|
+
_NON_TEST = ("/test/", "/tests/", "/spec/")
|
|
2253
|
+
for _p in all_paths:
|
|
2254
|
+
if len(_covered) >= 3:
|
|
2255
|
+
break
|
|
2256
|
+
if _p in _sel_set:
|
|
2257
|
+
continue
|
|
2258
|
+
if any(s in _p.replace("\\", "/") for s in _NON_TEST):
|
|
2259
|
+
continue
|
|
2260
|
+
_layer = _arch_layer(_p)
|
|
2261
|
+
if _layer not in _missing:
|
|
2262
|
+
continue
|
|
2263
|
+
_rf_map[_p] = RelevantFile(
|
|
2264
|
+
path=_p,
|
|
2265
|
+
role="source",
|
|
2266
|
+
score=0.1,
|
|
2267
|
+
reason="layer coverage (onboard)",
|
|
2268
|
+
)
|
|
2269
|
+
_selected.append(_p)
|
|
2270
|
+
_sel_set.add(_p)
|
|
2271
|
+
_covered.add(_layer)
|
|
2272
|
+
_missing.discard(_layer)
|
|
2273
|
+
|
|
2211
2274
|
return [_rf_map[p] for p in _selected if p in _rf_map]
|
|
2212
2275
|
except Exception:
|
|
2213
2276
|
return [f for _, _, f in scored[:15]]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.31.
|
|
3
|
+
Version: 1.31.4
|
|
4
4
|
Summary: Deterministic codebase context for AI coding agents
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -225,7 +225,7 @@ Description-Content-Type: text/markdown
|
|
|
225
225
|
|
|
226
226
|
**Deterministic, behavior-aware codebase context for AI agents and PR review.**
|
|
227
227
|
|
|
228
|
-

|
|
229
229
|

|
|
230
230
|
|
|
231
231
|
---
|
|
@@ -261,7 +261,7 @@ pipx install sourcecode
|
|
|
261
261
|
|
|
262
262
|
```bash
|
|
263
263
|
sourcecode version
|
|
264
|
-
# sourcecode 1.31.
|
|
264
|
+
# sourcecode 1.31.4
|
|
265
265
|
```
|
|
266
266
|
|
|
267
267
|
---
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=P8MbrmTVGyjN1Uy_38VxYTdrjIoEPOf9I4Xn71cCRs8,103
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
|
|
3
3
|
sourcecode/architecture_analyzer.py,sha256=MyBa0Hf5HmkudZQDLKrjcWDKETXETXl0mQX1swtTwAA,39091
|
|
4
4
|
sourcecode/architecture_summary.py,sha256=z34_6v7cSwy98cof2UVciGho7SCrZ93tiqMmq5WNzRQ,20405
|
|
5
5
|
sourcecode/ast_extractor.py,sha256=XgrZg2DcWcUm9r87cRG3KGO7IK2TIL_N-CvhSbUmmh4,49901
|
|
6
6
|
sourcecode/classifier.py,sha256=-0t0HLc9L9UleMLfclfLM3AXhBjUb_AYyBPDbvgWtac,7755
|
|
7
|
-
sourcecode/cli.py,sha256=
|
|
7
|
+
sourcecode/cli.py,sha256=dcHIYDvDpcIzbExxKuemtElk3RClLRWaaO2RU9R8Hoc,123945
|
|
8
8
|
sourcecode/code_notes_analyzer.py,sha256=y1MJBnPZHYp4i6cQCXUb9ATIyifS_qMQWjw_8lPkpsU,9215
|
|
9
9
|
sourcecode/confidence_analyzer.py,sha256=ZUn-Nywi5TEQcuozqK_vfOyPT-a1dYYO42elAtVFV-k,16412
|
|
10
10
|
sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
|
|
@@ -22,7 +22,7 @@ sourcecode/git_analyzer.py,sha256=0Gyj-vMpIIN4nfriKXVRouNYBeJ59s6pQDX2Xu9Pq-U,13
|
|
|
22
22
|
sourcecode/graph_analyzer.py,sha256=iUK-7pSV-cvGqqD2hENdYmhnm0wcXFEyK-xnu5ul8OU,62515
|
|
23
23
|
sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
|
|
24
24
|
sourcecode/pr_comment_renderer.py,sha256=smHslxiG14lrytCkq5nFrFu-qTHgA-t-LFYfdrfjz2o,14423
|
|
25
|
-
sourcecode/prepare_context.py,sha256=
|
|
25
|
+
sourcecode/prepare_context.py,sha256=Eid3Wmh4lmtr20pmobcCU0yzjRXhFql2rEmfGUgkFpU,183214
|
|
26
26
|
sourcecode/progress.py,sha256=qn30sWaHOkjTgXsSBmiPkz7Rsbwc5oSlIe6JNEMYp_k,3149
|
|
27
27
|
sourcecode/ranking_engine.py,sha256=ZAucq_YX2KkWUuAZf4P0lhtQ_38vEFnUhuGtSZd1S0E,12970
|
|
28
28
|
sourcecode/redactor.py,sha256=xuGcadGEHaPw4qZXlMDvzMCsr4VOkdp3oBQptHyJk8c,2884
|
|
@@ -72,8 +72,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
|
|
|
72
72
|
sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
|
|
73
73
|
sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
|
|
74
74
|
sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
|
|
75
|
-
sourcecode-1.31.
|
|
76
|
-
sourcecode-1.31.
|
|
77
|
-
sourcecode-1.31.
|
|
78
|
-
sourcecode-1.31.
|
|
79
|
-
sourcecode-1.31.
|
|
75
|
+
sourcecode-1.31.4.dist-info/METADATA,sha256=JZXcuZYPozOjtbIY_xnVgUNwMF1aEU12mV4m2T__E1A,29083
|
|
76
|
+
sourcecode-1.31.4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
77
|
+
sourcecode-1.31.4.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
78
|
+
sourcecode-1.31.4.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
79
|
+
sourcecode-1.31.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|