entrygraph 0.1.3__tar.gz → 0.1.5__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.
- {entrygraph-0.1.3 → entrygraph-0.1.5}/PKG-INFO +1 -1
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/_version.py +2 -2
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/entrypoints/python.py +25 -14
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/frameworks.py +10 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/manifests.py +3 -1
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/graph/scoring.py +1 -1
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/pipeline/scanner.py +43 -22
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/resolve/resolver.py +4 -1
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_entrypoint_expansion.py +11 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_frameworks.py +12 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_manifests.py +11 -0
- entrygraph-0.1.5/tests/test_pool.py +64 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_resolver.py +32 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_scoring.py +9 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/.github/workflows/ci.yml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/.github/workflows/release.yml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/.gitignore +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/LICENSE +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/README.md +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/RELEASING.md +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/pyproject.toml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/__init__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/__main__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/api.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/cli/__init__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/cli/main.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/cli/render.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/data/sinks/csharp.toml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/data/sinks/go.toml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/data/sinks/java.toml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/data/sinks/javascript.toml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/data/sinks/lib_javascript.toml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/data/sinks/lib_python.toml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/data/sinks/php.toml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/data/sinks/python.toml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/data/sinks/ruby.toml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/data/sinks/rust.toml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/db/__init__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/db/engine.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/db/meta.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/db/models.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/db/queries.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/__init__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/entrypoints/__init__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/entrypoints/base.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/entrypoints/configs.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/entrypoints/csharp.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/entrypoints/golang.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/entrypoints/java.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/entrypoints/javascript.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/entrypoints/php.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/entrypoints/ruby.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/entrypoints/rust.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/detect/taint.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/errors.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/extract/__init__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/extract/base.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/extract/csharp.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/extract/golang.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/extract/ir.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/extract/java.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/extract/javascript.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/extract/php.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/extract/python.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/extract/registry.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/extract/ruby.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/extract/rust.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/fs/__init__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/fs/hashing.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/fs/lang.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/fs/walker.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/graph/__init__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/graph/adjacency.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/graph/cte.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/kinds.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/parsing/__init__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/parsing/parsers.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/parsing/queries.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/pipeline/__init__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/pipeline/worker.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/pipeline/writer.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/py.typed +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/csharp/calls.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/csharp/definitions.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/csharp/imports.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/go/calls.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/go/definitions.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/go/imports.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/java/calls.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/java/definitions.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/java/imports.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/javascript/calls.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/javascript/definitions.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/javascript/imports.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/php/calls.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/php/definitions.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/php/imports.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/python/calls.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/python/definitions.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/python/imports.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/ruby/calls.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/ruby/definitions.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/ruby/imports.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/rust/calls.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/rust/definitions.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/queries/rust/imports.scm +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/resolve/__init__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/resolve/externals.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/resolve/hierarchy.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/resolve/symbol_table.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/src/entrygraph/results.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/conftest.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/csharp/aspnet_app/Controllers/ReportsController.cs +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/csharp/aspnet_app/Program.cs +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/csharp/aspnet_app/Services/ReportService.cs +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/csharp/aspnet_app/app.csproj +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/go/gin_app/go.mod +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/go/gin_app/main.go +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/go/gin_app/service.go +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/java/spring_app/pom.xml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/java/spring_app/src/main/java/com/example/Application.java +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/java/spring_app/src/main/java/com/example/ReportRunner.java +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/java/spring_app/src/main/java/com/example/ReportService.java +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/java/spring_app/src/main/java/com/example/UserController.java +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/javascript/express_app/package.json +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/javascript/express_app/src/routes.js +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/javascript/express_app/src/services.js +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/php/laravel_app/app/Http/Controllers/ReportController.php +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/php/laravel_app/artisan +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/php/laravel_app/composer.json +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/php/laravel_app/routes/web.php +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/python/flask_app/app/__init__.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/python/flask_app/app/db.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/python/flask_app/app/routes.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/python/flask_app/app/services.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/python/flask_app/cli.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/python/flask_app/requirements.txt +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/ruby/sinatra_app/Gemfile +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/ruby/sinatra_app/app.rb +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/ruby/sinatra_app/services/runner.rb +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/rust/axum_app/Cargo.toml +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/rust/axum_app/src/handlers.rs +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/rust/axum_app/src/main.rs +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_api.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_cli.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_engine_pragmas.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_entrypoints.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_extract_csharp.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_extract_go.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_extract_java.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_extract_javascript.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_extract_php.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_extract_python.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_extract_ruby.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_extract_rust.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_hardening.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_incremental.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_indexer.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_lang.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_models.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_reachability.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_render.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_sink_catalog.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_taint.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/tests/test_walker.py +0 -0
- {entrygraph-0.1.3 → entrygraph-0.1.5}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: entrygraph
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
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.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
21
|
+
__version__ = version = '0.1.5'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 5)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -7,6 +7,7 @@ import re
|
|
|
7
7
|
|
|
8
8
|
from entrygraph.detect.entrypoints.base import (
|
|
9
9
|
EntrypointRule,
|
|
10
|
+
Matcher,
|
|
10
11
|
first_string_arg,
|
|
11
12
|
identifier_args,
|
|
12
13
|
methods_kwarg,
|
|
@@ -157,20 +158,30 @@ def _django_urls(x: FileExtraction) -> list[EntrypointHint]:
|
|
|
157
158
|
return hints
|
|
158
159
|
|
|
159
160
|
|
|
160
|
-
def
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
161
|
+
def _click_like(framework: str, rule_id: str) -> Matcher:
|
|
162
|
+
"""click and typer share decorator shape (`@app.command`); only the
|
|
163
|
+
reported framework/rule_id differ, so bind them per rule."""
|
|
164
|
+
|
|
165
|
+
def _match(x: FileExtraction) -> list[EntrypointHint]:
|
|
166
|
+
hints = []
|
|
167
|
+
for symbol, decorator in _decorated(x):
|
|
168
|
+
if _CLICK_CMD.match(decorator):
|
|
169
|
+
hints.append(
|
|
170
|
+
EntrypointHint(
|
|
171
|
+
rule_id=rule_id,
|
|
172
|
+
kind=EntrypointKind.CLI_COMMAND,
|
|
173
|
+
handler_qualified_name=symbol.qualified_name,
|
|
174
|
+
name=first_string_arg(decorator) or symbol.name,
|
|
175
|
+
framework=framework,
|
|
176
|
+
)
|
|
171
177
|
)
|
|
172
|
-
|
|
173
|
-
|
|
178
|
+
return hints
|
|
179
|
+
|
|
180
|
+
return _match
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
_click_commands = _click_like("click", "python.click.command")
|
|
184
|
+
_typer_commands = _click_like("typer", "python.typer.command")
|
|
174
185
|
|
|
175
186
|
|
|
176
187
|
def _celery_tasks(x: FileExtraction) -> list[EntrypointHint]:
|
|
@@ -363,7 +374,7 @@ register(
|
|
|
363
374
|
)
|
|
364
375
|
register(
|
|
365
376
|
EntrypointRule(
|
|
366
|
-
"python.typer.command", "python", "typer", EntrypointKind.CLI_COMMAND,
|
|
377
|
+
"python.typer.command", "python", "typer", EntrypointKind.CLI_COMMAND, _typer_commands
|
|
367
378
|
)
|
|
368
379
|
)
|
|
369
380
|
register(
|
|
@@ -104,6 +104,16 @@ FRAMEWORKS: list[FrameworkSpec] = [
|
|
|
104
104
|
),
|
|
105
105
|
),
|
|
106
106
|
# JS/TS, Go, Java, Ruby specs land with their extractors (M8)
|
|
107
|
+
FrameworkSpec(
|
|
108
|
+
"aws-lambda-js",
|
|
109
|
+
"javascript",
|
|
110
|
+
(
|
|
111
|
+
FrameworkSignal("file_presence", "serverless.y*ml", 0.5),
|
|
112
|
+
FrameworkSignal("file_presence", "template.y*ml", 0.3),
|
|
113
|
+
FrameworkSignal("manifest_dep", "@types/aws-lambda", 0.6),
|
|
114
|
+
FrameworkSignal("manifest_dep", "aws-lambda", 0.5),
|
|
115
|
+
),
|
|
116
|
+
),
|
|
107
117
|
FrameworkSpec(
|
|
108
118
|
"express",
|
|
109
119
|
"javascript",
|
|
@@ -223,9 +223,11 @@ def parse_manifests(root: str | Path) -> ManifestDeps:
|
|
|
223
223
|
if not manifest.is_file():
|
|
224
224
|
continue
|
|
225
225
|
if any(
|
|
226
|
-
part in ("node_modules", "vendor", ".venv", "venv", "target"
|
|
226
|
+
part in ("node_modules", "vendor", ".venv", "venv", "target")
|
|
227
227
|
for part in manifest.parts
|
|
228
228
|
):
|
|
229
|
+
# note: "packages" is intentionally NOT excluded — it is the
|
|
230
|
+
# standard JS workspace layout (packages/<name>/package.json).
|
|
229
231
|
continue
|
|
230
232
|
try:
|
|
231
233
|
text = manifest.read_text(encoding="utf-8", errors="replace")
|
|
@@ -33,7 +33,7 @@ _LENGTH_DECAY = 0.97
|
|
|
33
33
|
_CONST_TOKEN = re.compile(
|
|
34
34
|
r"""^(
|
|
35
35
|
\s | , | = | \( | \) | \[ | \] | \{ | \} | : |
|
|
36
|
-
'[^']*' | "[^"]*" | `[
|
|
36
|
+
'[^']*' | "[^"]*" | `(?:[^`$]|\$(?!\{))*` | # backtick: no ${...} interpolation
|
|
37
37
|
\d[\d_.eExXaAbBcCdDfF]* |
|
|
38
38
|
True|False|None|null|true|false|nil |
|
|
39
39
|
[A-Za-z_]\w*\s*= # kwarg name before '='
|
|
@@ -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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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:
|
|
@@ -285,12 +305,13 @@ def _write_files(
|
|
|
285
305
|
for path, fid in session.execute(select(File.path, File.id).where(File.repo_id == repo.id)):
|
|
286
306
|
file_id_by_path[path] = fid
|
|
287
307
|
|
|
288
|
-
|
|
308
|
+
to_index_paths = {w.path for w in diff.to_index}
|
|
309
|
+
reindexed = to_index_paths | {
|
|
289
310
|
w.path for w in walked if w.skip_reason and w.path not in file_id_by_path
|
|
290
311
|
}
|
|
291
312
|
new_rows = []
|
|
292
313
|
for wf in walked:
|
|
293
|
-
if wf.path in file_id_by_path and wf.path not in
|
|
314
|
+
if wf.path in file_id_by_path and wf.path not in to_index_paths:
|
|
294
315
|
continue # unchanged; row already present
|
|
295
316
|
if wf.path not in reindexed and not wf.skip_reason:
|
|
296
317
|
continue
|
|
@@ -71,6 +71,7 @@ class FileResolver:
|
|
|
71
71
|
self.module_symbol_id = module_symbol_id
|
|
72
72
|
self.table = table
|
|
73
73
|
self.externals = externals
|
|
74
|
+
self.is_package = is_package
|
|
74
75
|
self.prefix = LANG_PREFIX.get(extraction.language, extraction.language)
|
|
75
76
|
self.import_map, self.wildcard_modules = build_import_map(extraction, is_package)
|
|
76
77
|
|
|
@@ -122,7 +123,9 @@ class FileResolver:
|
|
|
122
123
|
seen: set[str] = set()
|
|
123
124
|
for imp in self.x.imports:
|
|
124
125
|
module = (
|
|
125
|
-
expand_relative(imp, self.x.module_path,
|
|
126
|
+
expand_relative(imp, self.x.module_path, self.is_package)
|
|
127
|
+
if imp.is_relative
|
|
128
|
+
else imp.module
|
|
126
129
|
)
|
|
127
130
|
if not module or module in seen:
|
|
128
131
|
continue
|
|
@@ -160,6 +160,17 @@ def test_lambda_js_handler_rule():
|
|
|
160
160
|
assert hints and hints[0].kind is EntrypointKind.LAMBDA_HANDLER
|
|
161
161
|
|
|
162
162
|
|
|
163
|
+
def test_typer_command_reports_typer_not_click():
|
|
164
|
+
# typer reuses click's decorator shape; the hint must carry framework/rule_id
|
|
165
|
+
# for typer, not be mislabeled as click.
|
|
166
|
+
sym = _sym("serve", "cli.serve")
|
|
167
|
+
sym.decorators = ["@app.command()"]
|
|
168
|
+
hints = _run("python.typer.command", _extraction([sym]), frameworks=frozenset({"typer"}))
|
|
169
|
+
assert hints
|
|
170
|
+
assert hints[0].framework == "typer"
|
|
171
|
+
assert hints[0].rule_id == "python.typer.command"
|
|
172
|
+
|
|
173
|
+
|
|
163
174
|
def test_sidekiq_worker_rule():
|
|
164
175
|
from entrygraph.detect.entrypoints import rules_for
|
|
165
176
|
|
|
@@ -33,6 +33,18 @@ def test_language_gating():
|
|
|
33
33
|
assert not any(d.name == "rails" for d in detected)
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def test_aws_lambda_js_detected_from_serverless_config():
|
|
37
|
+
# the javascript.aws-lambda.handler entrypoint rule is gated on this spec;
|
|
38
|
+
# regression: the spec was missing, so the rule could never fire.
|
|
39
|
+
detected = detect_frameworks(
|
|
40
|
+
ManifestDeps(javascript={"@types/aws-lambda"}),
|
|
41
|
+
import_signals=set(),
|
|
42
|
+
file_paths=["serverless.yml"],
|
|
43
|
+
languages_present={"javascript"},
|
|
44
|
+
)
|
|
45
|
+
assert any(d.name == "aws-lambda-js" for d in detected)
|
|
46
|
+
|
|
47
|
+
|
|
36
48
|
def test_typescript_counts_for_js_frameworks():
|
|
37
49
|
manifests = ManifestDeps(javascript={"express"})
|
|
38
50
|
detected = detect_frameworks(
|
|
@@ -78,6 +78,17 @@ def test_parse_manifests_fixture():
|
|
|
78
78
|
assert "requirements.txt" in deps.sources
|
|
79
79
|
|
|
80
80
|
|
|
81
|
+
def test_parse_manifests_reads_js_workspace_packages(tmp_path: Path):
|
|
82
|
+
# `packages/<name>/package.json` is the standard JS workspace layout and must
|
|
83
|
+
# not be excluded (regression: "packages" was in the skip list).
|
|
84
|
+
pkg = tmp_path / "packages" / "api"
|
|
85
|
+
pkg.mkdir(parents=True)
|
|
86
|
+
(pkg / "package.json").write_text('{"dependencies": {"express": "^4"}}')
|
|
87
|
+
deps = parse_manifests(tmp_path)
|
|
88
|
+
assert "express" in deps.javascript
|
|
89
|
+
assert "packages/api/package.json" in deps.sources
|
|
90
|
+
|
|
91
|
+
|
|
81
92
|
# ---------------- C1: C#, PHP, Rust manifests ----------------
|
|
82
93
|
from entrygraph.detect.manifests import ( # noqa: E402
|
|
83
94
|
parse_cargo_toml,
|
|
@@ -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]
|
|
@@ -57,6 +57,38 @@ def test_import_based_project_resolution():
|
|
|
57
57
|
assert call.confidence is Confidence.IMPORT
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
def test_relative_import_from_package_init_resolves_submodule():
|
|
61
|
+
# `from .sub import thing` in the package's own __init__.py (module_path
|
|
62
|
+
# "app", is_package=True) must resolve to the project module "app.sub".
|
|
63
|
+
# Regression: is_package was hardcoded False, dropping "app" and yielding a
|
|
64
|
+
# bogus ".sub" external placeholder.
|
|
65
|
+
table = make_table()
|
|
66
|
+
table.add_module("app.sub", 20)
|
|
67
|
+
x = FileExtraction(
|
|
68
|
+
path="app/__init__.py",
|
|
69
|
+
language="python",
|
|
70
|
+
module_path="app",
|
|
71
|
+
parse_ok=True,
|
|
72
|
+
error_count=0,
|
|
73
|
+
imports=[
|
|
74
|
+
RawImport(
|
|
75
|
+
module="sub",
|
|
76
|
+
imported_name="thing",
|
|
77
|
+
alias="thing",
|
|
78
|
+
span=SPAN,
|
|
79
|
+
is_relative=True,
|
|
80
|
+
relative_level=1,
|
|
81
|
+
)
|
|
82
|
+
],
|
|
83
|
+
)
|
|
84
|
+
externals = ExternalRegistry(iter(range(100, 200)).__next__)
|
|
85
|
+
resolver = FileResolver(x, 1, table, externals, is_package=True)
|
|
86
|
+
edges = resolver.resolve()
|
|
87
|
+
imp = next(e for e in edges if e.kind is EdgeKind.IMPORTS)
|
|
88
|
+
assert imp.dst_qname == "app.sub"
|
|
89
|
+
assert imp.dst_symbol_id == 20
|
|
90
|
+
|
|
91
|
+
|
|
60
92
|
def test_external_import_creates_placeholder():
|
|
61
93
|
table = make_table()
|
|
62
94
|
imports = [RawImport(module="subprocess", imported_name=None, alias="sub", span=SPAN)]
|
|
@@ -17,6 +17,15 @@ def test_is_constant_args():
|
|
|
17
17
|
assert not is_constant_args("(a, b, c, d, e, f, ...")
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
def test_js_template_literal_interpolation_is_not_constant():
|
|
21
|
+
# a plain backtick template with no interpolation is a literal
|
|
22
|
+
assert is_constant_args("(`ls -la`)")
|
|
23
|
+
# `${...}` carries a variable into the sink -> the archetypal JS command
|
|
24
|
+
# injection must NOT be scored as constant/safe
|
|
25
|
+
assert not is_constant_args("(`ls ${req.query.dir}`)")
|
|
26
|
+
assert not is_constant_args("(`${userInput}`)")
|
|
27
|
+
|
|
28
|
+
|
|
20
29
|
def test_score_path_ordering_by_severity():
|
|
21
30
|
common = {
|
|
22
31
|
"hop_confidences": [int(Confidence.EXACT)],
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{entrygraph-0.1.3 → entrygraph-0.1.5}/tests/fixtures/csharp/aspnet_app/Services/ReportService.cs
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|