openhound 0.1.3.dev2__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.
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/workflows/test.yml +8 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/PKG-INFO +11 -10
- {openhound-0.1.3.dev2 → openhound-0.1.4}/pyproject.toml +15 -14
- openhound-0.1.4/src/openhound/core/lookup.py +56 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/preproc.py +34 -0
- openhound-0.1.4/tests/test_lookup.py +41 -0
- openhound-0.1.4/tests/test_preproc.py +93 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/uv.lock +366 -300
- openhound-0.1.3.dev2/src/openhound/core/lookup.py +0 -33
- openhound-0.1.3.dev2/tests/test_preproc_default_lookup.py +0 -34
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/CODE_OF_CONDUCT.md +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/CONTRIBUTING.md +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/GOVERNANCE.md +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/GitHub-COC-Header.png +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/GitHub-CONTRIB-Header.png +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/GitHub-GOV-Header.png +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/GitHub-Header.png +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/GitHub-LIC-Header.png +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/GitHub-SEC-Header.png +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/OpenHoundDark.svg +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/OpenHoundLight.svg +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/SECURITY.md +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/SO-Dark.svg +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/SO-FOSS-Dark.svg +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/SO-FOSS-Light.svg +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/SO-Light.svg +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/workflows/build-and-publish.yml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/workflows/build-and-sign-container.yml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/workflows/release-on-merge.yml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.github/workflows/validate-branch.yml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.gitignore +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/.pre-commit-config.yaml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/Dockerfile +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/LICENSE.md +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/README.md +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/deployments/helm/openhound/Chart.yaml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/deployments/helm/openhound/README.md +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/deployments/helm/openhound/templates/_helpers.tpl +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/deployments/helm/openhound/templates/deployment.yaml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/deployments/helm/openhound/templates/serviceaccount.yaml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/deployments/helm/openhound/values.yaml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/deployments/helm/values.example.yaml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/example-configurations/bloodhound-community/.dlt-example/config.toml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/example-configurations/bloodhound-community/.dlt-example/secrets_github.toml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/example-configurations/bloodhound-community/.dlt-example/secrets_jamf.toml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/example-configurations/bloodhound-community/.dlt-example/secrets_okta.toml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/example-configurations/bloodhound-community/README.md +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/example-configurations/bloodhound-community/docker-compose.yml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/example-configurations/bloodhound-enterprise/.dlt-example/config.toml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/example-configurations/bloodhound-enterprise/.dlt-example/secrets_github.toml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/example-configurations/bloodhound-enterprise/.dlt-example/secrets_jamf.toml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/example-configurations/bloodhound-enterprise/.dlt-example/secrets_okta.toml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/example-configurations/bloodhound-enterprise/README.md +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/example-configurations/bloodhound-enterprise/docker-compose.yml +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/justfile +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/__main__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/cli/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/cli/collect.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/cli/convert.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/cli/create.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/cli/override.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/cli/preproc.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/cli/privilege_zone.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/cli/saved_search.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/app.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/app.pyi +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/asset.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/clients/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/clients/bloodhound.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/clients/bloodhound_enterprise.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/clients/models/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/clients/models/asset_groups.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/clients/models/custom_nodes.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/clients/models/graph_cypher.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/clients/models/jobs.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/clients/models/saved_query.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/collect.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/context.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/convert.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/exceptions.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/logging.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/manager.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/models/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/models/collector.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/models/entries.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/models/entries_dataclass.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/models/extension.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/models/graph.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/models/icons.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/models/privilege_zone.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/models/saved_search.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/pipeline.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/privilege_zones.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/progress.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/resources.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/core/saved_searches.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/destinations/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/destinations/bloodhound/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/destinations/bloodhound/destination.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/destinations/bloodhound_enterprise/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/destinations/bloodhound_enterprise/destination.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/destinations/opengraph/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/destinations/opengraph/destination.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/docs/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/docs/pipeline.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/docs/templates/asset.md.j2 +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/docs/templates/class_table.md.j2 +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/docs/templates/edge.md.j2 +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/docs/templates/node.md.j2 +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/docs/templates/overview.md.j2 +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/docs/templates/pipeline.md.j2 +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/docs/templates/test +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/main.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/scheduler/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/scheduler/dataflow.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/scheduler/service.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/sources/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/sources/bloodhound_config/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/sources/bloodhound_config/source.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/sources/opengraph/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/sources/opengraph/entries.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/sources/opengraph/guid.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/sources/opengraph/source.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/sources/resource_files/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/openhound/sources/resource_files/source.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/src/scheduler.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/__init__.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/conftest.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_bhe_job_scheduling.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_collect_output_dir.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_convert_lookup_file.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_convert_output_dir.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_create_docs.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_data/api/jobs/job_end.json +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_data/api/jobs/job_start.json +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_data/api/jobs/jobs_available_empty.json +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_data/api/jobs/jobs_available_with_job.json +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_data/api/jobs/jobs_current.json +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_data/api/upload/upload_start.json +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_data/extensions/pz_selectors/jamf_tenant.json +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_data/extensions/saved_searches/jamf_query_by_name.json +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_data/sample_graph.json +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_docs_pipeline.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_extensions_format.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_log_handlers.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_pz_selector_sync.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_resource_def.py +0 -0
- {openhound-0.1.3.dev2 → openhound-0.1.4}/tests/test_saved_searches_sync.py +0 -0
|
@@ -59,3 +59,11 @@ jobs:
|
|
|
59
59
|
- name: Run BHE job scheduling test
|
|
60
60
|
run: |
|
|
61
61
|
.venv/bin/pytest tests/test_bhe_job_scheduling.py -v
|
|
62
|
+
|
|
63
|
+
- name: Run preprocess lookup generation tests
|
|
64
|
+
run: |
|
|
65
|
+
.venv/bin/pytest tests/test_preproc.py -v
|
|
66
|
+
|
|
67
|
+
- name: Run DuckDB lookup exception handling tests
|
|
68
|
+
run: |
|
|
69
|
+
.venv/bin/pytest tests/test_lookup.py -v
|
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhound
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: OpenGraph collector framework for BloodHound
|
|
5
5
|
License-File: LICENSE.md
|
|
6
6
|
Requires-Python: >=3.13
|
|
7
7
|
Requires-Dist: alive-progress>=3.3.0
|
|
8
8
|
Requires-Dist: cookiecutter>=2.6.0
|
|
9
|
-
Requires-Dist: dlt==1.
|
|
10
|
-
Requires-Dist: duckdb==1.5.
|
|
9
|
+
Requires-Dist: dlt==1.26.0
|
|
10
|
+
Requires-Dist: duckdb==1.5.2
|
|
11
11
|
Requires-Dist: griffe-fieldz>=0.5.0
|
|
12
12
|
Requires-Dist: griffe>=1.15.0
|
|
13
13
|
Requires-Dist: jinja2>=3.1.6
|
|
14
14
|
Requires-Dist: mkdocstrings[python]>=1.0.0
|
|
15
15
|
Requires-Dist: psutil>=7.2.1
|
|
16
|
-
Requires-Dist: pydantic-extra-types>=2.11.
|
|
17
|
-
Requires-Dist: pydantic==2.
|
|
16
|
+
Requires-Dist: pydantic-extra-types>=2.11.1
|
|
17
|
+
Requires-Dist: pydantic==2.13.3
|
|
18
18
|
Requires-Dist: tqdm>=4.67.1
|
|
19
|
-
Requires-Dist: typer>=0.
|
|
19
|
+
Requires-Dist: typer>=0.25.1
|
|
20
|
+
Requires-Dist: types-requests==2.33.0.20260503
|
|
20
21
|
Provides-Extra: all
|
|
21
22
|
Requires-Dist: openhound-github==0.1.0; extra == 'all'
|
|
22
|
-
Requires-Dist: openhound-jamf==0.1.
|
|
23
|
-
Requires-Dist: openhound-okta==0.1.
|
|
23
|
+
Requires-Dist: openhound-jamf==0.1.3; extra == 'all'
|
|
24
|
+
Requires-Dist: openhound-okta==0.1.2; extra == 'all'
|
|
24
25
|
Provides-Extra: github
|
|
25
26
|
Requires-Dist: openhound-github==0.1.0; extra == 'github'
|
|
26
27
|
Provides-Extra: jamf
|
|
27
|
-
Requires-Dist: openhound-jamf==0.1.
|
|
28
|
+
Requires-Dist: openhound-jamf==0.1.3; extra == 'jamf'
|
|
28
29
|
Provides-Extra: okta
|
|
29
|
-
Requires-Dist: openhound-okta==0.1.
|
|
30
|
+
Requires-Dist: openhound-okta==0.1.2; extra == 'okta'
|
|
30
31
|
Description-Content-Type: text/markdown
|
|
31
32
|
|
|
32
33
|
<p align="center">
|
|
@@ -6,35 +6,36 @@ readme = "README.md"
|
|
|
6
6
|
requires-python = ">=3.13"
|
|
7
7
|
dependencies = [
|
|
8
8
|
"alive-progress>=3.3.0",
|
|
9
|
-
"dlt==1.
|
|
10
|
-
"duckdb==1.5.
|
|
9
|
+
"dlt==1.26.0",
|
|
10
|
+
"duckdb==1.5.2",
|
|
11
11
|
"griffe>=1.15.0",
|
|
12
12
|
"griffe-fieldz>=0.5.0",
|
|
13
13
|
"mkdocstrings[python]>=1.0.0",
|
|
14
14
|
"psutil>=7.2.1",
|
|
15
|
-
"pydantic==2.
|
|
15
|
+
"pydantic==2.13.3",
|
|
16
16
|
"tqdm>=4.67.1",
|
|
17
|
-
"typer>=0.
|
|
17
|
+
"typer>=0.25.1",
|
|
18
18
|
"cookiecutter>=2.6.0",
|
|
19
|
-
"pydantic-extra-types>=2.11.0",
|
|
20
19
|
"jinja2>=3.1.6",
|
|
20
|
+
"types-requests==2.33.0.20260503",
|
|
21
|
+
"pydantic-extra-types>=2.11.1",
|
|
21
22
|
]
|
|
22
23
|
|
|
23
24
|
[project.optional-dependencies]
|
|
24
25
|
all = [
|
|
25
|
-
"openhound-jamf==0.1.
|
|
26
|
+
"openhound-jamf==0.1.3",
|
|
26
27
|
"openhound-github==0.1.0",
|
|
27
|
-
"openhound-okta==0.1.
|
|
28
|
+
"openhound-okta==0.1.2",
|
|
28
29
|
]
|
|
29
30
|
jamf = [
|
|
30
|
-
"openhound-jamf==0.1.
|
|
31
|
+
"openhound-jamf==0.1.3",
|
|
31
32
|
]
|
|
32
33
|
github = [
|
|
33
34
|
"openhound-github==0.1.0"
|
|
34
35
|
]
|
|
35
36
|
|
|
36
37
|
okta = [
|
|
37
|
-
"openhound-okta==0.1.
|
|
38
|
+
"openhound-okta==0.1.2",
|
|
38
39
|
]
|
|
39
40
|
|
|
40
41
|
[project.scripts]
|
|
@@ -66,13 +67,13 @@ local_scheme = "no-local-version"
|
|
|
66
67
|
[dependency-groups]
|
|
67
68
|
dev = [
|
|
68
69
|
"openhound-faker==0.0.6",
|
|
69
|
-
"ipython>=9.
|
|
70
|
+
"ipython>=9.13.0",
|
|
70
71
|
"pre-commit>=4.5.1",
|
|
71
72
|
"pytest>=9.0.1",
|
|
72
|
-
"marimo>=0.23.
|
|
73
|
-
"altair>=6.
|
|
74
|
-
"fastapi>=0.
|
|
75
|
-
"zensical>=0.0.
|
|
73
|
+
"marimo>=0.23.5",
|
|
74
|
+
"altair>=6.1.0",
|
|
75
|
+
"fastapi>=0.136.1",
|
|
76
|
+
"zensical>=0.0.40",
|
|
76
77
|
"ruff>=0.15.4",
|
|
77
78
|
"mypy>=1.19.1",
|
|
78
79
|
"types-pyyaml>=6.0.12.20250915",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import duckdb
|
|
4
|
+
from duckdb import DuckDBPyConnection
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LookupManager:
|
|
10
|
+
def __init__(self, client: DuckDBPyConnection, schema: str):
|
|
11
|
+
"""Create a DuckDB lookup helper bound to specific schema.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
client (DuckDBPyConnection): DuckDB connection used for queries.
|
|
15
|
+
schema (str): Schema name containing lookup tables.
|
|
16
|
+
"""
|
|
17
|
+
self.schema = schema
|
|
18
|
+
self.client = client
|
|
19
|
+
|
|
20
|
+
def _find_all_objects(self, *args) -> list:
|
|
21
|
+
"""Execute a query and return all rows.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
list: Query result rows as a list of tuples.
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
self.client.execute(*args)
|
|
28
|
+
results = self.client.fetchall()
|
|
29
|
+
return results
|
|
30
|
+
|
|
31
|
+
except duckdb.CatalogException as err:
|
|
32
|
+
logger.error("DuckDB lookup failed, missing table: %s", err)
|
|
33
|
+
return []
|
|
34
|
+
|
|
35
|
+
except duckdb.Error as err:
|
|
36
|
+
logger.error("DuckDB lookup query failed: %s", err)
|
|
37
|
+
return []
|
|
38
|
+
|
|
39
|
+
def _find_single_object(self, *args) -> str | None:
|
|
40
|
+
"""Execute a query and return the ID of the matching row
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
str | None: The first column (ie. ID) value as a string or None if no result is found
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
self.client.execute(*args)
|
|
47
|
+
result = self.client.fetchone()
|
|
48
|
+
return str(result[0]) if result else None
|
|
49
|
+
|
|
50
|
+
except duckdb.CatalogException as err:
|
|
51
|
+
logger.error("DuckDB lookup failed, missing table: %s", err)
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
except duckdb.Error as err:
|
|
55
|
+
logger.error("DuckDB lookup query failed: %s", err)
|
|
56
|
+
return None
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from dataclasses import dataclass
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from typing import Callable
|
|
@@ -12,6 +13,33 @@ from openhound.core.pipeline import BasePipeline
|
|
|
12
13
|
from openhound.core.progress import Progress
|
|
13
14
|
from openhound.sources.resource_files.source import resource_files
|
|
14
15
|
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def run_transform(
|
|
20
|
+
transform: Callable[..., None],
|
|
21
|
+
con: duckdb.DuckDBPyConnection,
|
|
22
|
+
*args,
|
|
23
|
+
**kwargs,
|
|
24
|
+
) -> None:
|
|
25
|
+
"""A transformer helper function that handles DuckDB exceptions when generating a lookup"""
|
|
26
|
+
try:
|
|
27
|
+
transform(con, *args, **kwargs)
|
|
28
|
+
|
|
29
|
+
except duckdb.CatalogException as err:
|
|
30
|
+
logger.error(
|
|
31
|
+
"DuckDB preprocessing transform '%s' failed due to missing table: %s",
|
|
32
|
+
transform.__name__,
|
|
33
|
+
err,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
except duckdb.Error as err:
|
|
37
|
+
logger.error(
|
|
38
|
+
"DuckDB preprocessing transform '%s' failed: %s",
|
|
39
|
+
transform.__name__,
|
|
40
|
+
err,
|
|
41
|
+
)
|
|
42
|
+
|
|
15
43
|
|
|
16
44
|
class PreProcessor(BasePipeline):
|
|
17
45
|
def __init__(
|
|
@@ -66,6 +94,12 @@ class PreProcessor(BasePipeline):
|
|
|
66
94
|
con = duckdb.connect(str(self.output_file))
|
|
67
95
|
try:
|
|
68
96
|
self.transformer(con)
|
|
97
|
+
except duckdb.CatalogException as err:
|
|
98
|
+
logger.error(
|
|
99
|
+
"DuckDB preprocessing failed due to missing table: %s", err
|
|
100
|
+
)
|
|
101
|
+
except duckdb.Error as err:
|
|
102
|
+
logger.error("DuckDB preprocessing failed: %s", err)
|
|
69
103
|
finally:
|
|
70
104
|
con.close()
|
|
71
105
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import duckdb
|
|
4
|
+
|
|
5
|
+
from openhound.core.lookup import LookupManager
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_find_single_object_returns_none_on_duckdb_error(caplog):
|
|
9
|
+
client = duckdb.connect(":memory:")
|
|
10
|
+
lookup = LookupManager(client, "main")
|
|
11
|
+
caplog.set_level(logging.ERROR, logger="openhound.core.lookup")
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
result = lookup._find_single_object("SELECT id FROM missing_table")
|
|
15
|
+
finally:
|
|
16
|
+
client.close()
|
|
17
|
+
|
|
18
|
+
assert result is None
|
|
19
|
+
assert any(
|
|
20
|
+
"DuckDB lookup failed, missing table:" in record.message
|
|
21
|
+
and "missing_table" in record.message
|
|
22
|
+
for record in caplog.records
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_find_all_objects_returns_empty_list_on_duckdb_error(caplog):
|
|
27
|
+
client = duckdb.connect(":memory:")
|
|
28
|
+
lookup = LookupManager(client, "main")
|
|
29
|
+
caplog.set_level(logging.ERROR, logger="openhound.core.lookup")
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
result = lookup._find_all_objects("SELECT id FROM missing_table")
|
|
33
|
+
finally:
|
|
34
|
+
client.close()
|
|
35
|
+
|
|
36
|
+
assert result == []
|
|
37
|
+
assert any(
|
|
38
|
+
"DuckDB lookup failed, missing table:" in record.message
|
|
39
|
+
and "missing_table" in record.message
|
|
40
|
+
for record in caplog.records
|
|
41
|
+
)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import duckdb
|
|
6
|
+
|
|
7
|
+
os.environ["RUNTIME__LOG_PATH"] = "/tmp/openhound-test-logs"
|
|
8
|
+
|
|
9
|
+
from openhound.core.app import DEFAULT_LOOKUP_FILE, OpenHound
|
|
10
|
+
from openhound.core.preproc import PreProcessor, run_transform
|
|
11
|
+
from openhound.core.progress import Progress
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_preproc_uses_default_lookup_file(monkeypatch, tmp_path):
|
|
15
|
+
captured: dict[str, Path] = {}
|
|
16
|
+
|
|
17
|
+
def fake_run(self, resources, filters=None):
|
|
18
|
+
captured["output_file"] = self.output_file
|
|
19
|
+
captured["resources"] = resources
|
|
20
|
+
return "ok"
|
|
21
|
+
|
|
22
|
+
monkeypatch.setattr(PreProcessor, "run", fake_run)
|
|
23
|
+
|
|
24
|
+
app = OpenHound("test", "test")
|
|
25
|
+
|
|
26
|
+
@app.preproc()
|
|
27
|
+
def preprocess(ctx):
|
|
28
|
+
return {"resource": "resource"}
|
|
29
|
+
|
|
30
|
+
result = app.preprocessor( # type: ignore[misc]
|
|
31
|
+
input_path=tmp_path,
|
|
32
|
+
progress=Progress.log,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
assert result == "ok"
|
|
36
|
+
assert captured["output_file"] == DEFAULT_LOOKUP_FILE
|
|
37
|
+
assert captured["resources"] == {"resource": "resource"}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_preproc_logs_duckdb_transform_errors(monkeypatch, tmp_path, caplog):
|
|
41
|
+
def fake_run(self, source, **kwargs):
|
|
42
|
+
return "ok"
|
|
43
|
+
|
|
44
|
+
def missing_table_transform(con: duckdb.DuckDBPyConnection):
|
|
45
|
+
con.execute("SELECT * FROM missing_table")
|
|
46
|
+
|
|
47
|
+
monkeypatch.setattr(PreProcessor, "_run", fake_run)
|
|
48
|
+
caplog.set_level(logging.ERROR, logger="openhound.core.preproc")
|
|
49
|
+
|
|
50
|
+
preprocessor = PreProcessor(
|
|
51
|
+
name="test",
|
|
52
|
+
input_path=tmp_path,
|
|
53
|
+
output_file=tmp_path / "lookup.duckdb",
|
|
54
|
+
transformer=missing_table_transform,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
result = preprocessor.run(resources={"resource": "resource"})
|
|
58
|
+
|
|
59
|
+
assert result == "ok"
|
|
60
|
+
assert any(
|
|
61
|
+
"DuckDB preprocessing failed due to missing table:" in record.message
|
|
62
|
+
and "missing_table" in record.message
|
|
63
|
+
for record in caplog.records
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_run_transform_logs_transform_name_and_continues(caplog):
|
|
68
|
+
called: list[str] = []
|
|
69
|
+
|
|
70
|
+
def missing_table_transform(con: duckdb.DuckDBPyConnection):
|
|
71
|
+
called.append("missing")
|
|
72
|
+
con.execute("SELECT * FROM missing_table")
|
|
73
|
+
|
|
74
|
+
def successful_transform(con: duckdb.DuckDBPyConnection):
|
|
75
|
+
called.append("successful")
|
|
76
|
+
con.execute("SELECT 1")
|
|
77
|
+
|
|
78
|
+
con = duckdb.connect(":memory:")
|
|
79
|
+
caplog.set_level(logging.ERROR, logger="openhound.core.preproc")
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
run_transform(missing_table_transform, con)
|
|
83
|
+
run_transform(successful_transform, con)
|
|
84
|
+
finally:
|
|
85
|
+
con.close()
|
|
86
|
+
|
|
87
|
+
assert called == ["missing", "successful"]
|
|
88
|
+
assert any(
|
|
89
|
+
"DuckDB preprocessing transform 'missing_table_transform' failed due to missing table:"
|
|
90
|
+
in record.message
|
|
91
|
+
and "missing_table" in record.message
|
|
92
|
+
for record in caplog.records
|
|
93
|
+
)
|