codmap 0.0.6__tar.gz → 0.0.7__tar.gz

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.
Files changed (121) hide show
  1. {codmap-0.0.6 → codmap-0.0.7}/PKG-INFO +2 -2
  2. {codmap-0.0.6 → codmap-0.0.7}/README.md +1 -1
  3. {codmap-0.0.6 → codmap-0.0.7}/codemap/provenance.py +23 -5
  4. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/session.py +11 -3
  5. {codmap-0.0.6 → codmap-0.0.7}/codmap.egg-info/PKG-INFO +2 -2
  6. {codmap-0.0.6 → codmap-0.0.7}/pyproject.toml +1 -1
  7. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c38_tool_drift.py +49 -0
  8. {codmap-0.0.6 → codmap-0.0.7}/LICENSE +0 -0
  9. {codmap-0.0.6 → codmap-0.0.7}/codemap/__init__.py +0 -0
  10. {codmap-0.0.6 → codmap-0.0.7}/codemap/apidiff.py +0 -0
  11. {codmap-0.0.6 → codmap-0.0.7}/codemap/arch.py +0 -0
  12. {codmap-0.0.6 → codmap-0.0.7}/codemap/cli.py +0 -0
  13. {codmap-0.0.6 → codmap-0.0.7}/codemap/diagnostics.py +0 -0
  14. {codmap-0.0.6 → codmap-0.0.7}/codemap/extract/__init__.py +0 -0
  15. {codmap-0.0.6 → codmap-0.0.7}/codemap/extract/attrflow.py +0 -0
  16. {codmap-0.0.6 → codmap-0.0.7}/codemap/extract/behavior.py +0 -0
  17. {codmap-0.0.6 → codmap-0.0.7}/codemap/extract/dataflow.py +0 -0
  18. {codmap-0.0.6 → codmap-0.0.7}/codemap/extract/dispatch.py +0 -0
  19. {codmap-0.0.6 → codmap-0.0.7}/codemap/extract/griffe_extractor.py +0 -0
  20. {codmap-0.0.6 → codmap-0.0.7}/codemap/extract/gsource.py +0 -0
  21. {codmap-0.0.6 → codmap-0.0.7}/codemap/extract/roots.py +0 -0
  22. {codmap-0.0.6 → codmap-0.0.7}/codemap/freshness.py +0 -0
  23. {codmap-0.0.6 → codmap-0.0.7}/codemap/incremental.py +0 -0
  24. {codmap-0.0.6 → codmap-0.0.7}/codemap/integrations/__init__.py +0 -0
  25. {codmap-0.0.6 → codmap-0.0.7}/codemap/integrations/base.py +0 -0
  26. {codmap-0.0.6 → codmap-0.0.7}/codemap/integrations/cocoindex.py +0 -0
  27. {codmap-0.0.6 → codmap-0.0.7}/codemap/integrations/gate.py +0 -0
  28. {codmap-0.0.6 → codmap-0.0.7}/codemap/integrations/gitnexus.py +0 -0
  29. {codmap-0.0.6 → codmap-0.0.7}/codemap/integrations/registry.py +0 -0
  30. {codmap-0.0.6 → codmap-0.0.7}/codemap/integrations/transport.py +0 -0
  31. {codmap-0.0.6 → codmap-0.0.7}/codemap/model.py +0 -0
  32. {codmap-0.0.6 → codmap-0.0.7}/codemap/query.py +0 -0
  33. {codmap-0.0.6 → codmap-0.0.7}/codemap/scope.py +0 -0
  34. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/__init__.py +0 -0
  35. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/_scip_pb2.py +0 -0
  36. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/api_surface.py +0 -0
  37. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/apidiff.py +0 -0
  38. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/architecture.py +0 -0
  39. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/audit.py +0 -0
  40. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/check.py +0 -0
  41. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/ctags.py +0 -0
  42. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/impact.py +0 -0
  43. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/limits.py +0 -0
  44. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/livingdocs.py +0 -0
  45. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/mcp_server.py +0 -0
  46. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/mermaid.py +0 -0
  47. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/pack.py +0 -0
  48. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/rag.py +0 -0
  49. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/review.py +0 -0
  50. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/scip.py +0 -0
  51. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/semantic.py +0 -0
  52. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/server.py +0 -0
  53. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/subsystems.py +0 -0
  54. {codmap-0.0.6 → codmap-0.0.7}/codemap/serve/vault.py +0 -0
  55. {codmap-0.0.6 → codmap-0.0.7}/codemap/store.py +0 -0
  56. {codmap-0.0.6 → codmap-0.0.7}/codemap/tomlio.py +0 -0
  57. {codmap-0.0.6 → codmap-0.0.7}/codemap/watch.py +0 -0
  58. {codmap-0.0.6 → codmap-0.0.7}/codmap.egg-info/SOURCES.txt +0 -0
  59. {codmap-0.0.6 → codmap-0.0.7}/codmap.egg-info/dependency_links.txt +0 -0
  60. {codmap-0.0.6 → codmap-0.0.7}/codmap.egg-info/entry_points.txt +0 -0
  61. {codmap-0.0.6 → codmap-0.0.7}/codmap.egg-info/requires.txt +0 -0
  62. {codmap-0.0.6 → codmap-0.0.7}/codmap.egg-info/top_level.txt +0 -0
  63. {codmap-0.0.6 → codmap-0.0.7}/setup.cfg +0 -0
  64. {codmap-0.0.6 → codmap-0.0.7}/tests/test_epistemic.py +0 -0
  65. {codmap-0.0.6 → codmap-0.0.7}/tests/test_gitnexus_router.py +0 -0
  66. {codmap-0.0.6 → codmap-0.0.7}/tests/test_impact_depth.py +0 -0
  67. {codmap-0.0.6 → codmap-0.0.7}/tests/test_integrations.py +0 -0
  68. {codmap-0.0.6 → codmap-0.0.7}/tests/test_issue3_serve_freshness.py +0 -0
  69. {codmap-0.0.6 → codmap-0.0.7}/tests/test_livingdocs.py +0 -0
  70. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m0_api_surface.py +0 -0
  71. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m11_argcontract.py +0 -0
  72. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m12_dataflow.py +0 -0
  73. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m15_review.py +0 -0
  74. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m16_architecture.py +0 -0
  75. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m17_mcp.py +0 -0
  76. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m18_freshness.py +0 -0
  77. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m1_5_semantics.py +0 -0
  78. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m1_query.py +0 -0
  79. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m2_views.py +0 -0
  80. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m32_watch.py +0 -0
  81. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m3_serve.py +0 -0
  82. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m4_behavior.py +0 -0
  83. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m5_deep.py +0 -0
  84. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m6_repo_scope.py +0 -0
  85. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m7_dispatch.py +0 -0
  86. {codmap-0.0.6 → codmap-0.0.7}/tests/test_m9_family.py +0 -0
  87. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c13_callgraph_accuracy.py +0 -0
  88. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c13_grep_vs_graph.py +0 -0
  89. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c16_semantic.py +0 -0
  90. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c20_attribute_edges.py +0 -0
  91. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c21_flat_layout.py +0 -0
  92. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c22_source_visible_refs.py +0 -0
  93. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c23_hard_python.py +0 -0
  94. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c24_test_mapping.py +0 -0
  95. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c25_provenance.py +0 -0
  96. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c26_deep_union.py +0 -0
  97. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c27_config_honesty.py +0 -0
  98. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c28_limit_envelope.py +0 -0
  99. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c29_lazy_imports.py +0 -0
  100. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c2_ctags.py +0 -0
  101. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c30_local_import_calls.py +0 -0
  102. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c30f1_reexport_calls.py +0 -0
  103. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c30f2_check_scope.py +0 -0
  104. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c31_path_origin.py +0 -0
  105. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c32_report_json.py +0 -0
  106. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c33_query_signature.py +0 -0
  107. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c34_signature_kinds.py +0 -0
  108. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c35_contract_location.py +0 -0
  109. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c36_target_resolution.py +0 -0
  110. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c37_rule_mutation.py +0 -0
  111. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c3_arch_contract.py +0 -0
  112. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c3_dogfood.py +0 -0
  113. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c4_complexity.py +0 -0
  114. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c5_apidiff.py +0 -0
  115. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c6_pack.py +0 -0
  116. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c7_edge_vocab.py +0 -0
  117. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c8_deadcode.py +0 -0
  118. {codmap-0.0.6 → codmap-0.0.7}/tests/test_r1c9_incremental.py +0 -0
  119. {codmap-0.0.6 → codmap-0.0.7}/tests/test_scip_export.py +0 -0
  120. {codmap-0.0.6 → codmap-0.0.7}/tests/test_scope.py +0 -0
  121. {codmap-0.0.6 → codmap-0.0.7}/tests/test_subsystems.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codmap
3
- Version: 0.0.6
3
+ Version: 0.0.7
4
4
  Summary: Static analyzer that turns a package's source into a queryable code graph.
5
5
  Author-email: kogriv <kogriv@gmail.com>
6
6
  License-Expression: MIT
@@ -44,7 +44,7 @@ and a **SCIP index** for interop with Sourcegraph / Glean and other precise-code
44
44
 
45
45
  [![CI](https://github.com/kogriv/codemap/actions/workflows/ci.yml/badge.svg)](https://github.com/kogriv/codemap/actions/workflows/ci.yml)
46
46
 
47
- **Status:** 🟢 M0–M20 implemented + research track (R1/R2) — schema 0.13, **732 tests with no failures on
47
+ **Status:** 🟢 M0–M20 implemented + research track (R1/R2) — schema 0.13, **734 tests with no failures on
48
48
  Python 3.11–3.14** ([in CI](docs/ci.md): the full suite including the dogfood pass, a determinism check, a
49
49
  wheel smoke test, and ctags/SCIP interop against the real CLIs), warm serve surface with 31 ops (28 exposed
50
50
  as MCP tools), and SCIP export. See **[DESIGN.md](DESIGN.md)** (product design &
@@ -8,7 +8,7 @@ and a **SCIP index** for interop with Sourcegraph / Glean and other precise-code
8
8
 
9
9
  [![CI](https://github.com/kogriv/codemap/actions/workflows/ci.yml/badge.svg)](https://github.com/kogriv/codemap/actions/workflows/ci.yml)
10
10
 
11
- **Status:** 🟢 M0–M20 implemented + research track (R1/R2) — schema 0.13, **732 tests with no failures on
11
+ **Status:** 🟢 M0–M20 implemented + research track (R1/R2) — schema 0.13, **734 tests with no failures on
12
12
  Python 3.11–3.14** ([in CI](docs/ci.md): the full suite including the dogfood pass, a determinism check, a
13
13
  wheel smoke test, and ctags/SCIP interop against the real CLIs), warm serve surface with 31 ops (28 exposed
14
14
  as MCP tools), and SCIP export. See **[DESIGN.md](DESIGN.md)** (product design &
@@ -43,12 +43,17 @@ MATCH, OLDER, NEWER, UNKNOWN = "match", "older", "newer", "unknown"
43
43
 
44
44
  # -- tool identity (D2) -------------------------------------------------------
45
45
 
46
- @lru_cache(maxsize=1)
47
- def tool_version() -> str | None:
48
- """The installed distribution's version, or None when it is not installed.
46
+ def installed_version() -> str | None:
47
+ """What the distribution *currently on disk* says, read on every call.
49
48
 
50
- The single source: `codemap.__version__` reads it too, having drifted a release
51
- behind while it was a literal.
49
+ R1-C38-f1. `tool_version()` below is cached for a whole process, which is right for
50
+ provenance — every graph a build writes must carry one version — and wrong for the
51
+ only question that needs the value twice: has the installation changed underneath a
52
+ long-running server? The cached reader answers with what was true at import, so a
53
+ comparison between the two is a comparison of one value with itself.
54
+
55
+ Measured before trusting it: an upgrade performed while a process runs *is* visible
56
+ to a fresh `importlib.metadata.version()`, with no `invalidate_caches()` needed.
52
57
  """
53
58
  from importlib.metadata import PackageNotFoundError, version
54
59
  try:
@@ -57,6 +62,19 @@ def tool_version() -> str | None:
57
62
  return None
58
63
 
59
64
 
65
+ @lru_cache(maxsize=1)
66
+ def tool_version() -> str | None:
67
+ """The installed distribution's version, or None when it is not installed.
68
+
69
+ The single source: `codemap.__version__` reads it too, having drifted a release
70
+ behind while it was a literal.
71
+
72
+ Cached on purpose — a build stamps one version into everything it writes. Anything
73
+ asking *"is this still true?"* rather than *"what was it?"* wants `installed_version()`.
74
+ """
75
+ return installed_version()
76
+
77
+
60
78
 
61
79
  @lru_cache(maxsize=8)
62
80
  def _commit_of(root: Path) -> str | None:
@@ -109,7 +109,11 @@ def tool_drift(running: str | None, installed: str | None) -> dict | None:
109
109
 
110
110
  `stats` already separates the graph's schema from the running tool's. This is the same
111
111
  separation one level up, and it is only visible because the version is read from the
112
- installed metadata at call time while the code was loaded at import.
112
+ installed metadata at call time while the code was loaded at import — which is a
113
+ property of *how the caller reads it*, not of this function. Shipped once without it
114
+ (0.0.6): `Session._tool_drift` passed the cached `tool_version()` on both sides, so the
115
+ two arguments were the same value by construction and this returned `None` forever.
116
+ See `installed_version()` and R1-C38-f1.
113
117
  """
114
118
  if not running or not installed or running == installed:
115
119
  return None
@@ -306,9 +310,13 @@ class Session:
306
310
 
307
311
  @staticmethod
308
312
  def _tool_drift() -> dict | None:
313
+ # The two sides must be read differently or there is nothing to compare (R1-C38-f1):
314
+ # `codemap.__version__` is fixed at import — that is the *running* code — while the
315
+ # installed distribution is re-read here, on every call. Using the cached
316
+ # `tool_version()` on the right made this compare a value with itself.
309
317
  import codemap
310
- from codemap.provenance import tool_version
311
- return tool_drift(getattr(codemap, "__version__", None), tool_version())
318
+ from codemap.provenance import installed_version
319
+ return tool_drift(getattr(codemap, "__version__", None), installed_version())
312
320
 
313
321
  def _op_reload(self, args) -> dict:
314
322
  """Reload the on-disk artifact into the served graph, without a restart (#3).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codmap
3
- Version: 0.0.6
3
+ Version: 0.0.7
4
4
  Summary: Static analyzer that turns a package's source into a queryable code graph.
5
5
  Author-email: kogriv <kogriv@gmail.com>
6
6
  License-Expression: MIT
@@ -44,7 +44,7 @@ and a **SCIP index** for interop with Sourcegraph / Glean and other precise-code
44
44
 
45
45
  [![CI](https://github.com/kogriv/codemap/actions/workflows/ci.yml/badge.svg)](https://github.com/kogriv/codemap/actions/workflows/ci.yml)
46
46
 
47
- **Status:** 🟢 M0–M20 implemented + research track (R1/R2) — schema 0.13, **732 tests with no failures on
47
+ **Status:** 🟢 M0–M20 implemented + research track (R1/R2) — schema 0.13, **734 tests with no failures on
48
48
  Python 3.11–3.14** ([in CI](docs/ci.md): the full suite including the dogfood pass, a determinism check, a
49
49
  wheel smoke test, and ctags/SCIP interop against the real CLIs), warm serve surface with 31 ops (28 exposed
50
50
  as MCP tools), and SCIP export. See **[DESIGN.md](DESIGN.md)** (product design &
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
9
9
  # version. README says this plainly, since `pip install codmap` / `import codemap` is a
10
10
  # seam a reader will otherwise hit by surprise.
11
11
  name = "codmap"
12
- version = "0.0.6"
12
+ version = "0.0.7"
13
13
  description = "Static analyzer that turns a package's source into a queryable code graph."
14
14
  readme = "README.md"
15
15
  # M20/D1: measured, not assumed. Each release of the declared range runs the full suite in
@@ -53,6 +53,55 @@ def test_an_unknown_version_is_not_a_drift():
53
53
  assert tool_drift(None, None) is None
54
54
 
55
55
 
56
+ def _shadow_installed_version(tmp_path, monkeypatch, version: str) -> None:
57
+ """Make the *installed* distribution look like `version`, the way an upgrade would.
58
+
59
+ A `dist-info` directory earlier on `sys.path` wins, so this changes what
60
+ `importlib.metadata` answers without touching the imported module — exactly the
61
+ asymmetry a real upgrade under a running server creates.
62
+ """
63
+ d = tmp_path / "site"
64
+ (d / f"codmap-{version}.dist-info").mkdir(parents=True)
65
+ (d / f"codmap-{version}.dist-info" / "METADATA").write_text(
66
+ f"Metadata-Version: 2.1\nName: codmap\nVersion: {version}\n")
67
+ monkeypatch.syspath_prepend(str(d))
68
+
69
+
70
+ def test_the_installed_side_is_read_fresh_or_there_is_nothing_to_compare(tmp_path, monkeypatch):
71
+ """R1-C38-f1 — the defect 0.0.6 shipped, and the reason no test caught it.
72
+
73
+ Every other test here hands `tool_drift` two versions, or forces a difference by
74
+ patching `codemap.__version__`. Both prove the arithmetic. Neither asks the question
75
+ the feature exists for: *can the condition arise?* It could not — `_tool_drift` read
76
+ the installed side through `tool_version()`, which is `lru_cache`d and first called at
77
+ import by `codemap/__init__.py`, so both sides were the same cached value forever.
78
+
79
+ Here nothing about the process is patched. Only the installation changes, as an
80
+ upgrade changes it, and the running server must notice.
81
+ """
82
+ from codemap.serve.session import Session
83
+
84
+ assert Session._tool_drift() is None # agreement before the "upgrade"
85
+ _shadow_installed_version(tmp_path, monkeypatch, "9.9.9")
86
+
87
+ d = Session._tool_drift()
88
+ assert d is not None, "the installed version is being read from a process-lifetime cache"
89
+ assert d["code"] == "tool_restart_needed" and d["installed"] == "9.9.9"
90
+
91
+
92
+ def test_the_build_side_stays_cached(tmp_path, monkeypatch):
93
+ """The fix must not turn provenance into a per-call filesystem read: a build stamps
94
+ one version into everything it writes, so `tool_version()` keeps its cache. The two
95
+ readers exist because they answer different questions — "what was it?" and "is it
96
+ still?" — and this is the assertion that keeps them apart."""
97
+ from codemap.provenance import installed_version, tool_version
98
+
99
+ before = tool_version()
100
+ _shadow_installed_version(tmp_path, monkeypatch, "9.9.9")
101
+ assert tool_version() == before
102
+ assert installed_version() == "9.9.9"
103
+
104
+
56
105
  def test_stats_and_reload_carry_it_when_it_applies(tmp_path, monkeypatch):
57
106
  import codemap
58
107
  from codemap.extract import extract
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes