sphinx-codelinks 2.0.0__tar.gz → 2.1.0__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 (37) hide show
  1. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/PKG-INFO +5 -4
  2. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/pyproject.toml +9 -7
  3. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/__init__.py +1 -1
  4. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/analyse/analyse.py +39 -24
  5. sphinx_codelinks-2.1.0/src/sphinx_codelinks/analyse/entity.py +1610 -0
  6. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/analyse/models.py +23 -9
  7. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/analyse/utils.py +265 -192
  8. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/config.py +12 -7
  9. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/needextend_write.py +11 -3
  10. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/source_discover/config.py +4 -1
  11. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/sphinx_extension/debug.py +45 -7
  12. sphinx_codelinks-2.1.0/src/sphinx_codelinks/sphinx_extension/directives/__init__.py +0 -0
  13. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/sphinx_extension/directives/src_trace.py +230 -73
  14. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/sphinx_extension/html_wrapper.py +10 -2
  15. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/sphinx_extension/need_id_refs.py +13 -3
  16. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/sphinx_extension/source_tracing.py +6 -4
  17. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/LICENSE +0 -0
  18. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/README.md +0 -0
  19. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/analyse/__init__.py +0 -0
  20. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/analyse/multiline_parser.py +0 -0
  21. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/analyse/oneline_parser.py +0 -0
  22. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/analyse/preproc/__init__.py +0 -0
  23. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/analyse/preproc/compile_db.py +0 -0
  24. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/analyse/preproc/libclang_parser.py +0 -0
  25. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/analyse/preproc/loader.py +0 -0
  26. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/analyse/projects.py +0 -0
  27. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/analyse/references.py +0 -0
  28. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/cmd.py +0 -0
  29. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/logger.py +0 -0
  30. /sphinx_codelinks-2.0.0/src/sphinx_codelinks/source_discover/__init__.py → /sphinx_codelinks-2.1.0/src/sphinx_codelinks/py.typed +0 -0
  31. {sphinx_codelinks-2.0.0/src/sphinx_codelinks/sphinx_extension → sphinx_codelinks-2.1.0/src/sphinx_codelinks/source_discover}/__init__.py +0 -0
  32. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/source_discover/source_discover.py +0 -0
  33. {sphinx_codelinks-2.0.0/src/sphinx_codelinks/sphinx_extension/directives → sphinx_codelinks-2.1.0/src/sphinx_codelinks/sphinx_extension}/__init__.py +0 -0
  34. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/sphinx_extension/project_analysis.py +0 -0
  35. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/sphinx_extension/rediscovery.py +0 -0
  36. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/sphinx_extension/string_links.py +0 -0
  37. {sphinx_codelinks-2.0.0 → sphinx_codelinks-2.1.0}/src/sphinx_codelinks/sphinx_extension/ub_sct.css +0 -0
@@ -1,19 +1,20 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sphinx-codelinks
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: Fast Source Code Traceability for Sphinx-Needs
5
5
  Author-email: team useblocks <info@useblocks.com>
6
6
  Maintainer-email: Marco Heinemann <marco.heinemann@useblocks.com>, Jui-Wen Chen <jui-wen.chen@useblocks.com>
7
7
  Requires-Python: >=3.11,<4
8
8
  Description-Content-Type: text/markdown
9
+ Classifier: Typing :: Typed
9
10
  License-File: LICENSE
10
11
  Requires-Dist: comment-parser>=1.2.4
11
12
  Requires-Dist: ignore-python>=0.3.3
12
13
  Requires-Dist: typer>=0.16.0
13
14
  Requires-Dist: jsonschema
14
15
  Requires-Dist: sphinx>=7.4,<10
15
- Requires-Dist: sphinx-needs>=9.0.0,<10
16
- Requires-Dist: ub-project>=1.1.0,<2
16
+ Requires-Dist: sphinx-needs>=9.2.0,<10
17
+ Requires-Dist: ub-project>=1.2.0,<2
17
18
  Requires-Dist: jinja2
18
19
  Requires-Dist: pygments
19
20
  Requires-Dist: docutils>=0.21
@@ -28,7 +29,7 @@ Requires-Dist: tree-sitter-rust>=0.23.0
28
29
  Requires-Dist: tree-sitter-go>=0.23.0
29
30
  Requires-Dist: tree-sitter-json>=0.24.8
30
31
  Requires-Dist: tree-sitter-bash>=0.25.1
31
- Requires-Dist: furo>=2024.5.6 ; extra == "docs"
32
+ Requires-Dist: furo>=2025.12.19 ; extra == "docs"
32
33
  Requires-Dist: myst-parser>=4.0.0 ; extra == "docs"
33
34
  Requires-Dist: sphinx-ai-index>=1.0.0,<2 ; extra == "docs"
34
35
  Requires-Dist: sphinx-design>=0.6.1 ; extra == "docs"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sphinx-codelinks"
3
- version = "2.0.0"
3
+ version = "2.1.0"
4
4
  description = "Fast Source Code Traceability for Sphinx-Needs"
5
5
  authors = [{ name = "team useblocks", email = "info@useblocks.com" }]
6
6
  maintainers = [
@@ -15,6 +15,7 @@ readme = "README.md"
15
15
  # locks, builds and publishes -- the gap shows up only as an install failure for a user on
16
16
  # an interpreter inside it.
17
17
  requires-python = ">=3.11,<4"
18
+ classifiers = ["Typing :: Typed"]
18
19
  dependencies = [
19
20
  "comment-parser>=1.2.4",
20
21
  "ignore-python>=0.3.3",
@@ -27,8 +28,8 @@ dependencies = [
27
28
  # edge. uv never validates a specifier it resolves through `workspace = true` (uv#9811),
28
29
  # so the floor is the only statement of what was actually tested, and the cap is what
29
30
  # stops a future major being co-installed with a wheel written against this one.
30
- "sphinx-needs>=9.0.0,<10",
31
- "ub-project>=1.1.0,<2",
31
+ "sphinx-needs>=9.2.0,<10",
32
+ "ub-project>=1.2.0,<2",
32
33
  "jinja2",
33
34
  "pygments",
34
35
  "docutils>=0.21", # the `typing` group's series
@@ -64,7 +65,7 @@ libclang = ["libclang>=16"]
64
65
  # the root there re-exports each member's extra (`docs-mounts = ["sphinx-mounts[docs]"]`),
65
66
  # because `uv sync --extra` resolves against the workspace root rather than the member.
66
67
  docs = [
67
- "furo>=2024.5.6",
68
+ "furo>=2025.12.19",
68
69
  "myst-parser>=4.0.0",
69
70
  "sphinx-ai-index>=1.0.0,<2",
70
71
  "sphinx-design>=0.6.1",
@@ -84,9 +85,10 @@ Changelog = "https://github.com/useblocks/sphinx-needs/blob/master/packages/sphi
84
85
  requires = ["flit_core >=3.4,<4"]
85
86
  build-backend = "flit_core.buildapi"
86
87
 
87
- # Deliberately NO `[tool.flit.sdist]`: this distribution carries no non-Python payload, so
88
- # flit's module-only sdist is byte-for-byte the shape already published on PyPI, and
89
- # nothing downstream builds from it -- the git pins in the wild clone the repository.
88
+ # Deliberately NO `[tool.flit.sdist]`: this distribution carries nothing outside the module
89
+ # (its `py.typed` marker included, which flit ships as part of it), so flit's module-only
90
+ # sdist is the shape already published on PyPI, and nothing downstream builds from it --
91
+ # the git pins in the wild clone the repository.
90
92
  # Everything else -- ruff, ty, pytest and the dependency groups -- is the ROOT's, and
91
93
  # `tools/src/sn_tools/check_workspace.py` check (7) refuses those tables here: a member
92
94
  # `[tool.ruff]` does not extend the root's configuration, it replaces it.
@@ -2,7 +2,7 @@
2
2
 
3
3
  from sphinx_codelinks.sphinx_extension.source_tracing import setup
4
4
 
5
- __version__ = "2.0.0"
5
+ __version__ = "2.1.0"
6
6
 
7
7
  __all__ = [
8
8
  "__version__",
@@ -9,6 +9,7 @@ from tree_sitter import Node as TreeSitterNode
9
9
  from tree_sitter import Parser, Query
10
10
 
11
11
  from sphinx_codelinks.analyse import multiline_parser, utils
12
+ from sphinx_codelinks.analyse.entity import CodeScope, code_scope, scope_language
12
13
  from sphinx_codelinks.analyse.models import (
13
14
  MarkedContentType,
14
15
  MultilineNeed,
@@ -363,9 +364,14 @@ class SourceAnalyse:
363
364
  # ``c`` is a LibclangComment duck-typing the tree-sitter Node
364
365
  # interface SourceComment reads (``.text`` / ``.start_point.row``);
365
366
  # the Node-only path (find_associated_scope) is guarded by
366
- # ``is_libclang`` so it never runs on these.
367
+ # ``is_libclang`` so it never runs on these. The cast is that duck typing,
368
+ # which is why ty, seeing two unrelated classes, has to be told.
367
369
  src_comments = [
368
- SourceComment(cast("TreeSitterNode", c), c.column) for c in comments
370
+ SourceComment(
371
+ cast("TreeSitterNode", c), # ty: ignore[disjoint-cast]
372
+ c.column,
373
+ )
374
+ for c in comments
369
375
  ]
370
376
  src_file = SourceFile(src_path.absolute())
371
377
  src_file.add_comments(src_comments)
@@ -415,11 +421,13 @@ class SourceAnalyse:
415
421
  src_comment: SourceComment,
416
422
  first_row: int | None = None,
417
423
  first_column: int | None = None,
424
+ *,
425
+ scope: CodeScope | None = None,
418
426
  ) -> list[NeedIdRefs]:
419
427
  """Extract need-ids-refs from a comment.
420
428
 
421
429
  ``text`` starts at ``first_row`` and ``first_column`` (by default the
422
- comment's own row and column).
430
+ comment's own row and column). ``scope`` is the named ``tagged_scope``.
423
431
  """
424
432
  if first_row is None:
425
433
  first_row = src_comment.node.start_point.row
@@ -463,6 +471,7 @@ class SourceAnalyse:
463
471
  tagged_scope,
464
472
  need_ids,
465
473
  marker,
474
+ scope=scope,
466
475
  )
467
476
  )
468
477
  return anchors
@@ -573,11 +582,13 @@ class SourceAnalyse:
573
582
  oneline_comment_style: OneLineCommentStyle,
574
583
  first_row: int | None = None,
575
584
  first_column: int | None = None,
585
+ *,
586
+ scope: CodeScope | None = None,
576
587
  ) -> list[OneLineNeed]:
577
588
  """Extract the one-line needs of a comment.
578
589
 
579
590
  ``text`` starts at ``first_row`` and ``first_column`` (by default the
580
- comment's own row and column).
591
+ comment's own row and column). ``scope`` is the named ``tagged_scope``.
581
592
  """
582
593
  if first_row is None:
583
594
  first_row = src_comment.node.start_point.row
@@ -621,6 +632,7 @@ class SourceAnalyse:
621
632
  src_comment,
622
633
  tagged_scope,
623
634
  resolved, # int arguments were deleted # ty: ignore[invalid-argument-type]
635
+ scope=scope,
624
636
  )
625
637
  )
626
638
  return oneline_needs
@@ -689,11 +701,7 @@ class SourceAnalyse:
689
701
  ),
690
702
  run.comments[0],
691
703
  )
692
- tagged_scope: TreeSitterNode | None = None
693
- if not getattr(src_comment.node, "is_libclang", False):
694
- tagged_scope = utils.find_associated_scope(
695
- src_comment.node, self.analyse_config.comment_type
696
- )
704
+ tagged_scope, scope = self._associate(src_comment, filepath)
697
705
  lineno = block.open_row + 1
698
706
  remote_url = self.git_remote_url
699
707
  if self.git_remote_url and self.git_commit_rev:
@@ -713,15 +721,6 @@ class SourceAnalyse:
713
721
  if block.content_start is not None
714
722
  else None
715
723
  )
716
- scope = (
717
- {
718
- "kind": tagged_scope.type,
719
- "start": tagged_scope.start_point.row + 1,
720
- "end": tagged_scope.end_point.row + 1,
721
- }
722
- if tagged_scope is not None
723
- else None
724
- )
725
724
  source: dict[str, Any] = {
726
725
  "project": self.name,
727
726
  "path": _relative_posix(filepath, self.project_path),
@@ -742,6 +741,25 @@ class SourceAnalyse:
742
741
  block.need,
743
742
  block.markup,
744
743
  source,
744
+ scope=scope,
745
+ )
746
+
747
+ def _associate(
748
+ self, src_comment: SourceComment, filepath: Path
749
+ ) -> tuple[TreeSitterNode | None, CodeScope | None]:
750
+ """The entity a comment of ``filepath`` is attached to: its node and its
751
+ ``scope`` object.
752
+
753
+ A libclang comment has no tree-sitter node, so it is attached to nothing.
754
+ """
755
+ if getattr(src_comment.node, "is_libclang", False):
756
+ return None, None
757
+ comment_type = self.analyse_config.comment_type
758
+ association = utils.associate(src_comment.node, comment_type)
759
+ if association is None or not association.node.text:
760
+ return None, None
761
+ return association.node, code_scope(
762
+ association, comment_type, scope_language(comment_type, filepath)
745
763
  )
746
764
 
747
765
  def extract_marked_content(self) -> None:
@@ -756,12 +774,7 @@ class SourceAnalyse:
756
774
  )
757
775
  if not filepath:
758
776
  continue
759
- if getattr(src_comment.node, "is_libclang", False):
760
- tagged_scope: TreeSitterNode | None = None
761
- else:
762
- tagged_scope = utils.find_associated_scope(
763
- src_comment.node, self.analyse_config.comment_type
764
- )
777
+ tagged_scope, scope = self._associate(src_comment, filepath)
765
778
  for text, first_row, first_column in _scanned_texts(
766
779
  src_comment, claimed.get(id(src_comment))
767
780
  ):
@@ -774,6 +787,7 @@ class SourceAnalyse:
774
787
  src_comment,
775
788
  first_row,
776
789
  first_column,
790
+ scope=scope,
777
791
  )
778
792
  )
779
793
  if self.analyse_config.get_oneline_needs:
@@ -786,6 +800,7 @@ class SourceAnalyse:
786
800
  self.analyse_config.oneline_comment_style,
787
801
  first_row,
788
802
  first_column,
803
+ scope=scope,
789
804
  )
790
805
  )
791
806