sourcecode 3.1.0__py3-none-any.whl → 3.1.1__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.

Potentially problematic release.


This version of sourcecode might be problematic. Click here for more details.

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__ = "3.1.0"
7
+ __version__ = "3.1.1"
sourcecode/cli.py CHANGED
@@ -6201,6 +6201,65 @@ def posture_cmd(
6201
6201
  # ── Spring Boot Migration Check ───────────────────────────────────────────────
6202
6202
 
6203
6203
 
6204
+ def _migration_blast_radius(
6205
+ target: Path,
6206
+ file_list: list[str],
6207
+ report: "object",
6208
+ depth: int,
6209
+ ) -> dict:
6210
+ """Resolve the regression scope behind the migration findings (--blast-radius).
6211
+
6212
+ Reuses the shared context-cache CIR — the same one `impact-chain`, `explain` and
6213
+ `cache warm` use — so a warmed repository skips the Java parse. Every failure mode
6214
+ degrades to a block that says what could not be computed; it never fabricates reach
6215
+ and never breaks the scan that already succeeded.
6216
+ """
6217
+ from sourcecode.migration_blast import compute_migration_blast, SCHEMA_VERSION
6218
+
6219
+ _depth = max(1, min(int(depth), 8))
6220
+ findings = list(getattr(report, "findings", []) or [])
6221
+
6222
+ def _degraded(reason: str) -> dict:
6223
+ return {
6224
+ "schema_version": SCHEMA_VERSION,
6225
+ "depth": _depth,
6226
+ "status": "unavailable",
6227
+ "endpoints_in_repo": 0,
6228
+ "endpoints_reached": 0,
6229
+ "request_surface_share": None,
6230
+ "files_analyzed": 0,
6231
+ "files_on_request_path": 0,
6232
+ "files_off_request_path": 0,
6233
+ "files_unresolved": [],
6234
+ "files": [],
6235
+ "files_omitted": 0,
6236
+ "notes": [reason],
6237
+ }
6238
+
6239
+ if not file_list:
6240
+ return _degraded(
6241
+ "No Java sources in this repository — there is no call graph to traverse, "
6242
+ "so no regression scope can be attributed."
6243
+ )
6244
+
6245
+ try:
6246
+ from sourcecode import context_cache as _ctxcache
6247
+ from sourcecode.context_graph import ContextGraph
6248
+ from sourcecode.spring_model import SpringSemanticModel
6249
+
6250
+ try:
6251
+ cir, _ = _ctxcache.get_or_build_cir(_resolve_repo_root(target), target, file_list)
6252
+ except Exception:
6253
+ cir = ContextGraph.build(file_list, target).cir
6254
+ model = SpringSemanticModel.build(cir)
6255
+ return compute_migration_blast(cir, model, findings, depth=_depth)
6256
+ except Exception as exc: # pragma: no cover - defensive
6257
+ return _degraded(
6258
+ f"Blast radius unavailable: {type(exc).__name__} while building the "
6259
+ "repository IR. The migration findings above are unaffected."
6260
+ )
6261
+
6262
+
6204
6263
  @app.command("migrate-check")
6205
6264
  def migrate_check_cmd(
6206
6265
  path: Path = typer.Argument(
@@ -6265,6 +6324,18 @@ def migrate_check_cmd(
6265
6324
  False, "--force",
6266
6325
  help="Emit to stdout even when the report exceeds the output-size guard.",
6267
6326
  ),
6327
+ blast_radius: bool = typer.Option(
6328
+ False, "--blast-radius",
6329
+ help=(
6330
+ "Rank affected product files by the HTTP endpoints whose call path runs "
6331
+ "through them (regression scope), using the same caller traversal as "
6332
+ "impact-chain. Builds the repository IR — slower than a plain scan."
6333
+ ),
6334
+ ),
6335
+ depth: int = typer.Option(
6336
+ 4, "--depth",
6337
+ help="Caller BFS depth for --blast-radius (1-8, default: 4).",
6338
+ ),
6268
6339
  ) -> None:
6269
6340
  """Spring Boot 2→3 migration readiness: detect javax→jakarta namespace blockers.
6270
6341
 
@@ -6366,6 +6437,9 @@ def migrate_check_cmd(
6366
6437
  if _file_limitations:
6367
6438
  report.limitations.extend(_file_limitations)
6368
6439
 
6440
+ if blast_radius:
6441
+ report.blast_radius = _migration_blast_radius(target, file_list, report, depth)
6442
+
6369
6443
  if format == "text":
6370
6444
  output = report.to_text(min_severity=min_severity)
6371
6445
  else:
@@ -1402,6 +1402,11 @@ class MigrationReport:
1402
1402
  # exposure map, call-chain detection, upgrade-vs-rewrite verdict). Attached
1403
1403
  # by run_migrate_check; None when not computed.
1404
1404
  hibernate: Optional["HibernateStratification"] = None
1405
+ # Regression scope behind the findings: the endpoints whose call path runs
1406
+ # through each affected file (migration_blast.compute_migration_blast).
1407
+ # Opt-in — attached by the CLI under --blast-radius; None when not computed,
1408
+ # and then absent from the payload so the default report stays byte-identical.
1409
+ blast_radius: Optional[dict] = None
1405
1410
 
1406
1411
  def finalize(self) -> "MigrationReport":
1407
1412
  if not self.generated_at:
@@ -1682,7 +1687,7 @@ class MigrationReport:
1682
1687
  return self
1683
1688
 
1684
1689
  def to_dict(self) -> dict:
1685
- return {
1690
+ d: dict = {
1686
1691
  "schema_version": self.schema_version,
1687
1692
  "generated_at": self.generated_at,
1688
1693
  "repo_id": self.repo_id,
@@ -1718,6 +1723,11 @@ class MigrationReport:
1718
1723
  "limitations": self.limitations,
1719
1724
  "metadata": self.metadata,
1720
1725
  }
1726
+ # Additive and opt-in: the key appears only when --blast-radius computed it,
1727
+ # so a default run emits exactly the payload it emitted before.
1728
+ if self.blast_radius is not None:
1729
+ d["blast_radius"] = self.blast_radius
1730
+ return d
1721
1731
 
1722
1732
  def to_compact_dict(self, top_n: int = _COMPACT_TOP_N) -> dict:
1723
1733
  """Bounded, decision-grade projection of `to_dict()`.
@@ -1754,6 +1764,11 @@ class MigrationReport:
1754
1764
  if isinstance(h.get(_col), list):
1755
1765
  h[_col] = _cap_collection(h[_col], top_n)
1756
1766
  compact["hibernate"] = h
1767
+ blast = full.get("blast_radius")
1768
+ if isinstance(blast, dict) and isinstance(blast.get("files"), list):
1769
+ b = dict(blast)
1770
+ b["files"] = _cap_collection(b["files"], top_n)
1771
+ compact["blast_radius"] = b
1757
1772
  return compact
1758
1773
 
1759
1774
  def to_text(self, min_severity: str = "low") -> str:
@@ -1802,6 +1817,12 @@ class MigrationReport:
1802
1817
  lines.append(self.hibernate.to_text())
1803
1818
  lines.append("")
1804
1819
 
1820
+ if self.blast_radius is not None:
1821
+ from sourcecode.migration_blast import render_blast_text
1822
+
1823
+ lines.append(render_blast_text(self.blast_radius))
1824
+ lines.append("")
1825
+
1805
1826
  if not visible:
1806
1827
  lines.append("No findings at or above selected severity.")
1807
1828
  return "\n".join(lines)
@@ -0,0 +1,232 @@
1
+ """Migration blast radius — which request surface a migration finding sits under.
2
+
3
+ `migrate-check` answers *what must change*: the files whose `javax.*` imports, XML
4
+ descriptors and build coordinates block a Spring Boot 2→3 upgrade. It does not answer
5
+ *what has to be re-tested when that change lands*, which is the question a migration
6
+ plan is actually built around.
7
+
8
+ This module answers it as a rule over the existing IR — no new detector, no second
9
+ traversal engine. Each affected file resolves to the types it declares, those types are
10
+ run through the same caller BFS `impact-chain` uses (`spring_impact._bfs_callers` +
11
+ `_collect_endpoints`), and the reachable HTTP endpoints become that file's re-test set.
12
+
13
+ Honesty contract:
14
+ * A `javax→jakarta` finding breaks compilation whether or not an endpoint reaches it.
15
+ `endpoints_reached` ranks *regression scope*, never "what fails to build".
16
+ * A file whose declared types the IR never resolved is reported in `files_unresolved`
17
+ with reach `unknown` — never folded into the zero-reach bucket.
18
+ * A repository the IR sees no endpoints in gets `request_surface_share: null` and a
19
+ note saying ranking fell back to caller counts, never a manufactured 0.0.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ from typing import TYPE_CHECKING, Any, Optional
25
+
26
+ from sourcecode.spring_impact import _bfs_callers, _collect_endpoints
27
+
28
+ if TYPE_CHECKING: # pragma: no cover - typing only
29
+ from sourcecode.canonical_ir import CanonicalRepositoryIR
30
+ from sourcecode.migrate_check import MigrationFinding
31
+ from sourcecode.spring_model import SpringSemanticModel
32
+
33
+ SCHEMA_VERSION = "1.0"
34
+
35
+ DEFAULT_DEPTH = 4
36
+ DEFAULT_TOP_N = 25
37
+ _ENDPOINTS_PER_FILE = 10
38
+
39
+ _SEVERITY_ORDER = {"critical": 0, "high": 1, "medium": 2, "low": 3}
40
+ _TYPE_KINDS = ("class", "interface", "enum", "annotation")
41
+
42
+ _REACH_NOTE = (
43
+ "endpoints_reached ranks REGRESSION SCOPE — the requests whose call path runs "
44
+ "through the file — not what fails to build. A javax→jakarta finding breaks "
45
+ "compilation whether or not any endpoint reaches it."
46
+ )
47
+
48
+
49
+ def _types_by_file(cir: "CanonicalRepositoryIR") -> dict[str, list[str]]:
50
+ """Map source file → the type FQNs declared in it, from the IR's graph nodes."""
51
+ raw = getattr(cir, "_raw_ir", None) or {}
52
+ nodes = (raw.get("graph") or {}).get("nodes") or []
53
+ out: dict[str, list[str]] = {}
54
+ for node in nodes:
55
+ if not isinstance(node, dict):
56
+ continue
57
+ src = node.get("source_file") or ""
58
+ fqn = node.get("fqn") or ""
59
+ if not src or not fqn or "#" in fqn:
60
+ continue
61
+ if (node.get("symbol_kind") or node.get("type")) not in _TYPE_KINDS:
62
+ continue
63
+ out.setdefault(src, []).append(fqn)
64
+ for src in out:
65
+ out[src] = sorted(set(out[src]))
66
+ return out
67
+
68
+
69
+ def _max_severity(findings: list["MigrationFinding"]) -> str:
70
+ return min(
71
+ (f.severity for f in findings),
72
+ key=lambda s: _SEVERITY_ORDER.get(s, 3),
73
+ default="low",
74
+ )
75
+
76
+
77
+ def compute_migration_blast(
78
+ cir: "CanonicalRepositoryIR",
79
+ model: "SpringSemanticModel",
80
+ findings: list["MigrationFinding"],
81
+ *,
82
+ depth: int = DEFAULT_DEPTH,
83
+ top_n: int = DEFAULT_TOP_N,
84
+ ) -> dict:
85
+ """Resolve the request surface behind each migration finding.
86
+
87
+ Args:
88
+ cir: CanonicalRepositoryIR for the same repository that was scanned.
89
+ model: SpringSemanticModel built from that CIR (endpoint index).
90
+ findings: MigrationFinding list from `run_migrate_check`.
91
+ depth: Caller BFS depth, the same knob `impact-chain --depth` exposes.
92
+ top_n: Files retained in `files`; the rest are counted in `files_omitted`.
93
+
94
+ Returns a JSON-serializable dict. Never raises on a partial IR: anything it
95
+ could not resolve is reported as unresolved rather than dropped.
96
+ """
97
+ by_file: dict[str, list["MigrationFinding"]] = {}
98
+ non_source = 0
99
+ non_product = 0
100
+ for f in findings:
101
+ src = getattr(f, "source_file", "") or ""
102
+ if not src.endswith(".java"):
103
+ # XML descriptors and build coordinates carry no type the call graph
104
+ # can reach; they are counted, never ranked.
105
+ non_source += 1
106
+ continue
107
+ if getattr(f, "code_context", "main") != "main":
108
+ # Test and generated sources are outside the product blast radius, the
109
+ # same boundary blocking_count draws.
110
+ non_product += 1
111
+ continue
112
+ by_file.setdefault(src, []).append(f)
113
+
114
+ declared = _types_by_file(cir)
115
+ impl_graph = getattr(cir, "implementation_graph", None)
116
+ reverse_graph = getattr(cir, "reverse_graph", None) or {}
117
+ endpoints_in_repo = len(getattr(cir, "endpoints", None) or [])
118
+
119
+ rows: list[dict[str, Any]] = []
120
+ unresolved: list[str] = []
121
+ reached_ids: set[str] = set()
122
+
123
+ for src in sorted(by_file):
124
+ seeds = declared.get(src) or []
125
+ if not seeds:
126
+ unresolved.append(src)
127
+ continue
128
+ direct, indirect, truncated, *_rest = _bfs_callers(
129
+ seeds, reverse_graph, depth, impl_graph=impl_graph,
130
+ )
131
+ endpoints = _collect_endpoints(list(direct) + list(indirect), seeds, model)
132
+ for ep in endpoints:
133
+ reached_ids.add(ep.endpoint_id)
134
+ file_findings = by_file[src]
135
+ shown = sorted(
136
+ (e.to_dict() for e in endpoints),
137
+ key=lambda e: (e["path"], e["method"], e["endpoint_id"]),
138
+ )
139
+ rows.append({
140
+ "file": src,
141
+ "max_severity": _max_severity(file_findings),
142
+ "finding_count": len(file_findings),
143
+ "rule_ids": sorted({f.rule_id for f in file_findings}),
144
+ "types": seeds,
145
+ "direct_callers": len(direct),
146
+ "indirect_callers": len(indirect),
147
+ "traversal_truncated": bool(truncated),
148
+ "endpoints_reached": len(endpoints),
149
+ "endpoints": shown[:_ENDPOINTS_PER_FILE],
150
+ "endpoints_omitted": max(0, len(shown) - _ENDPOINTS_PER_FILE),
151
+ })
152
+
153
+ rows.sort(key=lambda r: (
154
+ -r["endpoints_reached"],
155
+ -(r["direct_callers"] + r["indirect_callers"]),
156
+ _SEVERITY_ORDER.get(r["max_severity"], 3),
157
+ r["file"],
158
+ ))
159
+
160
+ on_path = sum(1 for r in rows if r["endpoints_reached"] > 0)
161
+ notes: list[str] = [_REACH_NOTE]
162
+ if endpoints_in_repo:
163
+ share = round(len(reached_ids) / endpoints_in_repo, 4)
164
+ else:
165
+ share = None
166
+ notes.append(
167
+ "The IR resolved no HTTP endpoints in this repository, so no request "
168
+ "surface can be attributed — files are ranked by caller count alone."
169
+ )
170
+ if unresolved:
171
+ notes.append(
172
+ f"{len(unresolved)} file(s) carry a finding but declare no type the IR "
173
+ "resolved (parse gap or non-type source); their reach is unknown, not zero."
174
+ )
175
+ if any(r["traversal_truncated"] for r in rows):
176
+ notes.append(
177
+ "At least one traversal hit the hub-class guard and was capped — those "
178
+ "files' reach is a lower bound."
179
+ )
180
+
181
+ return {
182
+ "schema_version": SCHEMA_VERSION,
183
+ "depth": depth,
184
+ "endpoints_in_repo": endpoints_in_repo,
185
+ "endpoints_reached": len(reached_ids),
186
+ "request_surface_share": share,
187
+ "files_analyzed": len(rows),
188
+ "files_on_request_path": on_path,
189
+ "files_off_request_path": len(rows) - on_path,
190
+ "files_unresolved": sorted(unresolved),
191
+ "findings_not_source": non_source,
192
+ "findings_not_product": non_product,
193
+ "files": rows[:top_n],
194
+ "files_omitted": max(0, len(rows) - top_n),
195
+ "notes": notes,
196
+ }
197
+
198
+
199
+ def render_blast_text(blast: Optional[dict], *, top_n: int = 10) -> str:
200
+ """Render the blast-radius block as the text-format section."""
201
+ if not blast:
202
+ return ""
203
+ lines: list[str] = ["Migration blast radius (regression scope)"]
204
+ share = blast.get("request_surface_share")
205
+ if share is None:
206
+ lines.append(
207
+ f" {blast.get('endpoints_in_repo', 0)} endpoints in IR — "
208
+ "no request surface attributable; ranked by callers."
209
+ )
210
+ else:
211
+ lines.append(
212
+ f" {blast.get('endpoints_reached', 0)}/{blast.get('endpoints_in_repo', 0)} "
213
+ f"endpoints reached ({share * 100:.1f}% of the request surface)"
214
+ )
215
+ lines.append(
216
+ f" Files on a request path: {blast.get('files_on_request_path', 0)}"
217
+ f" of {blast.get('files_analyzed', 0)} analyzed"
218
+ + (f" [{len(blast.get('files_unresolved') or [])} unresolved]"
219
+ if blast.get("files_unresolved") else "")
220
+ )
221
+ for row in (blast.get("files") or [])[:top_n]:
222
+ lines.append(
223
+ f" {row['endpoints_reached']:>4} endpoints "
224
+ f"[{row['max_severity'].upper()}] {row['file']} "
225
+ f"({row['direct_callers']}+{row['indirect_callers']} callers)"
226
+ )
227
+ omitted = blast.get("files_omitted", 0) + max(0, len(blast.get("files") or []) - top_n)
228
+ if omitted:
229
+ lines.append(f" +{omitted} more file(s)")
230
+ for note in blast.get("notes") or []:
231
+ lines.append(f" note: {note}")
232
+ return "\n".join(lines)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 3.1.0
3
+ Version: 3.1.1
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
@@ -139,7 +139,7 @@ The system map: module graph, dependency views, REST surface, per-class summarie
139
139
  → [reference](docs/USER_GUIDE.md#core-commands)
140
140
 
141
141
  ### 4 · Migration & Modernization
142
- Is this codebase ready to upgrade? Per-dimension readiness (Jakarta / Spring Boot / JDK / Hibernate), located blockers, and an effort estimate.
142
+ Is this codebase ready to upgrade? Per-dimension readiness (Jakarta / Spring Boot / JDK / Hibernate), located blockers, an effort estimate, and — with `--blast-radius` — the endpoints whose call path runs through each blocker, so the re-test plan is ordered by regression scope.
143
143
  `ask migrate-check` · `ask modernize`
144
144
  → [migrate-check reference](docs/migrate-check.md) · [MODERNIZATION.md](docs/MODERNIZATION.md)
145
145
 
@@ -233,7 +233,7 @@ Matching endpoints report `policy: "custom"` and drop out of the `no_security_si
233
233
  |-----|----------------|
234
234
  | [USER_GUIDE.md](docs/USER_GUIDE.md) | Full command reference, flags, output schema, workflows |
235
235
  | [contracts.md](docs/contracts.md) | Declare invariants in `.ask/contracts.yml`, enforce them in the edit loop and in CI |
236
- | [posture.md](docs/posture.md) | What a profile set actually wires, and what could not be decided (experimental) |
236
+ | [posture.md](docs/posture.md) | What a profile set actually wires — down to which endpoints its filter chain permits — and what could not be decided (experimental) |
237
237
  | [migrate-check.md](docs/migrate-check.md) | Migration rule catalogue (MIG-001..043) + Hibernate stratification |
238
238
  | [MODERNIZATION.md](docs/MODERNIZATION.md) | The modernization product: assess → understand → plan → execute |
239
239
  | [PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md) | Free vs Pro, pricing model |
@@ -1,4 +1,4 @@
1
- sourcecode/__init__.py,sha256=RdGu3pDWTIDIExICX1CUTqllYPhYoeMc4ByyRDVrjIY,308
1
+ sourcecode/__init__.py,sha256=gL2tqDxnoKJo0NZ97V-BftlhGDV-Hcty4joUrxJ6XF0,308
2
2
  sourcecode/adaptive_scanner.py,sha256=yJBKjNpkY6bpueYJ2YnRezen3sYZDecEt7WaaNWdqug,9466
3
3
  sourcecode/archetype.py,sha256=qWMCyL2UzSGXJhCIZj38JjS7wI15EE3eILsWiaoCZXQ,35779
4
4
  sourcecode/architectural_baseline.py,sha256=7QzJri4pbL3nzAn9gNutZW6mZR8HHD6H2C2H1EEwYPE,17904
@@ -14,7 +14,7 @@ sourcecode/chain_rules.py,sha256=1kLHv1t7g5rMmycIYSpm3az8SjCr01Xw7LtyV2tNhSA,106
14
14
  sourcecode/change_plan.py,sha256=MNgNyu4zrLGvBBaXPwyCh-T2ZGaUQX3Hm4W87uuhZ3w,7750
15
15
  sourcecode/cir_graphs.py,sha256=9G0HHj1kw2325IDyzo2OpX73BNswEckecf4MZUXB4JM,12078
16
16
  sourcecode/classifier.py,sha256=JBzPwSSrDG-tUHAbcKB678HRbjLpD-ohzbzzO62mgpo,20114
17
- sourcecode/cli.py,sha256=zLML0Qwsxa_DQICh9QZMxKk0SIxeRu80oGmW_NV6ulM,395225
17
+ sourcecode/cli.py,sha256=d6ztendlmJIuXvXqb2uoaJmZnO_nisPmsChdJBgjycU,398039
18
18
  sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
19
19
  sourcecode/compare.py,sha256=jdePg0dNCxFpysiTGMsROL5XABLjy_zVUgVeySvdb0o,7514
20
20
  sourcecode/confidence_analyzer.py,sha256=vnbPI-20FnHdjO6STxHW8fbaxmB4A7y58io63ibFZjc,21586
@@ -52,7 +52,8 @@ sourcecode/jdk_exports.py,sha256=fCrlwNAXUT9gge_joq6kMnY3zJxYB2pxqy-0w3o3MJI,874
52
52
  sourcecode/license.py,sha256=keFuwNxdAtvK2Ds91Wl79GMYxuxWYnN5Wbw1qBpaoUI,24896
53
53
  sourcecode/mcp_nudge.py,sha256=lKemOqK_wny2u7Ymcr2Idi5Kx8pXY02jCi-_nJYLGMg,2992
54
54
  sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
55
- sourcecode/migrate_check.py,sha256=07jZp8CY6GYybK67092ma_EomBsYbYt-O4jarW5HmKQ,109340
55
+ sourcecode/migrate_check.py,sha256=TJvaShbglENOr8ikvCodnlXQy54TtT05N2ZVe02b3lc,110421
56
+ sourcecode/migration_blast.py,sha256=5qUsUi4n0cwBKltdJYyvyfhBkGENAYjB-d_7LZtnVks,9282
56
57
  sourcecode/openapi_surface.py,sha256=BTt0K-woZbkbWTN77IkqeBm_Okag9owR0848fmot8sk,16207
57
58
  sourcecode/output_budget.py,sha256=Js9yUlfQtPhqBl9R6wn_9UHVjjJc3GtLcqyfjf5t50Q,9869
58
59
  sourcecode/parse_cache.py,sha256=IIsWhtMYpBLNyRCDVoiVpUZa62QIk1Q1sZBDNG4G4V0,6668
@@ -158,8 +159,8 @@ sourcecode/telemetry/consent.py,sha256=GuhCiA_x2n3LwGBkZnvyIwtFcf5Zwa8O0DdLqkATI
158
159
  sourcecode/telemetry/events.py,sha256=4_yeO58U-Cwc1Qb27VB0_EjhmroY0k91n3_VGxeALB8,2776
159
160
  sourcecode/telemetry/filters.py,sha256=RzxauTz8HliO4BllQnXEXc7zTeqdCZi5MgqGEDuW7OQ,6570
160
161
  sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
161
- sourcecode-3.1.0.dist-info/METADATA,sha256=B4R07QBUVN2LdW8xLyj6ABp3wCpWDhJqCtXcv45F34s,11091
162
- sourcecode-3.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
163
- sourcecode-3.1.0.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
164
- sourcecode-3.1.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
165
- sourcecode-3.1.0.dist-info/RECORD,,
162
+ sourcecode-3.1.1.dist-info/METADATA,sha256=u9yY71cBQ2ZoUpQWoG78kSFzKV7LXTTuEyGIYVd3Ppk,11286
163
+ sourcecode-3.1.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
164
+ sourcecode-3.1.1.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
165
+ sourcecode-3.1.1.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
166
+ sourcecode-3.1.1.dist-info/RECORD,,