entrygraph 0.1.3__tar.gz → 0.1.4__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 (166) hide show
  1. {entrygraph-0.1.3 → entrygraph-0.1.4}/PKG-INFO +1 -1
  2. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/_version.py +2 -2
  3. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/pipeline/scanner.py +40 -20
  4. entrygraph-0.1.4/tests/test_pool.py +64 -0
  5. {entrygraph-0.1.3 → entrygraph-0.1.4}/.github/workflows/ci.yml +0 -0
  6. {entrygraph-0.1.3 → entrygraph-0.1.4}/.github/workflows/release.yml +0 -0
  7. {entrygraph-0.1.3 → entrygraph-0.1.4}/.gitignore +0 -0
  8. {entrygraph-0.1.3 → entrygraph-0.1.4}/LICENSE +0 -0
  9. {entrygraph-0.1.3 → entrygraph-0.1.4}/README.md +0 -0
  10. {entrygraph-0.1.3 → entrygraph-0.1.4}/RELEASING.md +0 -0
  11. {entrygraph-0.1.3 → entrygraph-0.1.4}/pyproject.toml +0 -0
  12. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/__init__.py +0 -0
  13. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/__main__.py +0 -0
  14. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/api.py +0 -0
  15. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/cli/__init__.py +0 -0
  16. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/cli/main.py +0 -0
  17. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/cli/render.py +0 -0
  18. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/data/sinks/csharp.toml +0 -0
  19. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/data/sinks/go.toml +0 -0
  20. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/data/sinks/java.toml +0 -0
  21. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/data/sinks/javascript.toml +0 -0
  22. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/data/sinks/lib_javascript.toml +0 -0
  23. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/data/sinks/lib_python.toml +0 -0
  24. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/data/sinks/php.toml +0 -0
  25. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/data/sinks/python.toml +0 -0
  26. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/data/sinks/ruby.toml +0 -0
  27. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/data/sinks/rust.toml +0 -0
  28. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/db/__init__.py +0 -0
  29. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/db/engine.py +0 -0
  30. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/db/meta.py +0 -0
  31. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/db/models.py +0 -0
  32. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/db/queries.py +0 -0
  33. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/__init__.py +0 -0
  34. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/entrypoints/__init__.py +0 -0
  35. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/entrypoints/base.py +0 -0
  36. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/entrypoints/configs.py +0 -0
  37. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/entrypoints/csharp.py +0 -0
  38. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/entrypoints/golang.py +0 -0
  39. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/entrypoints/java.py +0 -0
  40. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/entrypoints/javascript.py +0 -0
  41. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/entrypoints/php.py +0 -0
  42. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/entrypoints/python.py +0 -0
  43. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/entrypoints/ruby.py +0 -0
  44. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/entrypoints/rust.py +0 -0
  45. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/frameworks.py +0 -0
  46. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/manifests.py +0 -0
  47. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/detect/taint.py +0 -0
  48. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/errors.py +0 -0
  49. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/extract/__init__.py +0 -0
  50. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/extract/base.py +0 -0
  51. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/extract/csharp.py +0 -0
  52. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/extract/golang.py +0 -0
  53. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/extract/ir.py +0 -0
  54. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/extract/java.py +0 -0
  55. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/extract/javascript.py +0 -0
  56. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/extract/php.py +0 -0
  57. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/extract/python.py +0 -0
  58. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/extract/registry.py +0 -0
  59. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/extract/ruby.py +0 -0
  60. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/extract/rust.py +0 -0
  61. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/fs/__init__.py +0 -0
  62. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/fs/hashing.py +0 -0
  63. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/fs/lang.py +0 -0
  64. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/fs/walker.py +0 -0
  65. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/graph/__init__.py +0 -0
  66. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/graph/adjacency.py +0 -0
  67. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/graph/cte.py +0 -0
  68. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/graph/scoring.py +0 -0
  69. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/kinds.py +0 -0
  70. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/parsing/__init__.py +0 -0
  71. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/parsing/parsers.py +0 -0
  72. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/parsing/queries.py +0 -0
  73. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/pipeline/__init__.py +0 -0
  74. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/pipeline/worker.py +0 -0
  75. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/pipeline/writer.py +0 -0
  76. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/py.typed +0 -0
  77. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/csharp/calls.scm +0 -0
  78. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/csharp/definitions.scm +0 -0
  79. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/csharp/imports.scm +0 -0
  80. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/go/calls.scm +0 -0
  81. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/go/definitions.scm +0 -0
  82. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/go/imports.scm +0 -0
  83. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/java/calls.scm +0 -0
  84. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/java/definitions.scm +0 -0
  85. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/java/imports.scm +0 -0
  86. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/javascript/calls.scm +0 -0
  87. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/javascript/definitions.scm +0 -0
  88. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/javascript/imports.scm +0 -0
  89. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/php/calls.scm +0 -0
  90. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/php/definitions.scm +0 -0
  91. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/php/imports.scm +0 -0
  92. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/python/calls.scm +0 -0
  93. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/python/definitions.scm +0 -0
  94. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/python/imports.scm +0 -0
  95. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/ruby/calls.scm +0 -0
  96. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/ruby/definitions.scm +0 -0
  97. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/ruby/imports.scm +0 -0
  98. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/rust/calls.scm +0 -0
  99. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/rust/definitions.scm +0 -0
  100. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/queries/rust/imports.scm +0 -0
  101. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/resolve/__init__.py +0 -0
  102. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/resolve/externals.py +0 -0
  103. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/resolve/hierarchy.py +0 -0
  104. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/resolve/resolver.py +0 -0
  105. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/resolve/symbol_table.py +0 -0
  106. {entrygraph-0.1.3 → entrygraph-0.1.4}/src/entrygraph/results.py +0 -0
  107. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/conftest.py +0 -0
  108. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/csharp/aspnet_app/Controllers/ReportsController.cs +0 -0
  109. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/csharp/aspnet_app/Program.cs +0 -0
  110. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/csharp/aspnet_app/Services/ReportService.cs +0 -0
  111. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/csharp/aspnet_app/app.csproj +0 -0
  112. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/go/gin_app/go.mod +0 -0
  113. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/go/gin_app/main.go +0 -0
  114. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/go/gin_app/service.go +0 -0
  115. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/java/spring_app/pom.xml +0 -0
  116. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/java/spring_app/src/main/java/com/example/Application.java +0 -0
  117. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/java/spring_app/src/main/java/com/example/ReportRunner.java +0 -0
  118. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/java/spring_app/src/main/java/com/example/ReportService.java +0 -0
  119. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/java/spring_app/src/main/java/com/example/UserController.java +0 -0
  120. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/javascript/express_app/package.json +0 -0
  121. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/javascript/express_app/src/routes.js +0 -0
  122. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/javascript/express_app/src/services.js +0 -0
  123. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/php/laravel_app/app/Http/Controllers/ReportController.php +0 -0
  124. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/php/laravel_app/artisan +0 -0
  125. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/php/laravel_app/composer.json +0 -0
  126. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/php/laravel_app/routes/web.php +0 -0
  127. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/python/flask_app/app/__init__.py +0 -0
  128. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/python/flask_app/app/db.py +0 -0
  129. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/python/flask_app/app/routes.py +0 -0
  130. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/python/flask_app/app/services.py +0 -0
  131. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/python/flask_app/cli.py +0 -0
  132. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/python/flask_app/requirements.txt +0 -0
  133. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/ruby/sinatra_app/Gemfile +0 -0
  134. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/ruby/sinatra_app/app.rb +0 -0
  135. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/ruby/sinatra_app/services/runner.rb +0 -0
  136. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/rust/axum_app/Cargo.toml +0 -0
  137. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/rust/axum_app/src/handlers.rs +0 -0
  138. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/fixtures/rust/axum_app/src/main.rs +0 -0
  139. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_api.py +0 -0
  140. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_cli.py +0 -0
  141. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_engine_pragmas.py +0 -0
  142. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_entrypoint_expansion.py +0 -0
  143. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_entrypoints.py +0 -0
  144. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_extract_csharp.py +0 -0
  145. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_extract_go.py +0 -0
  146. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_extract_java.py +0 -0
  147. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_extract_javascript.py +0 -0
  148. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_extract_php.py +0 -0
  149. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_extract_python.py +0 -0
  150. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_extract_ruby.py +0 -0
  151. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_extract_rust.py +0 -0
  152. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_frameworks.py +0 -0
  153. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_hardening.py +0 -0
  154. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_incremental.py +0 -0
  155. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_indexer.py +0 -0
  156. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_lang.py +0 -0
  157. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_manifests.py +0 -0
  158. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_models.py +0 -0
  159. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_reachability.py +0 -0
  160. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_render.py +0 -0
  161. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_resolver.py +0 -0
  162. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_scoring.py +0 -0
  163. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_sink_catalog.py +0 -0
  164. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_taint.py +0 -0
  165. {entrygraph-0.1.3 → entrygraph-0.1.4}/tests/test_walker.py +0 -0
  166. {entrygraph-0.1.3 → entrygraph-0.1.4}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: entrygraph
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Language-agnostic code graph: query symbols, entrypoints, and source-to-sink call paths from a SQLite index
5
5
  Project-URL: Repository, https://github.com/brettbergin/entrygraph
6
6
  Author-email: Brett Bergin <brettberginbc@yahoo.com>
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.1.3'
22
- __version_tuple__ = version_tuple = (0, 1, 3)
21
+ __version__ = version = '0.1.4'
22
+ __version_tuple__ = version_tuple = (0, 1, 4)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -11,8 +11,10 @@ from __future__ import annotations
11
11
 
12
12
  import json
13
13
  import os
14
+ import sys
14
15
  import time
15
16
  from concurrent.futures import ProcessPoolExecutor
17
+ from concurrent.futures.process import BrokenProcessPool
16
18
  from datetime import UTC, datetime
17
19
  from pathlib import Path
18
20
 
@@ -147,19 +149,36 @@ def index_repository(
147
149
  def _pool_context():
148
150
  """multiprocessing context for the parse pool.
149
151
 
150
- Prefer ``fork`` where available (POSIX): unlike the ``spawn`` default on
151
- macOS/Windows, fork does not re-import the caller's ``__main__`` module, so
152
- ``CodeGraph.index()`` works from any calling context scripts without an
153
- ``if __name__ == "__main__"`` guard, notebooks, web request handlers rather
154
- than raising a bootstrapping RuntimeError and re-running top-level code in
155
- every worker. tree-sitter parsers are created lazily inside each worker, so
156
- there is no pre-fork C state to corrupt.
152
+ On Linux, prefer ``fork``: unlike ``spawn``, fork does not re-import the
153
+ caller's ``__main__`` module, so ``CodeGraph.index()`` works from any calling
154
+ context scripts without an ``if __name__ == "__main__"`` guard, notebooks,
155
+ web request handlers rather than raising a bootstrapping RuntimeError and
156
+ re-running top-level code in every worker. tree-sitter parsers are created
157
+ lazily inside each worker, so there is no pre-fork C state to corrupt.
158
+
159
+ macOS is excluded: ``fork`` without ``exec`` is unsafe there because Apple
160
+ system frameworks (Objective-C runtime, libdispatch) abort the child if they
161
+ were touched by any thread in the parent (``+[NSNumber initialize] may have
162
+ been in progress ... when fork() was called``). macOS and Windows use
163
+ ``spawn``; the CLI entry points are ``__main__``-guarded, and unguarded
164
+ library callers fall back to sequential extraction in ``_parse_phase``.
157
165
  """
158
166
  import multiprocessing as mp
159
167
 
160
- if "fork" in mp.get_all_start_methods():
168
+ if sys.platform != "darwin" and "fork" in mp.get_all_start_methods():
161
169
  return mp.get_context("fork")
162
- return mp.get_context() # spawn (Windows); callers must guard __main__
170
+ return mp.get_context("spawn")
171
+
172
+
173
+ def _extract_sequential(
174
+ to_index: list[WalkedFile],
175
+ ) -> list[tuple[str, FileExtraction, bool]]:
176
+ results = []
177
+ for wf in to_index:
178
+ result = extract_one(wf)
179
+ if result is not None:
180
+ results.append(result)
181
+ return results
163
182
 
164
183
 
165
184
  def _parse_phase(
@@ -169,19 +188,20 @@ def _parse_phase(
169
188
  return []
170
189
  workers = max_workers if max_workers is not None else (os.cpu_count() or 2)
171
190
  if len(to_index) < _PARALLEL_THRESHOLD or workers <= 1:
172
- results = []
173
- for wf in to_index:
174
- result = extract_one(wf)
175
- if result is not None:
176
- results.append(result)
177
- return results
191
+ return _extract_sequential(to_index)
178
192
 
179
193
  batches = [to_index[i : i + _BATCH] for i in range(0, len(to_index), _BATCH)]
180
- results = []
181
- with ProcessPoolExecutor(max_workers=workers, mp_context=_pool_context()) as pool:
182
- for batch_result in pool.map(extract_batch, batches):
183
- results.extend(batch_result)
184
- return results
194
+ try:
195
+ results = []
196
+ with ProcessPoolExecutor(max_workers=workers, mp_context=_pool_context()) as pool:
197
+ for batch_result in pool.map(extract_batch, batches):
198
+ results.extend(batch_result)
199
+ return results
200
+ except BrokenProcessPool:
201
+ # A worker pool couldn't start or died (e.g. an unguarded __main__ under
202
+ # spawn, or a sandbox with no subprocess support). Degrade to correct,
203
+ # single-threaded extraction rather than crashing the whole index.
204
+ return _extract_sequential(to_index)
185
205
 
186
206
 
187
207
  def _load_or_create_repo(session: Session, root: Path, incremental: bool) -> Repository:
@@ -0,0 +1,64 @@
1
+ """Parse-pool start method selection and graceful fallback (issue #5)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import multiprocessing as mp
6
+
7
+ import pytest
8
+
9
+ from entrygraph.fs.walker import WalkedFile
10
+ from entrygraph.pipeline import scanner
11
+
12
+
13
+ def _walked(n: int) -> list[WalkedFile]:
14
+ return [
15
+ WalkedFile(
16
+ path=f"f{i}.py",
17
+ abs_path=f"/repo/f{i}.py",
18
+ language="python",
19
+ size_bytes=1,
20
+ mtime_ns=0,
21
+ )
22
+ for i in range(n)
23
+ ]
24
+
25
+
26
+ def test_pool_context_avoids_fork_on_macos(monkeypatch: pytest.MonkeyPatch) -> None:
27
+ monkeypatch.setattr("sys.platform", "darwin")
28
+ ctx = scanner._pool_context()
29
+ assert ctx.get_start_method() != "fork"
30
+
31
+
32
+ def test_pool_context_uses_fork_on_linux(monkeypatch: pytest.MonkeyPatch) -> None:
33
+ if "fork" not in mp.get_all_start_methods():
34
+ pytest.skip("fork start method unavailable on this platform")
35
+ monkeypatch.setattr("sys.platform", "linux")
36
+ ctx = scanner._pool_context()
37
+ assert ctx.get_start_method() == "fork"
38
+
39
+
40
+ def test_parse_phase_falls_back_when_pool_breaks(monkeypatch: pytest.MonkeyPatch) -> None:
41
+ to_index = _walked(scanner._PARALLEL_THRESHOLD + 5)
42
+
43
+ def fake_extract_one(wf: WalkedFile):
44
+ return (wf.path, object(), True)
45
+
46
+ class _BrokenPool:
47
+ def __init__(self, *args, **kwargs):
48
+ pass
49
+
50
+ def __enter__(self):
51
+ return self
52
+
53
+ def __exit__(self, *exc):
54
+ return False
55
+
56
+ def map(self, *args, **kwargs):
57
+ raise scanner.BrokenProcessPool("simulated worker death")
58
+
59
+ monkeypatch.setattr(scanner, "extract_one", fake_extract_one)
60
+ monkeypatch.setattr(scanner, "ProcessPoolExecutor", _BrokenPool)
61
+
62
+ results = scanner._parse_phase(to_index, max_workers=4)
63
+
64
+ assert [r[0] for r in results] == [wf.path for wf in to_index]
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