loomweave-plugin-python 1.3.1__py3-none-any.whl → 1.4.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.
@@ -1,3 +1,3 @@
1
1
  """loomweave-plugin-python — Python language plugin for Loomweave."""
2
2
 
3
- __version__ = "1.3.1"
3
+ __version__ = "1.4.0"
@@ -1058,7 +1058,10 @@ class _WalkState:
1058
1058
  seen_ids: set[str]
1059
1059
  file_path: str
1060
1060
  wardline_vocabulary: WardlineVocabulary | None = None
1061
- exported_names: set[str] = field(default_factory=set)
1061
+ # ``None`` means no ``__all__`` was declared (triggers the public-surface
1062
+ # heuristic); a set (possibly empty) means ``__all__`` was declared
1063
+ # explicitly (clarion-4ec50f3d92).
1064
+ exported_names: set[str] | None = None
1062
1065
  duplicate_entities_dropped: int = 0
1063
1066
 
1064
1067
 
@@ -1274,16 +1277,39 @@ def _attach_wardline_entity_metadata(
1274
1277
  }
1275
1278
 
1276
1279
 
1277
- def _module_export_names(tree: ast.Module) -> set[str]:
1278
- exported: set[str] = set()
1280
+ def _module_export_names(tree: ast.Module) -> set[str] | None:
1281
+ """Names declared in a module-level ``__all__``, or ``None`` when no
1282
+ ``__all__`` is declared at all (clarion-4ec50f3d92).
1283
+
1284
+ The ``None`` vs empty-set distinction is load-bearing for the public-surface
1285
+ fallback: an explicit ``__all__ = []`` declares an *empty* public surface
1286
+ (no roots), whereas the *absence* of ``__all__`` triggers the PEP 8
1287
+ public-surface heuristic in :func:`_function_tags` / :func:`_class_tags`.
1288
+ Both plain (``__all__ = [...]``) and annotated (``__all__: list[str] = [...]``)
1289
+ assignments count as a declaration so an annotated ``__all__`` is never
1290
+ mistaken for an absent one. A non-literal value (``__all__ = a + b``, an
1291
+ alias, a comprehension) or an annotation-only stub (``__all__: list[str]``
1292
+ with no value) counts as a declaration but contributes no names — i.e. it is
1293
+ treated as an explicit *empty* surface, which suppresses ``public-surface``;
1294
+ this matches the prior behaviour for non-literal ``__all__`` (no names were
1295
+ ever extracted from it). Bare ``__all__ += [...]`` (``ast.AugAssign`` without a
1296
+ prior ``=`` assignment) is intentionally not handled: it is a runtime
1297
+ ``NameError`` and so unreachable in valid Python."""
1298
+ exported: set[str] | None = None
1279
1299
  for statement in tree.body:
1280
- if not isinstance(statement, ast.Assign):
1300
+ if isinstance(statement, ast.Assign):
1301
+ targets: list[ast.expr] = statement.targets
1302
+ value: ast.expr | None = statement.value
1303
+ elif isinstance(statement, ast.AnnAssign):
1304
+ targets = [statement.target]
1305
+ value = statement.value
1306
+ else:
1281
1307
  continue
1282
- if not any(
1283
- isinstance(target, ast.Name) and target.id == "__all__" for target in statement.targets
1284
- ):
1308
+ if not any(isinstance(target, ast.Name) and target.id == "__all__" for target in targets):
1285
1309
  continue
1286
- match statement.value:
1310
+ if exported is None:
1311
+ exported = set()
1312
+ match value:
1287
1313
  case ast.List(elts=elts) | ast.Tuple(elts=elts) | ast.Set(elts=elts):
1288
1314
  for elt in elts:
1289
1315
  if isinstance(elt, ast.Constant) and isinstance(elt.value, str):
@@ -1321,16 +1347,18 @@ def _is_module_level(parents: list[ast.AST]) -> bool:
1321
1347
  def _function_tags(
1322
1348
  node: ast.FunctionDef | ast.AsyncFunctionDef,
1323
1349
  parents: list[ast.AST],
1324
- exported_names: set[str],
1350
+ exported_names: set[str] | None,
1325
1351
  ) -> set[str]:
1326
1352
  tags: set[str] = set()
1327
- if _is_module_level(parents) and node.name == "main":
1328
- tags.add("entry-point")
1329
- if _is_module_level(parents) and node.name in exported_names:
1330
- tags.add("exported-api")
1331
- if node.name.startswith("test_") or any(
1353
+ module_level = _is_module_level(parents)
1354
+ is_test = node.name.startswith("test_") or any(
1332
1355
  isinstance(parent, ast.ClassDef) and parent.name.startswith("Test") for parent in parents
1333
- ):
1356
+ )
1357
+ if module_level and node.name == "main":
1358
+ tags.add("entry-point")
1359
+ if module_level:
1360
+ tags.update(_module_surface_tag(node.name, exported_names, is_test=is_test))
1361
+ if is_test:
1334
1362
  tags.add("test")
1335
1363
  decorator_names = _decorator_names(node)
1336
1364
  if any(_last_name(name) in _HTTP_ROUTE_DECORATOR_NAMES for name in decorator_names):
@@ -1340,11 +1368,38 @@ def _function_tags(
1340
1368
  return tags
1341
1369
 
1342
1370
 
1343
- def _class_tags(node: ast.ClassDef, parents: list[ast.AST], exported_names: set[str]) -> set[str]:
1371
+ def _module_surface_tag(name: str, exported_names: set[str] | None, *, is_test: bool) -> set[str]:
1372
+ """Root tag for a module-level public entity (clarion-4ec50f3d92).
1373
+
1374
+ With a declared ``__all__`` (``exported_names is not None``) only listed
1375
+ names are ``exported-api`` — the declaration is **authoritative**: a name the
1376
+ author explicitly exported is an exported root regardless of its spelling
1377
+ (so a ``Test``/``test_``-named or underscore-prefixed name listed in
1378
+ ``__all__`` still gets ``exported-api``). With no ``__all__`` at all
1379
+ (``None``) every non-underscore, non-test module-level def/class is the
1380
+ module's public surface by PEP 8 convention, tagged ``public-surface``: a
1381
+ reachability root of lower confidence than a declared export. It is a
1382
+ fail-toward-live posture for public code invoked from outside the static call
1383
+ graph (a library's consumers, or an app's framework-dispatched / DI / CLI
1384
+ handlers), so a codebase that does not exhaustively declare ``__all__`` is not
1385
+ read as mostly dead. ``is_test`` gates only this inferred fallback — test
1386
+ entities are already roots via the ``test`` tag and are not a module's public
1387
+ API; it never suppresses a declared export."""
1388
+ if exported_names is None:
1389
+ if is_test or name.startswith("_"):
1390
+ return set()
1391
+ return {"public-surface"}
1392
+ return {"exported-api"} if name in exported_names else set()
1393
+
1394
+
1395
+ def _class_tags(
1396
+ node: ast.ClassDef, parents: list[ast.AST], exported_names: set[str] | None
1397
+ ) -> set[str]:
1344
1398
  tags: set[str] = set()
1345
- if _is_module_level(parents) and node.name in exported_names:
1346
- tags.add("exported-api")
1347
- if node.name.startswith("Test"):
1399
+ is_test = node.name.startswith("Test")
1400
+ if _is_module_level(parents):
1401
+ tags.update(_module_surface_tag(node.name, exported_names, is_test=is_test))
1402
+ if is_test:
1348
1403
  tags.add("test")
1349
1404
  decorator_names = _decorator_names(node)
1350
1405
  base_names = [_expr_qualified_name(base) for base in node.bases]
@@ -34,7 +34,7 @@ from loomweave_plugin_python.pyright_session import PyrightRunState, PyrightSess
34
34
  from loomweave_plugin_python.stdout_guard import install_stdio
35
35
  from loomweave_plugin_python.wardline_descriptor import WardlineVocabulary, load_wardline_descriptor
36
36
 
37
- ONTOLOGY_VERSION = "0.8.0"
37
+ ONTOLOGY_VERSION = "0.9.0"
38
38
 
39
39
  # Plugin-side Content-Length sanity cap. Matches the host's ADR-021 §2b
40
40
  # default (8 MiB) so the plugin never emits a frame the host would kill us
@@ -1,7 +1,7 @@
1
1
  [plugin]
2
2
  name = "loomweave-plugin-python"
3
3
  plugin_id = "python"
4
- version = "1.3.1"
4
+ version = "1.4.0"
5
5
  protocol_version = "1.0"
6
6
  # Bare basename per ADR-021 §Layer 1 + WP2 scrub commit eb0a41d — the host
7
7
  # refuses manifests whose `executable` carries any path component.
@@ -47,13 +47,15 @@ edge_kinds = ["contains", "calls", "references", "imports", "inherits_from", "de
47
47
  rule_id_prefix = "LMWV-PY-"
48
48
  # Bumps per ADR-027 when the entity/edge/rule set shifts. Phase 3 Task 3 is a
49
49
  # MINOR bump (additive `imports` edge kind); 0.8.0 is the additive
50
- # `inherits_from` + `decorates` MINOR bump (clarion-43416be550). NOTE:
50
+ # `inherits_from` + `decorates` MINOR bump (clarion-43416be550); 0.9.0 is the
51
+ # additive `public-surface` reachability-root tag (clarion-4ec50f3d92) — a
52
+ # no-`__all__` library's public defs/classes are now tagged as roots. NOTE:
51
53
  # ADR-007's summary-cache key is the 5-tuple (entity_id, content_hash,
52
54
  # prompt_template_id, model_tier, guidance_fingerprint) — ontology_version is
53
55
  # handshake-validation, NOT a cache-key component. New edge rows miss the
54
56
  # cache by component-1 of the 5-tuple organically (no edges live in the
55
57
  # 5-tuple yet anyway).
56
- ontology_version = "0.8.0"
58
+ ontology_version = "0.9.0"
57
59
 
58
60
  [integrations.wardline]
59
61
  expected_descriptor_version = "wardline-generic-2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: loomweave-plugin-python
3
- Version: 1.3.1
3
+ Version: 1.4.0
4
4
  Summary: Loomweave Python language plugin — v1.0 release
5
5
  Author-email: John Morrissey <qacona@gmail.com>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -1,17 +1,17 @@
1
- loomweave_plugin_python/__init__.py,sha256=F2hCKasKT9GVx-LeFB-lrVGCATmp3bUJPMsqp3sGmac,95
1
+ loomweave_plugin_python/__init__.py,sha256=hs4dysFpi1-vI4oTjEZT2tpWkWjGUjVASqb-5pZ32gM,95
2
2
  loomweave_plugin_python/__main__.py,sha256=oy1S1Ru7WdX6jcAVhsqNI25LxAbQq3jHBlETBt20fyM,376
3
3
  loomweave_plugin_python/call_resolver.py,sha256=V6w5AEmWgjkKuWdgUPJTXStOavmmbyvjP0PQOeT3xkU,1692
4
4
  loomweave_plugin_python/entity_id.py,sha256=l7_5yyh6WVH9xIzrQVCDb2GmBMhovOVmuQezcZIVf8k,2691
5
- loomweave_plugin_python/extractor.py,sha256=3S98y0CTjapmNnkU_-E5ySUXDeGyXjzQQsIZGhxB3HM,56261
5
+ loomweave_plugin_python/extractor.py,sha256=U5c4LZgD5oBF3r2gFVeEetaPy38bqbRGqBPGpzLe34A,59490
6
6
  loomweave_plugin_python/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  loomweave_plugin_python/pyright_session.py,sha256=QWgXlOSgmN29iZ059sJE6hIbogi2mSCR3aTMFITXoyY,67579
8
8
  loomweave_plugin_python/qualname.py,sha256=gk6N2e4tMvufnPgpVZLv--c9_9gW5y9YvJIwUArffFg,2102
9
9
  loomweave_plugin_python/reference_resolver.py,sha256=hfcKX3WcDL26vEq7Ahdouq7WBs7mqCNy-rBvl8t97iI,2698
10
- loomweave_plugin_python/server.py,sha256=_MdaoRE1D-4VMvqLw79JyIvBmyqpU540Grk9FIBXSZg,12329
10
+ loomweave_plugin_python/server.py,sha256=kzqXIYpdvJuoBvXIYuhNhYxAdAR8y53g_gnRwWXdlZw,12329
11
11
  loomweave_plugin_python/stdout_guard.py,sha256=tUzcXyvXuy3-dXB81_xCNk8KWE3mqtsWSge81KPDhKg,2007
12
12
  loomweave_plugin_python/wardline_descriptor.py,sha256=9rU-DhN1USpsHW9hS6YeWFRgGL4xgBeG41Yr1LfK-M4,8136
13
- loomweave_plugin_python-1.3.1.data/data/share/loomweave/plugins/python/plugin.toml,sha256=pl20Cy5kCXJTUuAj9xoyVCEAMaIxEYZFzIpsR_tREG0,3580
14
- loomweave_plugin_python-1.3.1.dist-info/METADATA,sha256=TDhOrbMSA_cv8r0qtWuS5OoZugP_y9YdhytrSZ8YLaI,2751
15
- loomweave_plugin_python-1.3.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
16
- loomweave_plugin_python-1.3.1.dist-info/entry_points.txt,sha256=X1DQOYU5o1z2lBOJoh9JgQR5pg9fd_Yd8KozTHdfZqA,82
17
- loomweave_plugin_python-1.3.1.dist-info/RECORD,,
13
+ loomweave_plugin_python-1.4.0.data/data/share/loomweave/plugins/python/plugin.toml,sha256=NqBhIqUVl4UMmueS-tNJUarkhoX68NqvRStO70L4HGE,3740
14
+ loomweave_plugin_python-1.4.0.dist-info/METADATA,sha256=8R-BVHqL2iBXXs1umMRTAK9nzaX1VMWivkQ_Xr4C8JQ,2751
15
+ loomweave_plugin_python-1.4.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
16
+ loomweave_plugin_python-1.4.0.dist-info/entry_points.txt,sha256=X1DQOYU5o1z2lBOJoh9JgQR5pg9fd_Yd8KozTHdfZqA,82
17
+ loomweave_plugin_python-1.4.0.dist-info/RECORD,,