From 22bec9b07fa0df890168930155c01374cb544d9a Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Wed, 13 Aug 2025 12:55:20 -0700 Subject: [PATCH 01/19] migrated to uv --- .pre-commit-config.yaml | 10 +- LICENSE.md => LICENSE | 0 .../api => datacommons_api}/__init__.py | 0 .../api => datacommons_api}/app.py | 2 +- .../api => datacommons_api}/app_cli.py | 8 +- .../api => datacommons_api}/core/__init__.py | 0 .../api => datacommons_api}/core/config.py | 2 +- .../api => datacommons_api}/core/constants.py | 0 .../api => datacommons_api}/core/logging.py | 4 +- .../endpoints/__init__.py | 0 .../endpoints/dependencies.py | 6 +- .../endpoints/responses.py | 0 .../endpoints/routers/__init__.py | 0 .../endpoints/routers/node_router.py | 12 +- .../services/__init__.py | 0 .../services/graph_service.py | 8 +- .../datacommons_api/version.py | 1 + packages/datacommons-api/pyproject.toml | 52 +- packages/datacommons-db/README.md | 4 +- .../db => datacommons_db}/__init__.py | 0 .../db => datacommons_db}/models/__init__.py | 0 .../db => datacommons_db}/models/base.py | 0 .../db => datacommons_db}/models/edge.py | 2 +- .../db => datacommons_db}/models/node.py | 2 +- .../models/observation.py | 2 +- .../repositories/__init__.py | 0 .../repositories/node_repository.py | 2 +- .../db => datacommons_db}/session.py | 2 +- .../datacommons-db/datacommons_db/version.py | 1 + packages/datacommons-db/pyproject.toml | 25 +- .../src/datacommons/__init__.py | 1 - packages/datacommons-schema/README.md | 4 +- .../schema => datacommons_schema}/__init__.py | 0 .../converters/__init__.py | 0 .../converters/mcf_to_jsonld.py | 4 +- .../models/__init__.py | 0 .../models/jsonld.py | 0 .../models/mcf.py | 0 .../parsers/__init__.py | 0 .../parsers/mcf_parser.py | 2 +- .../parsers/mcf_parser_test.py | 2 +- .../schema_cli.py | 4 +- .../datacommons_schema/version.py | 1 + packages/datacommons-schema/pyproject.toml | 32 +- pyproject.toml | 109 +- uv.lock | 945 ++++++++++++++++++ 46 files changed, 1088 insertions(+), 161 deletions(-) rename LICENSE.md => LICENSE (100%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/__init__.py (100%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/app.py (93%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/app_cli.py (87%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/core/__init__.py (100%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/core/config.py (97%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/core/constants.py (100%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/core/logging.py (96%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/endpoints/__init__.py (100%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/endpoints/dependencies.py (86%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/endpoints/responses.py (100%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/endpoints/routers/__init__.py (100%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/endpoints/routers/node_router.py (83%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/services/__init__.py (100%) rename packages/datacommons-api/{src/datacommons/api => datacommons_api}/services/graph_service.py (97%) create mode 100644 packages/datacommons-api/datacommons_api/version.py rename packages/datacommons-db/{src/datacommons/db => datacommons_db}/__init__.py (100%) rename packages/datacommons-db/{src/datacommons/db => datacommons_db}/models/__init__.py (100%) rename packages/datacommons-db/{src/datacommons/db => datacommons_db}/models/base.py (100%) rename packages/datacommons-db/{src/datacommons/db => datacommons_db}/models/edge.py (97%) rename packages/datacommons-db/{src/datacommons/db => datacommons_db}/models/node.py (96%) rename packages/datacommons-db/{src/datacommons/db => datacommons_db}/models/observation.py (97%) rename packages/datacommons-db/{src/datacommons/db => datacommons_db}/repositories/__init__.py (100%) rename packages/datacommons-db/{src/datacommons/db => datacommons_db}/repositories/node_repository.py (95%) rename packages/datacommons-db/{src/datacommons/db => datacommons_db}/session.py (98%) create mode 100644 packages/datacommons-db/datacommons_db/version.py delete mode 100644 packages/datacommons-root/src/datacommons/__init__.py rename packages/datacommons-schema/{src/datacommons/schema => datacommons_schema}/__init__.py (100%) rename packages/datacommons-schema/{src/datacommons/schema => datacommons_schema}/converters/__init__.py (100%) rename packages/datacommons-schema/{src/datacommons/schema => datacommons_schema}/converters/mcf_to_jsonld.py (95%) rename packages/datacommons-schema/{src/datacommons/schema => datacommons_schema}/models/__init__.py (100%) rename packages/datacommons-schema/{src/datacommons/schema => datacommons_schema}/models/jsonld.py (100%) rename packages/datacommons-schema/{src/datacommons/schema => datacommons_schema}/models/mcf.py (100%) rename packages/datacommons-schema/{src/datacommons/schema => datacommons_schema}/parsers/__init__.py (100%) rename packages/datacommons-schema/{src/datacommons/schema => datacommons_schema}/parsers/mcf_parser.py (99%) rename packages/datacommons-schema/{src/datacommons/schema => datacommons_schema}/parsers/mcf_parser_test.py (98%) rename packages/datacommons-schema/{src/datacommons/schema => datacommons_schema}/schema_cli.py (91%) create mode 100644 packages/datacommons-schema/datacommons_schema/version.py create mode 100644 uv.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b08e5f1..bcbf7f6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,17 +1,9 @@ repos: - repo: local hooks: - # Formats code with ruff via `hatch fmt` - - id: ruff-format-hatch-settings - name: hatch-ruff - language: system - entry: hatch fmt - types: [python] - verbose: true - # Runs tests before git pushing - id: run-tests name: Run Tests - entry: hatch test + entry: uv run pytest language: system pass_filenames: false stages: [pre-push] diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/packages/datacommons-api/src/datacommons/api/__init__.py b/packages/datacommons-api/datacommons_api/__init__.py similarity index 100% rename from packages/datacommons-api/src/datacommons/api/__init__.py rename to packages/datacommons-api/datacommons_api/__init__.py diff --git a/packages/datacommons-api/src/datacommons/api/app.py b/packages/datacommons-api/datacommons_api/app.py similarity index 93% rename from packages/datacommons-api/src/datacommons/api/app.py rename to packages/datacommons-api/datacommons_api/app.py index 907c92e..c411e5b 100644 --- a/packages/datacommons-api/src/datacommons/api/app.py +++ b/packages/datacommons-api/datacommons_api/app.py @@ -14,7 +14,7 @@ from fastapi import FastAPI -from datacommons.api.endpoints.routers import node_router +from datacommons_api.endpoints.routers import node_router # FastAPI initialization app = FastAPI( diff --git a/packages/datacommons-api/src/datacommons/api/app_cli.py b/packages/datacommons-api/datacommons_api/app_cli.py similarity index 87% rename from packages/datacommons-api/src/datacommons/api/app_cli.py rename to packages/datacommons-api/datacommons_api/app_cli.py index 6b783fd..5449e23 100644 --- a/packages/datacommons-api/src/datacommons/api/app_cli.py +++ b/packages/datacommons-api/datacommons_api/app_cli.py @@ -15,10 +15,10 @@ import click import uvicorn -from datacommons.api.app import app -from datacommons.api.core.config import get_config -from datacommons.api.core.logging import get_logger, setup_logging -from datacommons.db.session import initialize_db +from datacommons_api.app import app +from datacommons_api.core.config import get_config +from datacommons_api.core.logging import get_logger, setup_logging +from datacommons_db.session import initialize_db setup_logging() logger = get_logger(__name__) diff --git a/packages/datacommons-api/src/datacommons/api/core/__init__.py b/packages/datacommons-api/datacommons_api/core/__init__.py similarity index 100% rename from packages/datacommons-api/src/datacommons/api/core/__init__.py rename to packages/datacommons-api/datacommons_api/core/__init__.py diff --git a/packages/datacommons-api/src/datacommons/api/core/config.py b/packages/datacommons-api/datacommons_api/core/config.py similarity index 97% rename from packages/datacommons-api/src/datacommons/api/core/config.py rename to packages/datacommons-api/datacommons_api/core/config.py index 2bdce50..9026efb 100644 --- a/packages/datacommons-api/src/datacommons/api/core/config.py +++ b/packages/datacommons-api/datacommons_api/core/config.py @@ -15,7 +15,7 @@ import os import sys -from datacommons.api.core.logging import get_logger +from datacommons_api.core.logging import get_logger logger = get_logger(__name__) diff --git a/packages/datacommons-api/src/datacommons/api/core/constants.py b/packages/datacommons-api/datacommons_api/core/constants.py similarity index 100% rename from packages/datacommons-api/src/datacommons/api/core/constants.py rename to packages/datacommons-api/datacommons_api/core/constants.py diff --git a/packages/datacommons-api/src/datacommons/api/core/logging.py b/packages/datacommons-api/datacommons_api/core/logging.py similarity index 96% rename from packages/datacommons-api/src/datacommons/api/core/logging.py rename to packages/datacommons-api/datacommons_api/core/logging.py index f0983cb..5e96c25 100644 --- a/packages/datacommons-api/src/datacommons/api/core/logging.py +++ b/packages/datacommons-api/datacommons_api/core/logging.py @@ -23,12 +23,12 @@ Usage: ```python -from datacommons.api.core.logging import get_logger +from datacommons_api.core.logging import get_logger logger = get_logger(__name__) logger.info("Hello, world!") -# Output: "INFO [datacommons.api.core.logging] [2021-01-01 12:00:00+0000] Hello, world!" +# Output: "INFO [datacommons_api.core.logging] [2021-01-01 12:00:00+0000] Hello, world!" ``` """ diff --git a/packages/datacommons-api/src/datacommons/api/endpoints/__init__.py b/packages/datacommons-api/datacommons_api/endpoints/__init__.py similarity index 100% rename from packages/datacommons-api/src/datacommons/api/endpoints/__init__.py rename to packages/datacommons-api/datacommons_api/endpoints/__init__.py diff --git a/packages/datacommons-api/src/datacommons/api/endpoints/dependencies.py b/packages/datacommons-api/datacommons_api/endpoints/dependencies.py similarity index 86% rename from packages/datacommons-api/src/datacommons/api/endpoints/dependencies.py rename to packages/datacommons-api/datacommons_api/endpoints/dependencies.py index bb506fc..b54ccfc 100644 --- a/packages/datacommons-api/src/datacommons/api/endpoints/dependencies.py +++ b/packages/datacommons-api/datacommons_api/endpoints/dependencies.py @@ -15,9 +15,9 @@ from collections.abc import Generator -from datacommons.api.core.config import get_config -from datacommons.api.services.graph_service import GraphService -from datacommons.db.session import get_session +from datacommons_api.core.config import get_config +from datacommons_api.services.graph_service import GraphService +from datacommons_db.session import get_session def with_graph_service() -> Generator[GraphService, None, None]: diff --git a/packages/datacommons-api/src/datacommons/api/endpoints/responses.py b/packages/datacommons-api/datacommons_api/endpoints/responses.py similarity index 100% rename from packages/datacommons-api/src/datacommons/api/endpoints/responses.py rename to packages/datacommons-api/datacommons_api/endpoints/responses.py diff --git a/packages/datacommons-api/src/datacommons/api/endpoints/routers/__init__.py b/packages/datacommons-api/datacommons_api/endpoints/routers/__init__.py similarity index 100% rename from packages/datacommons-api/src/datacommons/api/endpoints/routers/__init__.py rename to packages/datacommons-api/datacommons_api/endpoints/routers/__init__.py diff --git a/packages/datacommons-api/src/datacommons/api/endpoints/routers/node_router.py b/packages/datacommons-api/datacommons_api/endpoints/routers/node_router.py similarity index 83% rename from packages/datacommons-api/src/datacommons/api/endpoints/routers/node_router.py rename to packages/datacommons-api/datacommons_api/endpoints/routers/node_router.py index d14f682..2a01ed7 100644 --- a/packages/datacommons-api/src/datacommons/api/endpoints/routers/node_router.py +++ b/packages/datacommons-api/datacommons_api/endpoints/routers/node_router.py @@ -16,12 +16,12 @@ from fastapi import APIRouter, Depends, Query -from datacommons.api.core.constants import DEFAULT_NODE_FETCH_LIMIT -from datacommons.api.core.logging import get_logger -from datacommons.api.endpoints.dependencies import with_graph_service -from datacommons.api.endpoints.responses import UpdateResponse -from datacommons.api.services.graph_service import GraphService -from datacommons.schema.models.jsonld import JSONLDDocument +from datacommons_api.core.constants import DEFAULT_NODE_FETCH_LIMIT +from datacommons_api.core.logging import get_logger +from datacommons_api.endpoints.dependencies import with_graph_service +from datacommons_api.endpoints.responses import UpdateResponse +from datacommons_api.services.graph_service import GraphService +from datacommons_schema.models.jsonld import JSONLDDocument logger = get_logger(__name__) diff --git a/packages/datacommons-api/src/datacommons/api/services/__init__.py b/packages/datacommons-api/datacommons_api/services/__init__.py similarity index 100% rename from packages/datacommons-api/src/datacommons/api/services/__init__.py rename to packages/datacommons-api/datacommons_api/services/__init__.py diff --git a/packages/datacommons-api/src/datacommons/api/services/graph_service.py b/packages/datacommons-api/datacommons_api/services/graph_service.py similarity index 97% rename from packages/datacommons-api/src/datacommons/api/services/graph_service.py rename to packages/datacommons-api/datacommons_api/services/graph_service.py index 7279fb9..f0e8ef6 100644 --- a/packages/datacommons-api/src/datacommons/api/services/graph_service.py +++ b/packages/datacommons-api/datacommons_api/services/graph_service.py @@ -19,12 +19,12 @@ from sqlalchemy.orm import Session, joinedload # Third-party imports -from datacommons.api.core.constants import DEFAULT_NODE_FETCH_LIMIT -from datacommons.db.models.edge import EdgeModel +from datacommons_api.core.constants import DEFAULT_NODE_FETCH_LIMIT +from datacommons_db.models.edge import EdgeModel # Local application imports -from datacommons.db.models.node import NodeModel -from datacommons.schema.models.jsonld import GraphNode, GraphNodePropertyValue, JSONLDDocument +from datacommons_db.models.node import NodeModel +from datacommons_schema.models.jsonld import GraphNode, GraphNodePropertyValue, JSONLDDocument # Configure logging logger = logging.getLogger(__name__) diff --git a/packages/datacommons-api/datacommons_api/version.py b/packages/datacommons-api/datacommons_api/version.py new file mode 100644 index 0000000..b3c06d4 --- /dev/null +++ b/packages/datacommons-api/datacommons_api/version.py @@ -0,0 +1 @@ +__version__ = "0.0.1" \ No newline at end of file diff --git a/packages/datacommons-api/pyproject.toml b/packages/datacommons-api/pyproject.toml index bb6f202..70e85a8 100644 --- a/packages/datacommons-api/pyproject.toml +++ b/packages/datacommons-api/pyproject.toml @@ -1,55 +1,31 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - [project] name = "datacommons-api" dynamic = ["version"] description = 'Data Commons API' +license = "Apache-2.0" readme = "README.md" -requires-python = ">=3.11" -license = "MIT" +requires-python = ">=3.12,<3.13" keywords = [] -authors = [ - { name = "Dan Noble", email = "dwnoble@google.com" }, -] -classifiers = [ - "Development Status :: 4 - Beta", - "Programming Language :: Python", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", -] +authors = [] dependencies = [ "fastapi>=0.95.0", "uvicorn[standard]>=0.22.0", "sqlalchemy>=2.0.0", "pydantic>=2.0.0", - "datacommons-db @ {root:uri}/../datacommons-db", - "datacommons-schema @ {root:uri}/../datacommons-schema", + "datacommons-db", + "datacommons-schema", ] - -[project.urls] -Documentation = "https://github.com/datacommonsorg/datacommons#readme" -Issues = "https://github.com/datacommonsorg/datacommons/issues" -Source = "https://github.com/datacommonsorg/datacommons" +urls = {Homepage = "https://github.com/datacommonsorg/datacommons"} [project.scripts] -datacommons-api = "datacommons.api.app_cli:main" - -[tool.hatch.build.targets.wheel] -packages = ["src/datacommons"] +datacommons-api = "datacommons_api.app_cli:main" -[tool.hatch.metadata] -allow-direct-references = true +[build-system] +requires = ["uv", "setuptools"] +build-backend = "setuptools.build_meta" -[tool.hatch.version] -path = "src/datacommons/api/__init__.py" +[tool.setuptools] +include-package-data = true -# Install dependencies in editable mode for local development -[tool.hatch.envs.default] -post-install-commands = [ - "pip install -e ../datacommons-db", - "pip install -e ../datacommons-schema", -] +[tool.setuptools.dynamic] +version = {attr = "datacommons_api.version.__version__"} diff --git a/packages/datacommons-db/README.md b/packages/datacommons-db/README.md index 0c5f57e..e427a0f 100644 --- a/packages/datacommons-db/README.md +++ b/packages/datacommons-db/README.md @@ -47,8 +47,8 @@ This module provides the database models for the Data Commons project, implement ```python from sqlalchemy import create_engine -from datacommons.db.models.node import NodeModel -from datacommons.db.models.edge import EdgeModel +from datacommons_db.models.node import NodeModel +from datacommons_db.models.edge import EdgeModel # Initialize database connection engine = create_engine('spanner:///projects/your-project/instances/your-instance/databases/your-database') diff --git a/packages/datacommons-db/src/datacommons/db/__init__.py b/packages/datacommons-db/datacommons_db/__init__.py similarity index 100% rename from packages/datacommons-db/src/datacommons/db/__init__.py rename to packages/datacommons-db/datacommons_db/__init__.py diff --git a/packages/datacommons-db/src/datacommons/db/models/__init__.py b/packages/datacommons-db/datacommons_db/models/__init__.py similarity index 100% rename from packages/datacommons-db/src/datacommons/db/models/__init__.py rename to packages/datacommons-db/datacommons_db/models/__init__.py diff --git a/packages/datacommons-db/src/datacommons/db/models/base.py b/packages/datacommons-db/datacommons_db/models/base.py similarity index 100% rename from packages/datacommons-db/src/datacommons/db/models/base.py rename to packages/datacommons-db/datacommons_db/models/base.py diff --git a/packages/datacommons-db/src/datacommons/db/models/edge.py b/packages/datacommons-db/datacommons_db/models/edge.py similarity index 97% rename from packages/datacommons-db/src/datacommons/db/models/edge.py rename to packages/datacommons-db/datacommons_db/models/edge.py index c7ac3fe..ac32c68 100644 --- a/packages/datacommons-db/src/datacommons/db/models/edge.py +++ b/packages/datacommons-db/datacommons_db/models/edge.py @@ -17,7 +17,7 @@ from sqlalchemy.orm import deferred, relationship from sqlalchemy.types import String, Text -from datacommons.db.models.base import Base +from datacommons_db.models.base import Base class EdgeModel(Base): diff --git a/packages/datacommons-db/src/datacommons/db/models/node.py b/packages/datacommons-db/datacommons_db/models/node.py similarity index 96% rename from packages/datacommons-db/src/datacommons/db/models/node.py rename to packages/datacommons-db/datacommons_db/models/node.py index 4861cd5..053367d 100644 --- a/packages/datacommons-db/src/datacommons/db/models/node.py +++ b/packages/datacommons-db/datacommons_db/models/node.py @@ -17,7 +17,7 @@ from sqlalchemy.orm import relationship from sqlalchemy.types import ARRAY, String, Text -from datacommons.db.models.base import Base +from datacommons_db.models.base import Base class NodeModel(Base): diff --git a/packages/datacommons-db/src/datacommons/db/models/observation.py b/packages/datacommons-db/datacommons_db/models/observation.py similarity index 97% rename from packages/datacommons-db/src/datacommons/db/models/observation.py rename to packages/datacommons-db/datacommons_db/models/observation.py index 1c5455a..9bfec2f 100644 --- a/packages/datacommons-db/src/datacommons/db/models/observation.py +++ b/packages/datacommons-db/datacommons_db/models/observation.py @@ -16,7 +16,7 @@ import sqlalchemy as sa from sqlalchemy.types import String -from datacommons.db.models.base import Base +from datacommons_db.models.base import Base class ObservationModel(Base): diff --git a/packages/datacommons-db/src/datacommons/db/repositories/__init__.py b/packages/datacommons-db/datacommons_db/repositories/__init__.py similarity index 100% rename from packages/datacommons-db/src/datacommons/db/repositories/__init__.py rename to packages/datacommons-db/datacommons_db/repositories/__init__.py diff --git a/packages/datacommons-db/src/datacommons/db/repositories/node_repository.py b/packages/datacommons-db/datacommons_db/repositories/node_repository.py similarity index 95% rename from packages/datacommons-db/src/datacommons/db/repositories/node_repository.py rename to packages/datacommons-db/datacommons_db/repositories/node_repository.py index ee67bdc..9c15490 100644 --- a/packages/datacommons-db/src/datacommons/db/repositories/node_repository.py +++ b/packages/datacommons-db/datacommons_db/repositories/node_repository.py @@ -14,7 +14,7 @@ from sqlalchemy.orm import Session -from datacommons.db.models.node import NodeModel +from datacommons_db.models.node import NodeModel class NodeRepository: diff --git a/packages/datacommons-db/src/datacommons/db/session.py b/packages/datacommons-db/datacommons_db/session.py similarity index 98% rename from packages/datacommons-db/src/datacommons/db/session.py rename to packages/datacommons-db/datacommons_db/session.py index 51f384a..810b0bc 100644 --- a/packages/datacommons-db/src/datacommons/db/session.py +++ b/packages/datacommons-db/datacommons_db/session.py @@ -17,7 +17,7 @@ from sqlalchemy import Engine, create_engine, inspect from sqlalchemy.orm import Session, sessionmaker -from datacommons.db.models.base import Base +from datacommons_db.models.base import Base logger = logging.getLogger(__name__) diff --git a/packages/datacommons-db/datacommons_db/version.py b/packages/datacommons-db/datacommons_db/version.py new file mode 100644 index 0000000..b3c06d4 --- /dev/null +++ b/packages/datacommons-db/datacommons_db/version.py @@ -0,0 +1 @@ +__version__ = "0.0.1" \ No newline at end of file diff --git a/packages/datacommons-db/pyproject.toml b/packages/datacommons-db/pyproject.toml index 9d46249..935d789 100644 --- a/packages/datacommons-db/pyproject.toml +++ b/packages/datacommons-db/pyproject.toml @@ -1,18 +1,12 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - [project] name = "datacommons-db" dynamic = ["version"] description = 'Data Commons DB' +license = "Apache-2.0" readme = "README.md" -requires-python = ">=3.11" -license = "MIT" +requires-python = ">=3.12,<3.13" keywords = [] -authors = [ - { name = "Dan Noble", email = "dwnoble@google.com" }, -] +authors = [] classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python", @@ -32,12 +26,9 @@ Documentation = "https://github.com/datacommonsorg/datacommons#readme" Issues = "https://github.com/datacommonsorg/datacommons/issues" Source = "https://github.com/datacommonsorg/datacommons" -[tool.hatch.build.targets.wheel] -packages = ["src/datacommons"] - -[tool.hatch.metadata] -allow-direct-references = true - -[tool.hatch.version] -path = "src/datacommons/db/__init__.py" +[build-system] +requires = ["uv", "setuptools"] +build-backend = "setuptools.build_meta" +[tool.setuptools.dynamic] +version = {attr = "datacommons_db.version.__version__"} diff --git a/packages/datacommons-root/src/datacommons/__init__.py b/packages/datacommons-root/src/datacommons/__init__.py deleted file mode 100644 index 9f7a875..0000000 --- a/packages/datacommons-root/src/datacommons/__init__.py +++ /dev/null @@ -1 +0,0 @@ -version = "0.1.0" diff --git a/packages/datacommons-schema/README.md b/packages/datacommons-schema/README.md index 5473a5a..9bce043 100644 --- a/packages/datacommons-schema/README.md +++ b/packages/datacommons-schema/README.md @@ -60,8 +60,8 @@ The module defines data models for: ### Python API ```python -from datacommons.schema.parsers.mcf_parser import parse_mcf_string -from datacommons.schema.converters.mcf_to_jsonld import mcf_nodes_to_jsonld +from datacommons_schema.parsers.mcf_parser import parse_mcf_string +from datacommons_schema.converters.mcf_to_jsonld import mcf_nodes_to_jsonld # Parse MCF content mcf_nodes = parse_mcf_string(mcf_content) diff --git a/packages/datacommons-schema/src/datacommons/schema/__init__.py b/packages/datacommons-schema/datacommons_schema/__init__.py similarity index 100% rename from packages/datacommons-schema/src/datacommons/schema/__init__.py rename to packages/datacommons-schema/datacommons_schema/__init__.py diff --git a/packages/datacommons-schema/src/datacommons/schema/converters/__init__.py b/packages/datacommons-schema/datacommons_schema/converters/__init__.py similarity index 100% rename from packages/datacommons-schema/src/datacommons/schema/converters/__init__.py rename to packages/datacommons-schema/datacommons_schema/converters/__init__.py diff --git a/packages/datacommons-schema/src/datacommons/schema/converters/mcf_to_jsonld.py b/packages/datacommons-schema/datacommons_schema/converters/mcf_to_jsonld.py similarity index 95% rename from packages/datacommons-schema/src/datacommons/schema/converters/mcf_to_jsonld.py rename to packages/datacommons-schema/datacommons_schema/converters/mcf_to_jsonld.py index 6458432..b5cf0e6 100644 --- a/packages/datacommons-schema/src/datacommons/schema/converters/mcf_to_jsonld.py +++ b/packages/datacommons-schema/datacommons_schema/converters/mcf_to_jsonld.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from datacommons.schema.models.jsonld import GraphNode, JSONLDDocument -from datacommons.schema.models.mcf import McfNode +from datacommons_schema.models.jsonld import GraphNode, JSONLDDocument +from datacommons_schema.models.mcf import McfNode def mcf_node_to_jsonld(node: McfNode, *, compact: bool = False) -> GraphNode: diff --git a/packages/datacommons-schema/src/datacommons/schema/models/__init__.py b/packages/datacommons-schema/datacommons_schema/models/__init__.py similarity index 100% rename from packages/datacommons-schema/src/datacommons/schema/models/__init__.py rename to packages/datacommons-schema/datacommons_schema/models/__init__.py diff --git a/packages/datacommons-schema/src/datacommons/schema/models/jsonld.py b/packages/datacommons-schema/datacommons_schema/models/jsonld.py similarity index 100% rename from packages/datacommons-schema/src/datacommons/schema/models/jsonld.py rename to packages/datacommons-schema/datacommons_schema/models/jsonld.py diff --git a/packages/datacommons-schema/src/datacommons/schema/models/mcf.py b/packages/datacommons-schema/datacommons_schema/models/mcf.py similarity index 100% rename from packages/datacommons-schema/src/datacommons/schema/models/mcf.py rename to packages/datacommons-schema/datacommons_schema/models/mcf.py diff --git a/packages/datacommons-schema/src/datacommons/schema/parsers/__init__.py b/packages/datacommons-schema/datacommons_schema/parsers/__init__.py similarity index 100% rename from packages/datacommons-schema/src/datacommons/schema/parsers/__init__.py rename to packages/datacommons-schema/datacommons_schema/parsers/__init__.py diff --git a/packages/datacommons-schema/src/datacommons/schema/parsers/mcf_parser.py b/packages/datacommons-schema/datacommons_schema/parsers/mcf_parser.py similarity index 99% rename from packages/datacommons-schema/src/datacommons/schema/parsers/mcf_parser.py rename to packages/datacommons-schema/datacommons_schema/parsers/mcf_parser.py index 751b9c1..c4dca17 100644 --- a/packages/datacommons-schema/src/datacommons/schema/parsers/mcf_parser.py +++ b/packages/datacommons-schema/datacommons_schema/parsers/mcf_parser.py @@ -16,7 +16,7 @@ from io import StringIO from typing import IO -from datacommons.schema.models.mcf import McfNode +from datacommons_schema.models.mcf import McfNode class MCFParseError(Exception): diff --git a/packages/datacommons-schema/src/datacommons/schema/parsers/mcf_parser_test.py b/packages/datacommons-schema/datacommons_schema/parsers/mcf_parser_test.py similarity index 98% rename from packages/datacommons-schema/src/datacommons/schema/parsers/mcf_parser_test.py rename to packages/datacommons-schema/datacommons_schema/parsers/mcf_parser_test.py index a75f8a9..fdb6a36 100644 --- a/packages/datacommons-schema/src/datacommons/schema/parsers/mcf_parser_test.py +++ b/packages/datacommons-schema/datacommons_schema/parsers/mcf_parser_test.py @@ -17,7 +17,7 @@ import pytest -from datacommons.schema.parsers.mcf_parser import MCFParseError, parse_mcf, parse_mcf_string +from datacommons_schema.parsers.mcf_parser import MCFParseError, parse_mcf, parse_mcf_string # Test cases for loading mcf diff --git a/packages/datacommons-schema/src/datacommons/schema/schema_cli.py b/packages/datacommons-schema/datacommons_schema/schema_cli.py similarity index 91% rename from packages/datacommons-schema/src/datacommons/schema/schema_cli.py rename to packages/datacommons-schema/datacommons_schema/schema_cli.py index 049b91f..5176b1a 100644 --- a/packages/datacommons-schema/src/datacommons/schema/schema_cli.py +++ b/packages/datacommons-schema/datacommons_schema/schema_cli.py @@ -2,8 +2,8 @@ import click -from datacommons.schema.converters.mcf_to_jsonld import mcf_nodes_to_jsonld -from datacommons.schema.parsers.mcf_parser import parse_mcf_string +from datacommons_schema.converters.mcf_to_jsonld import mcf_nodes_to_jsonld +from datacommons_schema.parsers.mcf_parser import parse_mcf_string @click.group() diff --git a/packages/datacommons-schema/datacommons_schema/version.py b/packages/datacommons-schema/datacommons_schema/version.py new file mode 100644 index 0000000..b3c06d4 --- /dev/null +++ b/packages/datacommons-schema/datacommons_schema/version.py @@ -0,0 +1 @@ +__version__ = "0.0.1" \ No newline at end of file diff --git a/packages/datacommons-schema/pyproject.toml b/packages/datacommons-schema/pyproject.toml index bf03e43..02625ce 100644 --- a/packages/datacommons-schema/pyproject.toml +++ b/packages/datacommons-schema/pyproject.toml @@ -1,18 +1,12 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - [project] name = "datacommons-schema" dynamic = ["version"] description = 'Data Commons Schema' +license = "Apache-2.0" readme = "README.md" -requires-python = ">=3.11" -license = "MIT" +requires-python = ">=3.12,<3.13" keywords = [] -authors = [ - { name = "Dan Noble", email = "dwnoble@google.com" }, -] +authors = [] classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python", @@ -33,19 +27,11 @@ Issues = "https://github.com/datacommonsorg/datacommons/issues" Source = "https://github.com/datacommonsorg/datacommons" [project.scripts] -datacommons-schema = "datacommons.schema.schema_cli:cli" +datacommons-schema = "datacommons_schema.schema_cli:cli" -[tool.hatch.build.targets.wheel] -packages = ["src/datacommons"] - -[tool.hatch.metadata] -allow-direct-references = true - -[tool.hatch.version] -path = "src/datacommons/schema/__init__.py" +[build-system] +requires = ["uv", "setuptools"] +build-backend = "setuptools.build_meta" -[tool.ruff.lint.per-file-ignores] -# For any file in a "tests" directory or ending in "_test.py", -# ignore the S101 (assert) and a few other common test-only rules. -"**/tests/*" = ["S101", "PLR2004"] -"*_test.py" = ["S101", "PLR2004"] \ No newline at end of file +[tool.setuptools.dynamic] +version = {attr = "datacommons_schema.version.__version__"} diff --git a/pyproject.toml b/pyproject.toml index ddf8f22..4d40588 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,27 +1,29 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - [project] -name = "datacommons" -dynamic = ["version"] -description = 'Data Commons is an open source semantic graph database for modeling, querying, and analyzing interconnected data.' +name = "datacommons-platform" +version = "0.0.1" +description = 'The Data Commons Platform is an open source semantic graph database for modeling, querying, and analyzing interconnected data.' readme = "README.md" -requires-python = ">=3.11" -license = "MIT" +requires-python = ">=3.11,<3.13" +license = "Apache-2.0" +license-files = ["LICENSE"] keywords = [] authors = [] classifiers = [ - "Development Status :: 4 - Beta", + "Development Status :: 3 - Alpha", "Programming Language :: Python", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ] dependencies = [ - "datacommons-api @ {root:uri}/datacommons/datacommons-api", + "datacommons-api", "pre-commit", + "ruff>=0.12.5", +] + +[project.optional-dependencies] +test = [ + "pytest>=8.4.1", "pytest-cov>=4.1.0" ] @@ -30,28 +32,13 @@ Documentation = "https://github.com/datacommonsorg/datacommons#readme" Issues = "https://github.com/datacommonsorg/datacommons/issues" Source = "https://github.com/datacommonsorg/datacommons" -[tool.hatch.build.targets.wheel] -packages = ["packages/datacommons-root"] - -[tool.hatch.metadata] -allow-direct-references = true - -[tool.hatch.version] -path = "packages/datacommons-root/src/datacommons/__init__.py" - +[tool.uv.sources] +datacommons-api = { workspace = true } +datacommons-db = { workspace = true } +datacommons-schema = { workspace = true } -# Install dependencies in editable mode for local development -[tool.hatch.envs.default] -post-install-commands = [ - "pip install -e ./packages/datacommons-api", - "pip install -e ./packages/datacommons-db", - "pip install -e ./packages/datacommons-schema", - "pre-commit install", - "pre-commit install --hook-type pre-push" -] - -[tool.hatch.envs.hatch-test] -default-args = [] +[tool.uv.workspace] +members = ["packages/*"] [tool.pytest.ini_options] addopts = "--cov=datacommons --cov-report=term --cov-report=html" @@ -72,8 +59,56 @@ exclude_lines = [ "@abstractmethod", ] +[tool.ruff.lint] +# Visit https://docs.astral.sh/ruff/rules/ for detailed rule descriptions. +extend-select = [ + "ERA", # removes commented-out code from Python files. + "FAST", # linting FastAPI specifics + "YTT", # Flake8 checks for misuse of `sys.version` or `sys.version_info` + "ANN", # Flake8 Type Annotation Checks + "S", # Automated security testing with bandit and flake8. + "BLE", # A flake8 extension that checks for blind except: statements + "FBT", # A flake8 plugin to detect boolean traps. + "B", # flake8-bugbear finds likely bugs and design problems. + "A", # flake8-builtins detects shadowed python builtins + "COM", # flake8 lint for trailing commas. + "C4", # flake8 plugin to help write better list/set/dict comprehensions + "ISC", # flake8 plugin to encourage correct string literal concatenation + "LOG", # flake8 plugin checks for issues with standard library logging module + "G", # flake8 plugin to encourage better formatting of built-in logging strings + "PIE", # flake8 misc lints + "PT", # flake8 plugin checks common pitfalls with pytest-based tests + "RET", # encourages good return practices + "SIM", # flake8-simplify checks common simplification patterns + "TID", # flake8-tidy-imports encourages tidier imports + "TD", # flake8-todos lints TODOs throughout the project for consistency + "TC", # flake8 plugin for managing type-checking imports & forward references + "ARG", # flake8-unused-arguments warns on unused function arguments + "PTH", # flake8-use-pathlib encourages pathlib over os.path + "I", # isort sorts Python imports + "N", # check PEP-8 naming conventions + "PERF", # Pylint extension with performance anti-patterns + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes analyzes and detects various errors + "PL", # pylint + "Q", # defaults to double quotes + "UP", # Encourage syntax supported by newer python versions + # "CPY", # check for copyright headers # uncomment once graduated from preview +] +extend-ignore = [ + "TD003", # missing-todo-link: Missing issue link for this TODO + "PLC", # pylint conventions + "PLR", # pylint refactor + "COM812", # missing-trailing-comma may conflict with formatter + "E501", # line-too-long +] + [tool.ruff.lint.per-file-ignores] -# For any file in a "tests" directory or ending in "_test.py", -# ignore the S101 (assert) and a few other common test-only rules. -"**/tests/*" = ["S101", "PLR2004"] -"*_test.py" = ["S101", "PLR2004"] \ No newline at end of file +"packages/*/tests/**/*.py" = [ + "S101", # Allow using `assert` in pytests + "ANN001", # Ignore missing type annotations in test files + "ANN201", # Ignore missing return types in public test functions + "ANN202", # Ignore missing return types in private test functions + "PLR2004" +] diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..80d2ed0 --- /dev/null +++ b/uv.lock @@ -0,0 +1,945 @@ +version = 1 +revision = 2 +requires-python = "==3.12.*" + +[manifest] +members = [ + "datacommons-api", + "datacommons-db", + "datacommons-platform", + "datacommons-schema", +] + +[[package]] +name = "alembic" +version = "1.16.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mako" }, + { name = "sqlalchemy" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/52/72e791b75c6b1efa803e491f7cbab78e963695e76d4ada05385252927e76/alembic-1.16.4.tar.gz", hash = "sha256:efab6ada0dd0fae2c92060800e0bf5c1dc26af15a10e02fb4babff164b4725e2", size = 1968161, upload-time = "2025-07-10T16:17:20.192Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/62/96b5217b742805236614f05904541000f55422a6060a90d7fd4ce26c172d/alembic-1.16.4-py3-none-any.whl", hash = "sha256:b05e51e8e82efc1abd14ba2af6392897e145930c3e0a2faf2b0da2f7f7fd660d", size = 247026, upload-time = "2025-07-10T16:17:21.845Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, +] + +[[package]] +name = "cachetools" +version = "5.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, +] + +[[package]] +name = "certifi" +version = "2025.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" }, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" }, + { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" }, + { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" }, + { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" }, + { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" }, + { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" }, + { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" }, + { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" }, + { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" }, + { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" }, + { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.10.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/2c/253cc41cd0f40b84c1c34c5363e0407d73d4a1cae005fed6db3b823175bd/coverage-7.10.3.tar.gz", hash = "sha256:812ba9250532e4a823b070b0420a36499859542335af3dca8f47fc6aa1a05619", size = 822936, upload-time = "2025-08-10T21:27:39.968Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/62/13c0b66e966c43d7aa64dadc8cd2afa1f5a2bf9bb863bdabc21fb94e8b63/coverage-7.10.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:449c1e2d3a84d18bd204258a897a87bc57380072eb2aded6a5b5226046207b42", size = 216262, upload-time = "2025-08-10T21:25:55.367Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f0/59fdf79be7ac2f0206fc739032f482cfd3f66b18f5248108ff192741beae/coverage-7.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d4f9ce50b9261ad196dc2b2e9f1fbbee21651b54c3097a25ad783679fd18294", size = 216496, upload-time = "2025-08-10T21:25:56.759Z" }, + { url = "https://files.pythonhosted.org/packages/34/b1/bc83788ba31bde6a0c02eb96bbc14b2d1eb083ee073beda18753fa2c4c66/coverage-7.10.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4dd4564207b160d0d45c36a10bc0a3d12563028e8b48cd6459ea322302a156d7", size = 247989, upload-time = "2025-08-10T21:25:58.067Z" }, + { url = "https://files.pythonhosted.org/packages/0c/29/f8bdf88357956c844bd872e87cb16748a37234f7f48c721dc7e981145eb7/coverage-7.10.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5ca3c9530ee072b7cb6a6ea7b640bcdff0ad3b334ae9687e521e59f79b1d0437", size = 250738, upload-time = "2025-08-10T21:25:59.406Z" }, + { url = "https://files.pythonhosted.org/packages/ae/df/6396301d332b71e42bbe624670af9376f63f73a455cc24723656afa95796/coverage-7.10.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b6df359e59fa243c9925ae6507e27f29c46698359f45e568fd51b9315dbbe587", size = 251868, upload-time = "2025-08-10T21:26:00.65Z" }, + { url = "https://files.pythonhosted.org/packages/91/21/d760b2df6139b6ef62c9cc03afb9bcdf7d6e36ed4d078baacffa618b4c1c/coverage-7.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a181e4c2c896c2ff64c6312db3bda38e9ade2e1aa67f86a5628ae85873786cea", size = 249790, upload-time = "2025-08-10T21:26:02.009Z" }, + { url = "https://files.pythonhosted.org/packages/69/91/5dcaa134568202397fa4023d7066d4318dc852b53b428052cd914faa05e1/coverage-7.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a374d4e923814e8b72b205ef6b3d3a647bb50e66f3558582eda074c976923613", size = 247907, upload-time = "2025-08-10T21:26:03.757Z" }, + { url = "https://files.pythonhosted.org/packages/38/ed/70c0e871cdfef75f27faceada461206c1cc2510c151e1ef8d60a6fedda39/coverage-7.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:daeefff05993e5e8c6e7499a8508e7bd94502b6b9a9159c84fd1fe6bce3151cb", size = 249344, upload-time = "2025-08-10T21:26:05.11Z" }, + { url = "https://files.pythonhosted.org/packages/5f/55/c8a273ed503cedc07f8a00dcd843daf28e849f0972e4c6be4c027f418ad6/coverage-7.10.3-cp312-cp312-win32.whl", hash = "sha256:187ecdcac21f9636d570e419773df7bd2fda2e7fa040f812e7f95d0bddf5f79a", size = 218693, upload-time = "2025-08-10T21:26:06.534Z" }, + { url = "https://files.pythonhosted.org/packages/94/58/dd3cfb2473b85be0b6eb8c5b6d80b6fc3f8f23611e69ef745cef8cf8bad5/coverage-7.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:4a50ad2524ee7e4c2a95e60d2b0b83283bdfc745fe82359d567e4f15d3823eb5", size = 219501, upload-time = "2025-08-10T21:26:08.195Z" }, + { url = "https://files.pythonhosted.org/packages/56/af/7cbcbf23d46de6f24246e3f76b30df099d05636b30c53c158a196f7da3ad/coverage-7.10.3-cp312-cp312-win_arm64.whl", hash = "sha256:c112f04e075d3495fa3ed2200f71317da99608cbb2e9345bdb6de8819fc30571", size = 218135, upload-time = "2025-08-10T21:26:09.584Z" }, + { url = "https://files.pythonhosted.org/packages/84/19/e67f4ae24e232c7f713337f3f4f7c9c58afd0c02866fb07c7b9255a19ed7/coverage-7.10.3-py3-none-any.whl", hash = "sha256:416a8d74dc0adfd33944ba2f405897bab87b7e9e84a391e09d241956bd953ce1", size = 207921, upload-time = "2025-08-10T21:27:38.254Z" }, +] + +[[package]] +name = "datacommons-api" +source = { editable = "packages/datacommons-api" } +dependencies = [ + { name = "datacommons-db" }, + { name = "datacommons-schema" }, + { name = "fastapi" }, + { name = "pydantic" }, + { name = "sqlalchemy" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[package.metadata] +requires-dist = [ + { name = "datacommons-db", editable = "packages/datacommons-db" }, + { name = "datacommons-schema", editable = "packages/datacommons-schema" }, + { name = "fastapi", specifier = ">=0.95.0" }, + { name = "pydantic", specifier = ">=2.0.0" }, + { name = "sqlalchemy", specifier = ">=2.0.0" }, + { name = "uvicorn", extras = ["standard"], specifier = ">=0.22.0" }, +] + +[[package]] +name = "datacommons-db" +source = { editable = "packages/datacommons-db" } +dependencies = [ + { name = "setuptools" }, + { name = "sqlalchemy" }, + { name = "sqlalchemy-spanner" }, +] + +[package.metadata] +requires-dist = [ + { name = "setuptools", specifier = "<=80.0.0" }, + { name = "sqlalchemy" }, + { name = "sqlalchemy-spanner" }, +] + +[[package]] +name = "datacommons-platform" +version = "0.0.1" +source = { virtual = "." } +dependencies = [ + { name = "datacommons-api" }, + { name = "pre-commit" }, + { name = "ruff" }, +] + +[package.optional-dependencies] +test = [ + { name = "pytest" }, + { name = "pytest-cov" }, +] + +[package.metadata] +requires-dist = [ + { name = "datacommons-api", editable = "packages/datacommons-api" }, + { name = "pre-commit" }, + { name = "pytest", marker = "extra == 'test'", specifier = ">=8.4.1" }, + { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=4.1.0" }, + { name = "ruff", specifier = ">=0.12.5" }, +] +provides-extras = ["test"] + +[[package]] +name = "datacommons-schema" +source = { editable = "packages/datacommons-schema" } +dependencies = [ + { name = "click" }, + { name = "pydantic" }, + { name = "pytest" }, +] + +[package.metadata] +requires-dist = [ + { name = "click" }, + { name = "pydantic" }, + { name = "pytest" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "fastapi" +version = "0.116.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/d7/6c8b3bfe33eeffa208183ec037fee0cce9f7f024089ab1c5d12ef04bd27c/fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143", size = 296485, upload-time = "2025-07-11T16:22:32.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631, upload-time = "2025-07-11T16:22:30.485Z" }, +] + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, +] + +[[package]] +name = "google-api-core" +version = "2.25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "googleapis-common-protos" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/21/e9d043e88222317afdbdb567165fdbc3b0aad90064c7e0c9eb0ad9955ad8/google_api_core-2.25.1.tar.gz", hash = "sha256:d2aaa0b13c78c61cb3f4282c464c046e45fbd75755683c9c525e6e8f7ed0a5e8", size = 165443, upload-time = "2025-06-12T20:52:20.439Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/4b/ead00905132820b623732b175d66354e9d3e69fcf2a5dcdab780664e7896/google_api_core-2.25.1-py3-none-any.whl", hash = "sha256:8a2a56c1fef82987a524371f99f3bd0143702fecc670c72e600c1cda6bf8dbb7", size = 160807, upload-time = "2025-06-12T20:52:19.334Z" }, +] + +[package.optional-dependencies] +grpc = [ + { name = "grpcio" }, + { name = "grpcio-status" }, +] + +[[package]] +name = "google-auth" +version = "2.40.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/9b/e92ef23b84fa10a64ce4831390b7a4c2e53c0132568d99d4ae61d04c8855/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77", size = 281029, upload-time = "2025-06-04T18:04:57.577Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca", size = 216137, upload-time = "2025-06-04T18:04:55.573Z" }, +] + +[[package]] +name = "google-cloud-core" +version = "2.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/b8/2b53838d2acd6ec6168fd284a990c76695e84c65deee79c9f3a4276f6b4f/google_cloud_core-2.4.3.tar.gz", hash = "sha256:1fab62d7102844b278fe6dead3af32408b1df3eb06f5c7e8634cbd40edc4da53", size = 35861, upload-time = "2025-03-10T21:05:38.948Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/86/bda7241a8da2d28a754aad2ba0f6776e35b67e37c36ae0c45d49370f1014/google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e", size = 29348, upload-time = "2025-03-10T21:05:37.785Z" }, +] + +[[package]] +name = "google-cloud-spanner" +version = "3.56.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core", extra = ["grpc"] }, + { name = "google-cloud-core" }, + { name = "grpc-google-iam-v1" }, + { name = "grpc-interceptor" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "sqlparse" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/67/33c120f21462173846b202c84bb9979f12ff8227cc6de4e0957c37fd9a60/google_cloud_spanner-3.56.0.tar.gz", hash = "sha256:bf7e4359d2f2148eda18a11f909813d07e794347a02f56dfbbd544418d30e5b2", size = 701321, upload-time = "2025-07-24T11:12:56.461Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/e9/60ac93f633e3ed78d399d3f0e2117c4e4e751a2ac8c87ae994d86cae7a2b/google_cloud_spanner-3.56.0-py3-none-any.whl", hash = "sha256:64f732a44f6a2892b5cc3be88e6e2cc92b5e52db883d1e946e1e3ff93c9745f0", size = 501309, upload-time = "2025-07-24T11:12:54.892Z" }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.70.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903, upload-time = "2025-04-14T10:17:02.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530, upload-time = "2025-04-14T10:17:01.271Z" }, +] + +[package.optional-dependencies] +grpc = [ + { name = "grpcio" }, +] + +[[package]] +name = "greenlet" +version = "3.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" }, + { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" }, + { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" }, + { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" }, + { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" }, + { url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142, upload-time = "2025-08-07T13:18:22.981Z" }, + { url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899, upload-time = "2025-08-07T13:38:53.448Z" }, +] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.14.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos", extra = ["grpc"] }, + { name = "grpcio" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/4e/8d0ca3b035e41fe0b3f31ebbb638356af720335e5a11154c330169b40777/grpc_google_iam_v1-0.14.2.tar.gz", hash = "sha256:b3e1fc387a1a329e41672197d0ace9de22c78dd7d215048c4c78712073f7bd20", size = 16259, upload-time = "2025-03-17T11:40:23.586Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/6f/dd9b178aee7835b96c2e63715aba6516a9d50f6bebbd1cc1d32c82a2a6c3/grpc_google_iam_v1-0.14.2-py3-none-any.whl", hash = "sha256:a3171468459770907926d56a440b2bb643eec1d7ba215f48f3ecece42b4d8351", size = 19242, upload-time = "2025-03-17T11:40:22.648Z" }, +] + +[[package]] +name = "grpc-interceptor" +version = "0.15.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "grpcio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/28/57449d5567adf4c1d3e216aaca545913fbc21a915f2da6790d6734aac76e/grpc-interceptor-0.15.4.tar.gz", hash = "sha256:1f45c0bcb58b6f332f37c637632247c9b02bc6af0fdceb7ba7ce8d2ebbfb0926", size = 19322, upload-time = "2023-11-16T02:05:42.459Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/ac/8d53f230a7443401ce81791ec50a3b0e54924bf615ad287654fa4a2f5cdc/grpc_interceptor-0.15.4-py3-none-any.whl", hash = "sha256:0035f33228693ed3767ee49d937bac424318db173fef4d2d0170b3215f254d9d", size = 20848, upload-time = "2023-11-16T02:05:40.913Z" }, +] + +[[package]] +name = "grpcio" +version = "1.74.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1", size = 12756048, upload-time = "2025-07-24T18:54:23.039Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/5d/e504d5d5c4469823504f65687d6c8fb97b7f7bf0b34873b7598f1df24630/grpcio-1.74.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8533e6e9c5bd630ca98062e3a1326249e6ada07d05acf191a77bc33f8948f3d8", size = 5445551, upload-time = "2025-07-24T18:53:23.641Z" }, + { url = "https://files.pythonhosted.org/packages/43/01/730e37056f96f2f6ce9f17999af1556df62ee8dab7fa48bceeaab5fd3008/grpcio-1.74.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:2918948864fec2a11721d91568effffbe0a02b23ecd57f281391d986847982f6", size = 10979810, upload-time = "2025-07-24T18:53:25.349Z" }, + { url = "https://files.pythonhosted.org/packages/79/3d/09fd100473ea5c47083889ca47ffd356576173ec134312f6aa0e13111dee/grpcio-1.74.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:60d2d48b0580e70d2e1954d0d19fa3c2e60dd7cbed826aca104fff518310d1c5", size = 5941946, upload-time = "2025-07-24T18:53:27.387Z" }, + { url = "https://files.pythonhosted.org/packages/8a/99/12d2cca0a63c874c6d3d195629dcd85cdf5d6f98a30d8db44271f8a97b93/grpcio-1.74.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3601274bc0523f6dc07666c0e01682c94472402ac2fd1226fd96e079863bfa49", size = 6621763, upload-time = "2025-07-24T18:53:29.193Z" }, + { url = "https://files.pythonhosted.org/packages/9d/2c/930b0e7a2f1029bbc193443c7bc4dc2a46fedb0203c8793dcd97081f1520/grpcio-1.74.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:176d60a5168d7948539def20b2a3adcce67d72454d9ae05969a2e73f3a0feee7", size = 6180664, upload-time = "2025-07-24T18:53:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/db/d5/ff8a2442180ad0867717e670f5ec42bfd8d38b92158ad6bcd864e6d4b1ed/grpcio-1.74.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e759f9e8bc908aaae0412642afe5416c9f983a80499448fcc7fab8692ae044c3", size = 6301083, upload-time = "2025-07-24T18:53:32.454Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ba/b361d390451a37ca118e4ec7dccec690422e05bc85fba2ec72b06cefec9f/grpcio-1.74.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e7c4389771855a92934b2846bd807fc25a3dfa820fd912fe6bd8136026b2707", size = 6994132, upload-time = "2025-07-24T18:53:34.506Z" }, + { url = "https://files.pythonhosted.org/packages/3b/0c/3a5fa47d2437a44ced74141795ac0251bbddeae74bf81df3447edd767d27/grpcio-1.74.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cce634b10aeab37010449124814b05a62fb5f18928ca878f1bf4750d1f0c815b", size = 6489616, upload-time = "2025-07-24T18:53:36.217Z" }, + { url = "https://files.pythonhosted.org/packages/ae/95/ab64703b436d99dc5217228babc76047d60e9ad14df129e307b5fec81fd0/grpcio-1.74.0-cp312-cp312-win32.whl", hash = "sha256:885912559974df35d92219e2dc98f51a16a48395f37b92865ad45186f294096c", size = 3807083, upload-time = "2025-07-24T18:53:37.911Z" }, + { url = "https://files.pythonhosted.org/packages/84/59/900aa2445891fc47a33f7d2f76e00ca5d6ae6584b20d19af9c06fa09bf9a/grpcio-1.74.0-cp312-cp312-win_amd64.whl", hash = "sha256:42f8fee287427b94be63d916c90399ed310ed10aadbf9e2e5538b3e497d269bc", size = 4490123, upload-time = "2025-07-24T18:53:39.528Z" }, +] + +[[package]] +name = "grpcio-status" +version = "1.74.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/22/238c5f01e6837df54494deb08d5c772bc3f5bf5fb80a15dce254892d1a81/grpcio_status-1.74.0.tar.gz", hash = "sha256:c58c1b24aa454e30f1fc6a7e0dbbc194c54a408143971a94b5f4e40bb5831432", size = 13662, upload-time = "2025-07-24T19:01:56.874Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/aa/1b1fe7d8ab699e1ec26d3a36b91d3df9f83a30abc07d4c881d0296b17b67/grpcio_status-1.74.0-py3-none-any.whl", hash = "sha256:52cdbd759a6760fc8f668098a03f208f493dd5c76bf8e02598bbbaf1f6fc2876", size = 14425, upload-time = "2025-07-24T19:01:19.963Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httptools" +version = "0.6.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639, upload-time = "2024-10-16T19:45:08.902Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683, upload-time = "2024-10-16T19:44:30.175Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337, upload-time = "2024-10-16T19:44:31.786Z" }, + { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796, upload-time = "2024-10-16T19:44:32.825Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837, upload-time = "2024-10-16T19:44:33.974Z" }, + { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289, upload-time = "2024-10-16T19:44:35.111Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779, upload-time = "2024-10-16T19:44:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634, upload-time = "2024-10-16T19:44:37.357Z" }, +] + +[[package]] +name = "identify" +version = "2.6.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ca/ffbabe3635bb839aa36b3a893c91a9b0d368cb4d8073e03a12896970af82/identify-2.6.13.tar.gz", hash = "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32", size = 99243, upload-time = "2025-08-09T19:35:00.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/ce/461b60a3ee109518c055953729bf9ed089a04db895d47e95444071dcdef2/identify-2.6.13-py2.py3-none-any.whl", hash = "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b", size = 99153, upload-time = "2025-08-09T19:34:59.1Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "mako" +version = "1.3.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" }, +] + +[[package]] +name = "proto-plus" +version = "1.26.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142, upload-time = "2025-03-10T15:54:38.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163, upload-time = "2025-03-10T15:54:37.335Z" }, +] + +[[package]] +name = "protobuf" +version = "6.31.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797, upload-time = "2025-05-28T19:25:54.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603, upload-time = "2025-05-28T19:25:41.198Z" }, + { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283, upload-time = "2025-05-28T19:25:44.275Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604, upload-time = "2025-05-28T19:25:45.702Z" }, + { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115, upload-time = "2025-05-28T19:25:47.128Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070, upload-time = "2025-05-28T19:25:50.036Z" }, + { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724, upload-time = "2025-05-28T19:25:53.926Z" }, +] + +[[package]] +name = "pyasn1" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + +[[package]] +name = "pydantic" +version = "2.11.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, +] + +[[package]] +name = "pytest-cov" +version = "6.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + +[[package]] +name = "rsa" +version = "4.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, +] + +[[package]] +name = "ruff" +version = "0.12.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/da/5bd7565be729e86e1442dad2c9a364ceeff82227c2dece7c29697a9795eb/ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033", size = 5242373, upload-time = "2025-08-07T19:05:47.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/1e/c843bfa8ad1114fab3eb2b78235dda76acd66384c663a4e0415ecc13aa1e/ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513", size = 11675315, upload-time = "2025-08-07T19:05:06.15Z" }, + { url = "https://files.pythonhosted.org/packages/24/ee/af6e5c2a8ca3a81676d5480a1025494fd104b8896266502bb4de2a0e8388/ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc", size = 12456653, upload-time = "2025-08-07T19:05:09.759Z" }, + { url = "https://files.pythonhosted.org/packages/99/9d/e91f84dfe3866fa648c10512904991ecc326fd0b66578b324ee6ecb8f725/ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec", size = 11659690, upload-time = "2025-08-07T19:05:12.551Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ac/a363d25ec53040408ebdd4efcee929d48547665858ede0505d1d8041b2e5/ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53", size = 11896923, upload-time = "2025-08-07T19:05:14.821Z" }, + { url = "https://files.pythonhosted.org/packages/58/9f/ea356cd87c395f6ade9bb81365bd909ff60860975ca1bc39f0e59de3da37/ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f", size = 11477612, upload-time = "2025-08-07T19:05:16.712Z" }, + { url = "https://files.pythonhosted.org/packages/1a/46/92e8fa3c9dcfd49175225c09053916cb97bb7204f9f899c2f2baca69e450/ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f", size = 13182745, upload-time = "2025-08-07T19:05:18.709Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c4/f2176a310f26e6160deaf661ef60db6c3bb62b7a35e57ae28f27a09a7d63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609", size = 14206885, upload-time = "2025-08-07T19:05:21.025Z" }, + { url = "https://files.pythonhosted.org/packages/87/9d/98e162f3eeeb6689acbedbae5050b4b3220754554526c50c292b611d3a63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a", size = 13639381, upload-time = "2025-08-07T19:05:23.423Z" }, + { url = "https://files.pythonhosted.org/packages/81/4e/1b7478b072fcde5161b48f64774d6edd59d6d198e4ba8918d9f4702b8043/ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb", size = 12613271, upload-time = "2025-08-07T19:05:25.507Z" }, + { url = "https://files.pythonhosted.org/packages/e8/67/0c3c9179a3ad19791ef1b8f7138aa27d4578c78700551c60d9260b2c660d/ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818", size = 12847783, upload-time = "2025-08-07T19:05:28.14Z" }, + { url = "https://files.pythonhosted.org/packages/4e/2a/0b6ac3dd045acf8aa229b12c9c17bb35508191b71a14904baf99573a21bd/ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea", size = 11702672, upload-time = "2025-08-07T19:05:30.413Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ee/f9fdc9f341b0430110de8b39a6ee5fa68c5706dc7c0aa940817947d6937e/ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3", size = 11440626, upload-time = "2025-08-07T19:05:32.492Z" }, + { url = "https://files.pythonhosted.org/packages/89/fb/b3aa2d482d05f44e4d197d1de5e3863feb13067b22c571b9561085c999dc/ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161", size = 12462162, upload-time = "2025-08-07T19:05:34.449Z" }, + { url = "https://files.pythonhosted.org/packages/18/9f/5c5d93e1d00d854d5013c96e1a92c33b703a0332707a7cdbd0a4880a84fb/ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46", size = 12913212, upload-time = "2025-08-07T19:05:36.541Z" }, + { url = "https://files.pythonhosted.org/packages/71/13/ab9120add1c0e4604c71bfc2e4ef7d63bebece0cfe617013da289539cef8/ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3", size = 11694382, upload-time = "2025-08-07T19:05:38.468Z" }, + { url = "https://files.pythonhosted.org/packages/f6/dc/a2873b7c5001c62f46266685863bee2888caf469d1edac84bf3242074be2/ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e", size = 12740482, upload-time = "2025-08-07T19:05:40.391Z" }, + { url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718, upload-time = "2025-08-07T19:05:42.866Z" }, +] + +[[package]] +name = "setuptools" +version = "80.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/80/97e25f0f1e4067677806084b7382a6ff9979f3d15119375c475c288db9d7/setuptools-80.0.0.tar.gz", hash = "sha256:c40a5b3729d58dd749c0f08f1a07d134fb8a0a3d7f87dc33e7c5e1f762138650", size = 1354221, upload-time = "2025-04-27T17:21:10.806Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/63/5517029d6696ddf2bd378d46f63f479be001c31b462303170a1da57650cb/setuptools-80.0.0-py3-none-any.whl", hash = "sha256:a38f898dcd6e5380f4da4381a87ec90bd0a7eec23d204a5552e80ee3cab6bd27", size = 1240907, upload-time = "2025-04-27T17:21:09.175Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.43" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/bc/d59b5d97d27229b0e009bd9098cd81af71c2fa5549c580a0a67b9bed0496/sqlalchemy-2.0.43.tar.gz", hash = "sha256:788bfcef6787a7764169cfe9859fe425bf44559619e1d9f56f5bddf2ebf6f417", size = 9762949, upload-time = "2025-08-11T14:24:58.438Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/db/20c78f1081446095450bdc6ee6cc10045fce67a8e003a5876b6eaafc5cc4/sqlalchemy-2.0.43-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:20d81fc2736509d7a2bd33292e489b056cbae543661bb7de7ce9f1c0cd6e7f24", size = 2134891, upload-time = "2025-08-11T15:51:13.019Z" }, + { url = "https://files.pythonhosted.org/packages/45/0a/3d89034ae62b200b4396f0f95319f7d86e9945ee64d2343dcad857150fa2/sqlalchemy-2.0.43-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b9fc27650ff5a2c9d490c13c14906b918b0de1f8fcbb4c992712d8caf40e83", size = 2123061, upload-time = "2025-08-11T15:51:14.319Z" }, + { url = "https://files.pythonhosted.org/packages/cb/10/2711f7ff1805919221ad5bee205971254845c069ee2e7036847103ca1e4c/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6772e3ca8a43a65a37c88e2f3e2adfd511b0b1da37ef11ed78dea16aeae85bd9", size = 3320384, upload-time = "2025-08-11T15:52:35.088Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0e/3d155e264d2ed2778484006ef04647bc63f55b3e2d12e6a4f787747b5900/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a113da919c25f7f641ffbd07fbc9077abd4b3b75097c888ab818f962707eb48", size = 3329648, upload-time = "2025-08-11T15:56:34.153Z" }, + { url = "https://files.pythonhosted.org/packages/5b/81/635100fb19725c931622c673900da5efb1595c96ff5b441e07e3dd61f2be/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4286a1139f14b7d70141c67a8ae1582fc2b69105f1b09d9573494eb4bb4b2687", size = 3258030, upload-time = "2025-08-11T15:52:36.933Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ed/a99302716d62b4965fded12520c1cbb189f99b17a6d8cf77611d21442e47/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:529064085be2f4d8a6e5fab12d36ad44f1909a18848fcfbdb59cc6d4bbe48efe", size = 3294469, upload-time = "2025-08-11T15:56:35.553Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a2/3a11b06715149bf3310b55a98b5c1e84a42cfb949a7b800bc75cb4e33abc/sqlalchemy-2.0.43-cp312-cp312-win32.whl", hash = "sha256:b535d35dea8bbb8195e7e2b40059e2253acb2b7579b73c1b432a35363694641d", size = 2098906, upload-time = "2025-08-11T15:55:00.645Z" }, + { url = "https://files.pythonhosted.org/packages/bc/09/405c915a974814b90aa591280623adc6ad6b322f61fd5cff80aeaef216c9/sqlalchemy-2.0.43-cp312-cp312-win_amd64.whl", hash = "sha256:1c6d85327ca688dbae7e2b06d7d84cfe4f3fffa5b5f9e21bb6ce9d0e1a0e0e0a", size = 2126260, upload-time = "2025-08-11T15:55:02.965Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d9/13bdde6521f322861fab67473cec4b1cc8999f3871953531cf61945fad92/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc", size = 1924759, upload-time = "2025-08-11T15:39:53.024Z" }, +] + +[[package]] +name = "sqlalchemy-spanner" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alembic" }, + { name = "google-cloud-spanner" }, + { name = "sqlalchemy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/ea/4dc8ba08c53410eb9d97d052c75b58a78b916f4df3a32f1df226dd232f72/sqlalchemy_spanner-1.14.0.tar.gz", hash = "sha256:5e64c393c3cd2b618a14581f0d67b57cc16d557cc57d5c6da07a1b6f9d035bc8", size = 82007, upload-time = "2025-06-27T13:43:19.331Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/6e/b8db9b9adcd9b700f4dcc0a9874e4de417ea3db34782d60e32e3ef96904b/sqlalchemy_spanner-1.14.0-py3-none-any.whl", hash = "sha256:1f632c90a761ca081adc09bfe0c8f06cfde12e4198bf17d234e25548e6fe3281", size = 31531, upload-time = "2025-06-27T13:43:18.285Z" }, +] + +[[package]] +name = "sqlparse" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/40/edede8dd6977b0d3da179a342c198ed100dd2aba4be081861ee5911e4da4/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", size = 84999, upload-time = "2024-12-10T12:05:30.728Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload-time = "2024-12-10T12:05:27.824Z" }, +] + +[[package]] +name = "starlette" +version = "0.47.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/57/d062573f391d062710d4088fa1369428c38d51460ab6fedff920efef932e/starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8", size = 2583948, upload-time = "2025-07-20T17:31:58.522Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984, upload-time = "2025-07-20T17:31:56.738Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.35.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "uvloop" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741, upload-time = "2024-10-14T23:38:35.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/4c/03f93178830dc7ce8b4cdee1d36770d2f5ebb6f3d37d354e061eefc73545/uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c", size = 1471284, upload-time = "2024-10-14T23:37:47.833Z" }, + { url = "https://files.pythonhosted.org/packages/43/3e/92c03f4d05e50f09251bd8b2b2b584a2a7f8fe600008bcc4523337abe676/uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2", size = 821349, upload-time = "2024-10-14T23:37:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ef/a02ec5da49909dbbfb1fd205a9a1ac4e88ea92dcae885e7c961847cd51e2/uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d", size = 4580089, upload-time = "2024-10-14T23:37:51.703Z" }, + { url = "https://files.pythonhosted.org/packages/06/a7/b4e6a19925c900be9f98bec0a75e6e8f79bb53bdeb891916609ab3958967/uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc", size = 4693770, upload-time = "2024-10-14T23:37:54.122Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0c/f07435a18a4b94ce6bd0677d8319cd3de61f3a9eeb1e5f8ab4e8b5edfcb3/uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb", size = 4451321, upload-time = "2024-10-14T23:37:55.766Z" }, + { url = "https://files.pythonhosted.org/packages/8f/eb/f7032be105877bcf924709c97b1bf3b90255b4ec251f9340cef912559f28/uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f", size = 4659022, upload-time = "2024-10-14T23:37:58.195Z" }, +] + +[[package]] +name = "virtualenv" +version = "20.34.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808, upload-time = "2025-08-13T14:24:07.464Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", size = 5983279, upload-time = "2025-08-13T14:24:05.111Z" }, +] + +[[package]] +name = "watchfiles" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406, upload-time = "2025-06-15T19:06:59.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339, upload-time = "2025-06-15T19:05:24.516Z" }, + { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409, upload-time = "2025-06-15T19:05:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939, upload-time = "2025-06-15T19:05:26.494Z" }, + { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270, upload-time = "2025-06-15T19:05:27.466Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370, upload-time = "2025-06-15T19:05:28.548Z" }, + { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654, upload-time = "2025-06-15T19:05:29.997Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667, upload-time = "2025-06-15T19:05:31.172Z" }, + { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213, upload-time = "2025-06-15T19:05:32.299Z" }, + { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718, upload-time = "2025-06-15T19:05:33.415Z" }, + { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098, upload-time = "2025-06-15T19:05:34.534Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209, upload-time = "2025-06-15T19:05:35.577Z" }, + { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786, upload-time = "2025-06-15T19:05:36.559Z" }, + { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343, upload-time = "2025-06-15T19:05:37.5Z" }, +] + +[[package]] +name = "websockets" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, + { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, + { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, + { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, + { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, + { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, + { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, + { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, + { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, +] From ee1854e69831fc0e8710df9b77940f1954219c7e Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Thu, 20 Nov 2025 14:11:04 -0800 Subject: [PATCH 02/19] file formatting, more uv migrations --- .github/workflows/ci.yaml | 38 +++++++------------ README.md | 31 +++------------ .../datacommons_api/app_cli.py | 6 ++- .../datacommons_api/core/config.py | 12 +++++- .../datacommons_api/endpoints/dependencies.py | 6 ++- .../endpoints/routers/node_router.py | 11 ++++-- .../datacommons_api/services/graph_service.py | 36 ++++++++++++++---- .../datacommons_api/version.py | 2 +- .../datacommons_db/models/edge.py | 12 +++--- .../repositories/node_repository.py | 6 ++- .../datacommons-db/datacommons_db/session.py | 8 +++- .../datacommons-db/datacommons_db/version.py | 2 +- .../converters/mcf_to_jsonld.py | 4 +- .../datacommons_schema/models/jsonld.py | 27 ++++++++++--- .../datacommons_schema/models/mcf.py | 6 ++- .../parsers/mcf_parser_test.py | 6 ++- .../datacommons_schema/schema_cli.py | 14 +++++-- .../datacommons_schema/version.py | 2 +- pyproject.toml | 4 -- uv.lock | 2 + 20 files changed, 146 insertions(+), 89 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2505fbd..94a4747 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -31,32 +31,22 @@ jobs: restore-keys: | pip-${{ runner.os }}-${{ matrix.python-version }}- - - name: Install Hatch - run: pip install --no-cache-dir hatch + - name: Install uv + run: pip install uv - - name: Cache Hatch environments - uses: actions/cache@v4 - with: - path: ~/.local/share/hatch - key: hatch-env-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} - restore-keys: | - hatch-env-${{ runner.os }}-${{ matrix.python-version }}- + - name: Install dependencies + run: uv sync + + - name: Check formatting with Ruff + working-directory: packages/datacommons-mcp + run: uv run ruff format --check + + # TODO(dwnoble): Fix formatting issues in datacommons-schema and uncomment these + #- name: Lint with Ruff + # working-directory: packages/datacommons-mcp + # run: uv run ruff check - name: Run tests run: | # Enable parallel test execution - hatch test --parallel - env: - # Add environment variables to speed up test execution - PYTHONHASHSEED: 0 - PYTHONUNBUFFERED: 1 - - - name: Upload test results - if: always() - uses: actions/upload-artifact@v4 - with: - name: test-results-${{ matrix.python-version }} - path: | - .coverage - htmlcov/ - if-no-files-found: error + uv run pytest diff --git a/README.md b/README.md index e8e5464..614eda6 100644 --- a/README.md +++ b/README.md @@ -17,18 +17,10 @@ This guide covers setting up a local Data Commons, defining schemas in JSON-LD, Before you begin, ensure you have the following installed: - [Python](https://www.python.org/downloads/) 3.11 or higher -- [Hatch](https://hatch.pypa.io/latest/) (Python project manager) +- [uv](https://docs.astral.sh/uv/getting-started/installation/) (Python project manager) - A Google Cloud Platform (GCP) project with Cloud Spanner enabled - A Cloud Spanner instance and database (using Google Standard SQL) for storing the knowledge graph -### Installing Hatch - -You can install Hatch using pip: - -```bash -pip install hatch -``` - ## Setting Up Data Commons This section will guide you through setting up Data Commons locally and defining your first custom schema and data. @@ -49,10 +41,10 @@ The repository contains three main components: - `datacommons-db`: The database layer for storing and querying data - `datacommons-schema`: Schema management and validation tools -#### Create a Hatch environment +#### Create a virtual environment with uv ```bash -hatch env create +uv sync ``` #### Run Tests @@ -60,22 +52,11 @@ hatch env create Run the test suite to verify your setup: ```bash -hatch test +uv run pytest ``` Tests are also run automatically before pushing changes. -#### Enter the Hatch Shell - -Activate the project's environment to run local commands. All subsequent commands should be run inside this shell. - -```bash -hatch shell -``` - -To exit the shell, type `exit` or press `ctrl+d` - - #### Configure GCP Spanner Environment Variables Before starting the server, you need to set up your GCP Spanner environment variables. These are required for the application to connect to your Spanner database. The application will initialize a new database from scratch using these settings: @@ -90,10 +71,10 @@ Replace the values with your actual GCP project and Spanner instance details. Yo #### Start Data Commons: -Activate our hatch environment and run the `datacommons-api` command to start a local development server. +Run the `datacommons-api` command using `uv` to start a local development server. ```bash -datacommons-api +uv run datacommons-api ``` This will start the Data Commons API server on port 5000, ready to receive your schema and data. diff --git a/packages/datacommons-api/datacommons_api/app_cli.py b/packages/datacommons-api/datacommons_api/app_cli.py index 5449e23..fe9966e 100644 --- a/packages/datacommons-api/datacommons_api/app_cli.py +++ b/packages/datacommons-api/datacommons_api/app_cli.py @@ -35,7 +35,11 @@ def main(host: str, port: int, *, reload: bool = False): # Initialize the database logger.info("Initializing database...") - initialize_db(config.GCP_PROJECT_ID, config.GCP_SPANNER_INSTANCE_ID, config.GCP_SPANNER_DATABASE_NAME) + initialize_db( + config.GCP_PROJECT_ID, + config.GCP_SPANNER_INSTANCE_ID, + config.GCP_SPANNER_DATABASE_NAME, + ) logger.info("Starting API server...") uvicorn.run( app, diff --git a/packages/datacommons-api/datacommons_api/core/config.py b/packages/datacommons-api/datacommons_api/core/config.py index 9026efb..8c38e3e 100644 --- a/packages/datacommons-api/datacommons_api/core/config.py +++ b/packages/datacommons-api/datacommons_api/core/config.py @@ -20,7 +20,11 @@ logger = get_logger(__name__) # Required environment variables -REQUIRED_ENV_VARS = ["GCP_PROJECT_ID", "GCP_SPANNER_INSTANCE_ID", "GCP_SPANNER_DATABASE_NAME"] +REQUIRED_ENV_VARS = [ + "GCP_PROJECT_ID", + "GCP_SPANNER_INSTANCE_ID", + "GCP_SPANNER_DATABASE_NAME", +] class Config: @@ -45,7 +49,11 @@ class ProductionConfig(Config): # Configuration dictionary -config = {"development": DevelopmentConfig, "production": ProductionConfig, "default": DevelopmentConfig} +config = { + "development": DevelopmentConfig, + "production": ProductionConfig, + "default": DevelopmentConfig, +} def validate_config_or_exit(config: Config) -> None: diff --git a/packages/datacommons-api/datacommons_api/endpoints/dependencies.py b/packages/datacommons-api/datacommons_api/endpoints/dependencies.py index b54ccfc..bb25f32 100644 --- a/packages/datacommons-api/datacommons_api/endpoints/dependencies.py +++ b/packages/datacommons-api/datacommons_api/endpoints/dependencies.py @@ -28,7 +28,11 @@ def with_graph_service() -> Generator[GraphService, None, None]: GraphService: A GraphService instance """ config = get_config() - db = get_session(config.GCP_PROJECT_ID, config.GCP_SPANNER_INSTANCE_ID, config.GCP_SPANNER_DATABASE_NAME) + db = get_session( + config.GCP_PROJECT_ID, + config.GCP_SPANNER_INSTANCE_ID, + config.GCP_SPANNER_DATABASE_NAME, + ) graph_service = GraphService(db) try: yield graph_service diff --git a/packages/datacommons-api/datacommons_api/endpoints/routers/node_router.py b/packages/datacommons-api/datacommons_api/endpoints/routers/node_router.py index 2a01ed7..41f036c 100644 --- a/packages/datacommons-api/datacommons_api/endpoints/routers/node_router.py +++ b/packages/datacommons-api/datacommons_api/endpoints/routers/node_router.py @@ -32,7 +32,9 @@ @router.get("/nodes/", response_model=JSONLDDocument, response_model_exclude_none=True) def get_nodes( limit: int = DEFAULT_NODE_FETCH_LIMIT, - type_filter: Annotated[list[str] | None, Query(alias="type", description="Zero or more types")] = None, + type_filter: Annotated[ + list[str] | None, Query(alias="type", description="Zero or more types") + ] = None, graph_service: Annotated[GraphService, Depends(with_graph_service)] = None, ) -> JSONLDDocument: """ @@ -44,12 +46,15 @@ def get_nodes( @router.post("/nodes/", response_model=UpdateResponse, response_model_exclude_none=True) def insert_nodes( - jsonld: JSONLDDocument, graph_service: Annotated[GraphService, Depends(with_graph_service)] = None + jsonld: JSONLDDocument, + graph_service: Annotated[GraphService, Depends(with_graph_service)] = None, ) -> UpdateResponse: """Insert a JSON-LD document into the database""" try: graph_service.insert_graph_nodes(jsonld) - return UpdateResponse(success=True, message="Inserted %d nodes successfully" % len(jsonld.graph)) + return UpdateResponse( + success=True, message="Inserted %d nodes successfully" % len(jsonld.graph) + ) except Exception as e: logger.exception("Error inserting nodes") return UpdateResponse(success=False, message=str(e)) diff --git a/packages/datacommons-api/datacommons_api/services/graph_service.py b/packages/datacommons-api/datacommons_api/services/graph_service.py index f0e8ef6..941f2e6 100644 --- a/packages/datacommons-api/datacommons_api/services/graph_service.py +++ b/packages/datacommons-api/datacommons_api/services/graph_service.py @@ -24,7 +24,11 @@ # Local application imports from datacommons_db.models.node import NodeModel -from datacommons_schema.models.jsonld import GraphNode, GraphNodePropertyValue, JSONLDDocument +from datacommons_schema.models.jsonld import ( + GraphNode, + GraphNodePropertyValue, + JSONLDDocument, +) # Configure logging logger = logging.getLogger(__name__) @@ -218,7 +222,9 @@ def __init__(self, session: Session): logger.info("Initialized GraphService with new session") def get_graph_nodes( - self, limit: int = DEFAULT_NODE_FETCH_LIMIT, type_filter: list[str] | None = None + self, + limit: int = DEFAULT_NODE_FETCH_LIMIT, + type_filter: list[str] | None = None, ) -> JSONLDDocument: """ Get nodes with their outgoing edges, and transform them into JSON-LD format. @@ -230,18 +236,28 @@ def get_graph_nodes( Returns: A JSONLDDocument containing the transformed nodes """ - logger.info("Fetching graph nodes (limit=%d, type_filter=%s)", limit, type_filter) - node_models = self._get_nodes_with_outgoing_edges(limit=limit, type_filter=type_filter) + logger.info( + "Fetching graph nodes (limit=%d, type_filter=%s)", limit, type_filter + ) + node_models = self._get_nodes_with_outgoing_edges( + limit=limit, type_filter=type_filter + ) graph_nodes = [node_model_to_graph_node(n) for n in node_models] logger.info("Transformed %d nodes to JSON-LD format", len(graph_nodes)) return JSONLDDocument( - context={"@vocab": LOCAL_NAMESPACE_URL, LOCAL_NAMESPACE_NAME: LOCAL_NAMESPACE_URL, **BASE_NAMESPACES}, + context={ + "@vocab": LOCAL_NAMESPACE_URL, + LOCAL_NAMESPACE_NAME: LOCAL_NAMESPACE_URL, + **BASE_NAMESPACES, + }, graph=graph_nodes, ) def _get_nodes_with_outgoing_edges( - self, limit: int = DEFAULT_NODE_FETCH_LIMIT, type_filter: list[str] | None = None + self, + limit: int = DEFAULT_NODE_FETCH_LIMIT, + type_filter: list[str] | None = None, ) -> list[NodeModel]: """ Get nodes with their outgoing edges. @@ -258,7 +274,13 @@ def _get_nodes_with_outgoing_edges( if type_filter: logger.info("Filtering nodes by types: %s", type_filter) query = query.filter( - text("EXISTS (" " SELECT 1 " " FROM UNNEST(types) AS t " " WHERE t IN UNNEST(:type_filter)" ")") + text( + "EXISTS (" + " SELECT 1 " + " FROM UNNEST(types) AS t " + " WHERE t IN UNNEST(:type_filter)" + ")" + ) ).params(type_filter=type_filter) # Load outgoing edges diff --git a/packages/datacommons-api/datacommons_api/version.py b/packages/datacommons-api/datacommons_api/version.py index b3c06d4..f102a9c 100644 --- a/packages/datacommons-api/datacommons_api/version.py +++ b/packages/datacommons-api/datacommons_api/version.py @@ -1 +1 @@ -__version__ = "0.0.1" \ No newline at end of file +__version__ = "0.0.1" diff --git a/packages/datacommons-db/datacommons_db/models/edge.py b/packages/datacommons-db/datacommons_db/models/edge.py index ac32c68..25e85eb 100644 --- a/packages/datacommons-db/datacommons_db/models/edge.py +++ b/packages/datacommons-db/datacommons_db/models/edge.py @@ -26,7 +26,9 @@ class EdgeModel(Base): """ __tablename__ = "Edge" - subject_id = sa.Column(String(1024), sa.ForeignKey("Node.subject_id"), primary_key=True) + subject_id = sa.Column( + String(1024), sa.ForeignKey("Node.subject_id"), primary_key=True + ) predicate = sa.Column(String(1024), primary_key=True) object_id = sa.Column(String(1024), primary_key=True) object_value = sa.Column(Text(), nullable=True) @@ -38,7 +40,9 @@ class EdgeModel(Base): ) # TOKENLIST is a Spanner type, but represented as String in SQLAlchemy # Define relationships to both source and target nodes - source_node = relationship("NodeModel", foreign_keys=[subject_id], back_populates="outgoing_edges") + source_node = relationship( + "NodeModel", foreign_keys=[subject_id], back_populates="outgoing_edges" + ) # Indexes __table_args__ = ( @@ -47,6 +51,4 @@ class EdgeModel(Base): ) def __repr__(self): - return ( - f"" - ) + return f"" diff --git a/packages/datacommons-db/datacommons_db/repositories/node_repository.py b/packages/datacommons-db/datacommons_db/repositories/node_repository.py index 9c15490..b186426 100644 --- a/packages/datacommons-db/datacommons_db/repositories/node_repository.py +++ b/packages/datacommons-db/datacommons_db/repositories/node_repository.py @@ -26,7 +26,11 @@ def __init__(self, session: Session): self.session = session def get_node(self, subject_id: str) -> NodeModel: - return self.session.query(NodeModel).filter(NodeModel.subject_id == subject_id).first() + return ( + self.session.query(NodeModel) + .filter(NodeModel.subject_id == subject_id) + .first() + ) def create_node(self, node: NodeModel) -> NodeModel: self.session.add(node) diff --git a/packages/datacommons-db/datacommons_db/session.py b/packages/datacommons-db/datacommons_db/session.py index 810b0bc..dda8122 100644 --- a/packages/datacommons-db/datacommons_db/session.py +++ b/packages/datacommons-db/datacommons_db/session.py @@ -71,9 +71,13 @@ def initialize_db(project_id: str, instance_id: str, database_name: str): existing_tables = inspector.get_table_names() # Check if all required tables exist - missing_tables = [table for table in REQUIRED_TABLES if table not in existing_tables] + missing_tables = [ + table for table in REQUIRED_TABLES if table not in existing_tables + ] if missing_tables: - logger.warning("Missing required tables in database %s: %s", database_name, missing_tables) + logger.warning( + "Missing required tables in database %s: %s", database_name, missing_tables + ) # Only create tables if database is completely empty if not existing_tables or missing_tables: diff --git a/packages/datacommons-db/datacommons_db/version.py b/packages/datacommons-db/datacommons_db/version.py index b3c06d4..f102a9c 100644 --- a/packages/datacommons-db/datacommons_db/version.py +++ b/packages/datacommons-db/datacommons_db/version.py @@ -1 +1 @@ -__version__ = "0.0.1" \ No newline at end of file +__version__ = "0.0.1" diff --git a/packages/datacommons-schema/datacommons_schema/converters/mcf_to_jsonld.py b/packages/datacommons-schema/datacommons_schema/converters/mcf_to_jsonld.py index b5cf0e6..7cd936f 100644 --- a/packages/datacommons-schema/datacommons_schema/converters/mcf_to_jsonld.py +++ b/packages/datacommons-schema/datacommons_schema/converters/mcf_to_jsonld.py @@ -58,7 +58,9 @@ def mcf_node_to_jsonld(node: McfNode, *, compact: bool = False) -> GraphNode: return graph_node -def mcf_nodes_to_jsonld(nodes: list[McfNode], *, compact: bool = False) -> JSONLDDocument: +def mcf_nodes_to_jsonld( + nodes: list[McfNode], *, compact: bool = False +) -> JSONLDDocument: context = { "@version": 1.1, "@vocab": "https://schema.org/", diff --git a/packages/datacommons-schema/datacommons_schema/models/jsonld.py b/packages/datacommons-schema/datacommons_schema/models/jsonld.py index d97044d..ecb9662 100644 --- a/packages/datacommons-schema/datacommons_schema/models/jsonld.py +++ b/packages/datacommons-schema/datacommons_schema/models/jsonld.py @@ -30,10 +30,17 @@ class GraphNodePropertyValue(BaseModel): class GraphNode(BaseModel): id: str = Field(..., alias="@id", description="Unique identifier for this node") - type: str | list[str] | None = Field(None, alias="@type", description="RDF type(s) of this node") + type: str | list[str] | None = Field( + None, alias="@type", description="RDF type(s) of this node" + ) # Allow arbitrary fields with our custom types - model_config = ConfigDict(populate_by_name=False, extra="allow", arbitrary_types_allowed=True, exclude_none=True) + model_config = ConfigDict( + populate_by_name=False, + extra="allow", + arbitrary_types_allowed=True, + exclude_none=True, + ) def __init__(self, **data): # Process arbitrary fields to ensure they match our expected types @@ -47,7 +54,9 @@ def __init__(self, **data): processed_data[key] = self._process_field_value(value) super().__init__(**processed_data) - def _process_field_value(self, value: Any) -> GraphNodePropertyValue | list[GraphNodePropertyValue]: + def _process_field_value( + self, value: Any + ) -> GraphNodePropertyValue | list[GraphNodePropertyValue]: """Process field values to ensure they match our expected types.""" # If the value is a dict and has @value, @provenance, or @id, return a GraphNodePropertyValue @@ -70,12 +79,20 @@ def model_json_schema(cls, **kwargs) -> dict: {"value": "Alice Smith", "provenance": "https://example.org/source2"}, ], "home": { - "value": {"@id": "place:geoId/06", "@type": ["State"], "name": "California"}, + "value": { + "@id": "place:geoId/06", + "@type": ["State"], + "name": "California", + }, "provenance": "https://example.org/source1", }, "friends": [ { - "value": {"@id": "http://example.org/person/bob", "@type": "Person", "name": "Bob"}, + "value": { + "@id": "http://example.org/person/bob", + "@type": "Person", + "name": "Bob", + }, "provenance": "https://example.org/source1", } ], diff --git a/packages/datacommons-schema/datacommons_schema/models/mcf.py b/packages/datacommons-schema/datacommons_schema/models/mcf.py index 48b1e1a..e45f351 100644 --- a/packages/datacommons-schema/datacommons_schema/models/mcf.py +++ b/packages/datacommons-schema/datacommons_schema/models/mcf.py @@ -41,7 +41,11 @@ def get_value(self) -> Any: # Note: Pydantic's default number parsing might already handle this, # but keeping original logic for explicit control. if isinstance(self.value, int | float): - return float(self.value) if isinstance(self.value, float) or "." in str(self.value) else int(self.value) + return ( + float(self.value) + if isinstance(self.value, float) or "." in str(self.value) + else int(self.value) + ) return float(self.value) if "." in str(self.value) else int(self.value) if self.type == "reference": return f"{self.namespace}:{self.value}" if self.namespace else self.value diff --git a/packages/datacommons-schema/datacommons_schema/parsers/mcf_parser_test.py b/packages/datacommons-schema/datacommons_schema/parsers/mcf_parser_test.py index fdb6a36..533c88a 100644 --- a/packages/datacommons-schema/datacommons_schema/parsers/mcf_parser_test.py +++ b/packages/datacommons-schema/datacommons_schema/parsers/mcf_parser_test.py @@ -17,7 +17,11 @@ import pytest -from datacommons_schema.parsers.mcf_parser import MCFParseError, parse_mcf, parse_mcf_string +from datacommons_schema.parsers.mcf_parser import ( + MCFParseError, + parse_mcf, + parse_mcf_string, +) # Test cases for loading mcf diff --git a/packages/datacommons-schema/datacommons_schema/schema_cli.py b/packages/datacommons-schema/datacommons_schema/schema_cli.py index 5176b1a..6ce0561 100644 --- a/packages/datacommons-schema/datacommons_schema/schema_cli.py +++ b/packages/datacommons-schema/datacommons_schema/schema_cli.py @@ -13,8 +13,14 @@ def cli(): @cli.command() @click.argument("mcf_file", type=click.Path(exists=True)) -@click.option("--namespace", "-n", help='Namespace to inject into JSONLD output (e.g. "schema:https://schema.org/")') -@click.option("--outfile", "-o", type=click.Path(), help="Output file path (defaults to stdout)") +@click.option( + "--namespace", + "-n", + help='Namespace to inject into JSONLD output (e.g. "schema:https://schema.org/")', +) +@click.option( + "--outfile", "-o", type=click.Path(), help="Output file path (defaults to stdout)" +) @click.option( "--compact", "-c", @@ -36,7 +42,9 @@ def mcf2jsonld(mcf_file, namespace, outfile, *, compact: bool = False): ns_prefix, ns_url = namespace.split(":", 1) jsonld.context[ns_prefix] = ns_url except ValueError: - click.echo("Error: Invalid namespace format. Expected format: prefix:url", err=True) + click.echo( + "Error: Invalid namespace format. Expected format: prefix:url", err=True + ) sys.exit(1) # Convert to formatted JSON string diff --git a/packages/datacommons-schema/datacommons_schema/version.py b/packages/datacommons-schema/datacommons_schema/version.py index b3c06d4..f102a9c 100644 --- a/packages/datacommons-schema/datacommons_schema/version.py +++ b/packages/datacommons-schema/datacommons_schema/version.py @@ -1 +1 @@ -__version__ = "0.0.1" \ No newline at end of file +__version__ = "0.0.1" diff --git a/pyproject.toml b/pyproject.toml index 4d40588..e52b8ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,10 +40,6 @@ datacommons-schema = { workspace = true } [tool.uv.workspace] members = ["packages/*"] -[tool.pytest.ini_options] -addopts = "--cov=datacommons --cov-report=term --cov-report=html" -testpaths = ["tests"] - [tool.coverage.run] source = ["datacommons"] branch = true diff --git a/uv.lock b/uv.lock index 80d2ed0..ebe3245 100644 --- a/uv.lock +++ b/uv.lock @@ -345,6 +345,8 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" }, { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" }, { url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142, upload-time = "2025-08-07T13:18:22.981Z" }, + { url = "https://files.pythonhosted.org/packages/27/45/80935968b53cfd3f33cf99ea5f08227f2646e044568c9b1555b58ffd61c2/greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0", size = 1564846, upload-time = "2025-11-04T12:42:15.191Z" }, + { url = "https://files.pythonhosted.org/packages/69/02/b7c30e5e04752cb4db6202a3858b149c0710e5453b71a3b2aec5d78a1aab/greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d", size = 1633814, upload-time = "2025-11-04T12:42:17.175Z" }, { url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899, upload-time = "2025-08-07T13:38:53.448Z" }, ] From 363ca4c301f8317eac46eec719ea86bbb2d7300d Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Thu, 20 Nov 2025 15:21:53 -0800 Subject: [PATCH 03/19] unit test fixes --- .github/workflows/ci.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 94a4747..25738a2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -23,13 +23,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Cache pip dependencies - uses: actions/cache@v4 + - name: Set up Python + uses: actions/setup-python@v5 with: - path: ~/.cache/pip - key: pip-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} - restore-keys: | - pip-${{ runner.os }}-${{ matrix.python-version }}- + python-version: ${{ matrix.python-version }} - name: Install uv run: pip install uv From 0de6c30f95ba2a2d80c8f29105c9c4a102746e7e Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Thu, 20 Nov 2025 15:22:13 -0800 Subject: [PATCH 04/19] python version fix --- packages/datacommons-api/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/datacommons-api/pyproject.toml b/packages/datacommons-api/pyproject.toml index 70e85a8..dee7896 100644 --- a/packages/datacommons-api/pyproject.toml +++ b/packages/datacommons-api/pyproject.toml @@ -4,7 +4,7 @@ dynamic = ["version"] description = 'Data Commons API' license = "Apache-2.0" readme = "README.md" -requires-python = ">=3.12,<3.13" +requires-python = ">=3.11,<3.13" keywords = [] authors = [] dependencies = [ From ff20e878a555b49042a950d47a0e22c79afc10e8 Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Thu, 20 Nov 2025 15:34:53 -0800 Subject: [PATCH 05/19] test fixes --- .github/workflows/ci.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 25738a2..9830fa4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -23,11 +23,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Install uv run: pip install uv @@ -35,12 +30,10 @@ jobs: run: uv sync - name: Check formatting with Ruff - working-directory: packages/datacommons-mcp run: uv run ruff format --check # TODO(dwnoble): Fix formatting issues in datacommons-schema and uncomment these #- name: Lint with Ruff - # working-directory: packages/datacommons-mcp # run: uv run ruff check - name: Run tests From 3c0fa7b8015bbfc42fd15675b669102666a724f0 Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Thu, 20 Nov 2025 16:53:33 -0800 Subject: [PATCH 06/19] Added schema validation --- .../models/primitives/rdf.py | 44 ++++ .../models/primitives/shacl.py | 32 +++ .../parsers/jsonld_parser.py | 69 ++++++ .../datacommons_schema/services/__init__.py | 0 .../services/schema_service.py | 219 ++++++++++++++++++ .../services/schema_service_test.py | 59 +++++ packages/datacommons-schema/pyproject.toml | 3 +- 7 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 packages/datacommons-schema/datacommons_schema/models/primitives/rdf.py create mode 100644 packages/datacommons-schema/datacommons_schema/models/primitives/shacl.py create mode 100644 packages/datacommons-schema/datacommons_schema/parsers/jsonld_parser.py create mode 100644 packages/datacommons-schema/datacommons_schema/services/__init__.py create mode 100644 packages/datacommons-schema/datacommons_schema/services/schema_service.py create mode 100644 packages/datacommons-schema/datacommons_schema/services/schema_service_test.py diff --git a/packages/datacommons-schema/datacommons_schema/models/primitives/rdf.py b/packages/datacommons-schema/datacommons_schema/models/primitives/rdf.py new file mode 100644 index 0000000..ce28b40 --- /dev/null +++ b/packages/datacommons-schema/datacommons_schema/models/primitives/rdf.py @@ -0,0 +1,44 @@ +# Copyright 2025 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pydantic import BaseModel, Field + +# RDF, RDFS, and XSD Namespaces +RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" +RDFS_NS = "http://www.w3.org/2000/01/rdf-schema#" +XSD_NS = "http://www.w3.org/2001/XMLSchema#" + + +class RDFResource(BaseModel): + id: str = Field(..., alias="@id") + + +class RDFSClass(RDFResource): + type: str = Field(default=f"{RDFS_NS}Class", alias="@type") + label: str | None = Field(None, alias=f"{RDFS_NS}label") + comment: str | None = Field(None, alias=f"{RDFS_NS}comment") + subclass_of: list[str] | None = Field(None, alias=f"{RDFS_NS}subClassOf") + + +class RDFProperty(RDFResource): + type: str = Field(default=f"{RDF_NS}Property", alias="@type") + label: str | None = Field(None, alias=f"{RDFS_NS}label") + comment: str | None = Field(None, alias=f"{RDFS_NS}comment") + domain: list[str] | None = Field(None, alias=f"{RDFS_NS}domain") + range: list[str] | None = Field(None, alias=f"{RDFS_NS}range") + + +class XSDDatatype(RDFResource): + type: str = Field(default=f"{RDFS_NS}Datatype", alias="@type") + label: str | None = Field(None, alias=f"{RDFS_NS}label") diff --git a/packages/datacommons-schema/datacommons_schema/models/primitives/shacl.py b/packages/datacommons-schema/datacommons_schema/models/primitives/shacl.py new file mode 100644 index 0000000..92f308a --- /dev/null +++ b/packages/datacommons-schema/datacommons_schema/models/primitives/shacl.py @@ -0,0 +1,32 @@ +# Copyright 2025 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pydantic import BaseModel, Field + +SHACL_NS = "http://www.w3.org/ns/shacl#" + + +class SHACLProperty(BaseModel): + path: str = Field(..., alias=f"{SHACL_NS}path") + datatype: str | None = Field(None, alias=f"{SHACL_NS}datatype") + node_kind: str | None = Field(None, alias=f"{SHACL_NS}nodeKind") + min_count: int | None = Field(None, alias=f"{SHACL_NS}minCount") + max_count: int | None = Field(None, alias=f"{SHACL_NS}maxCount") + + +class SHACLNodeShape(BaseModel): + id: str = Field(..., alias="@id") + type: str = Field(default=f"{SHACL_NS}NodeShape", alias="@type") + target_class: str | None = Field(None, alias=f"{SHACL_NS}targetClass") + properties: list[SHACLProperty] | None = Field(None, alias=f"{SHACL_NS}property") diff --git a/packages/datacommons-schema/datacommons_schema/parsers/jsonld_parser.py b/packages/datacommons-schema/datacommons_schema/parsers/jsonld_parser.py new file mode 100644 index 0000000..3101918 --- /dev/null +++ b/packages/datacommons-schema/datacommons_schema/parsers/jsonld_parser.py @@ -0,0 +1,69 @@ +# Copyright 2025 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +from typing import IO + +from datacommons_schema.models.primitives import rdf, shacl + + +class JSONLDParseError(Exception): + """Raised when JSON-LD parsing fails""" + + +def parse_jsonld(stream: IO[str]) -> list[rdf.RDFResource]: + """Parse a JSON-LD file and return a list of RDFResource objects. + + Args: + stream: A file-like object containing JSON-LD content. + + Returns: + A list of RDFResource objects. + + Raises: + JSONLDParseError: If the JSON-LD content is invalid. + """ + try: + data = json.load(stream) + except json.JSONDecodeError as e: + raise JSONLDParseError(f"Invalid JSON: {e}") from e + + if "@graph" not in data: + raise JSONLDParseError("Missing '@graph' key in JSON-LD file.") + + context = data.get("@context", {}) + + def expand_uri(uri: str) -> str: + """Expand a URI using the context.""" + if not isinstance(uri, str): + return uri + for prefix, namespace in context.items(): + if uri.startswith(f"{prefix}:"): + return uri.replace(f"{prefix}:", namespace, 1) + return uri + + resources = [] + for node in data["@graph"]: + node_type = expand_uri(node.get("@type")) + if node_type == f"{rdf.RDFS_NS}Class": + resources.append(rdf.RDFSClass(**node)) + elif node_type == f"{rdf.RDF_NS}Property": + resources.append(rdf.RDFProperty(**node)) + elif node_type == f"{shacl.SHACL_NS}NodeShape": + resources.append(shacl.SHACLNodeShape(**node)) + else: + # For now, we only support RDFSClass, RDFProperty, and SHACLNodeShape + continue + + return resources diff --git a/packages/datacommons-schema/datacommons_schema/services/__init__.py b/packages/datacommons-schema/datacommons_schema/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/packages/datacommons-schema/datacommons_schema/services/schema_service.py b/packages/datacommons-schema/datacommons_schema/services/schema_service.py new file mode 100644 index 0000000..4b75fcb --- /dev/null +++ b/packages/datacommons-schema/datacommons_schema/services/schema_service.py @@ -0,0 +1,219 @@ +# Copyright 2025 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Schema Service Module. + +This module handles the "rules" (schema) for Data Commons. It loads definitions of +what things are (Classes), what properties they can have (Properties), and rules +for validation (Shapes). + +Example: + service = SchemaService() + with open("my_schema.jsonld") as f: + service.load_schema(f) + + # Now you can check if data is valid + service.validate_node(my_data_node) +""" + +import json +from typing import IO + +from datacommons_schema.models.jsonld import GraphNode +from datacommons_schema.models.primitives import rdf, shacl +from datacommons_schema.parsers import jsonld_parser +from pyshacl import validate + + +class ValidationError(Exception): + """Raised when data doesn't match the schema rules.""" + + +class SchemaService: + """A service for defining and validating semantic data. + + This service defines classes, properties, and objects, and constraints + using RDF Graphs and SHACL. It manages definitions for: + - Classes (e.g., "Person", "City") + - Properties (e.g., "name", "population") + - Shapes (Validation rules, e.g., "A Person must have a name") + + Example: + from io import StringIO + from datacommons_schema.models.jsonld import GraphNode + + service = SchemaService() + + # Define a simple Person class and a shape for it + schema_jsonld = \"\"\"{ + "@context": { + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#" + }, + "@graph": [ + { + "@id": "Person", + "@type": "rdfs:Class" + }, + { + "@id": "name", + "@type": "rdf:Property" + } + ] + }\"\"\" + service.load_schema(StringIO(schema_jsonld)) + + # Create a valid person node + valid_person_node = GraphNode(id="john_doe", type="Person", properties={"name": "John Doe"}) + is_valid, report = service.validate_node(valid_person_node) + print(f"Valid person node is valid: {is_valid}") # Expected: True + + # Create an invalid person node (missing name) + invalid_person_node = GraphNode(id="jane_doe", type="Person") + is_valid, report = service.validate_node(invalid_person_node) + print(f"Invalid person node is valid: {is_valid}") # Expected: False + # print(report) # Uncomment to see the validation details + """ + + def __init__(self) -> None: + self._classes: dict[str, rdf.RDFSClass] = {} + self._properties: dict[str, rdf.RDFProperty] = {} + self._shapes: dict[str, shacl.SHACLNodeShape] = {} + + def load_schema(self, stream: IO[str]) -> None: + """Reads a schema file and stores the definitions. + + Args: + stream: An open file containing the schema in JSON-LD format. + + Example Input (in the file): + [ + {"@id": "Person", "@type": "Class"}, + {"@id": "name", "@type": "Property"} + ] + """ + resources = jsonld_parser.parse_jsonld(stream) + for resource in resources: + if isinstance(resource, rdf.RDFSClass): + self._classes[resource.id] = resource + elif isinstance(resource, rdf.RDFProperty): + self._properties[resource.id] = resource + elif isinstance(resource, shacl.SHACLNodeShape): + self._shapes[resource.id] = resource + + def get_class(self, class_id: str) -> rdf.RDFSClass | None: + """Finds a Class definition by its ID. + + Args: + class_id: The ID to look up (e.g., "http://schema.org/Person"). + + Returns: + The Class object if found, otherwise None. + """ + return self._classes.get(class_id) + + def get_property(self, property_id: str) -> rdf.RDFProperty | None: + """Finds a Property definition by its ID. + + Args: + property_id: The ID to look up (e.g., "http://schema.org/name"). + + Returns: + The Property object if found, otherwise None. + """ + return self._properties.get(property_id) + + def get_shape_for_class(self, class_id: str) -> shacl.SHACLNodeShape | None: + """Finds the validation rules (Shape) for a specific Class. + + Args: + class_id: The Class ID (e.g., "Person"). + + Returns: + The Shape object if there are rules for this class, otherwise None. + """ + for shape in self._shapes.values(): + if shape.target_class == class_id: + return shape + return None + + def validate_node(self, node: GraphNode) -> None: + """Checks if a single data node follows all the schema rules. + + This does three main checks: + 1. Does the node have a valid type? (e.g., is "Person" a real class?) + 2. Are the properties valid? (e.g., is "age" a real property?) + 3. Does it pass specific rules? (e.g., "age" must be a number) + + Args: + node: The data node to check. + + Raises: + ValidationError: If the node breaks any rules. + + Example: + # If "Person" class requires a "name", this would raise an error: + node = GraphNode(id="bob", type="Person", age=30) + service.validate_node(node) # Raises ValidationError: "Person" must have a "name" + """ + if not node.type: + raise ValidationError(f"Node {node.id} has no type.") + + # Check if the node's type is a valid class + node_types = node.type if isinstance(node.type, list) else [node.type] + for node_type in node_types: + if node_type not in self._classes: + raise ValidationError( + f"Node {node.id} has an invalid type: {node_type}" + ) + + # Check if the node's properties are valid + for prop_name, _ in node.model_dump(by_alias=True, exclude_none=True).items(): + if prop_name not in ["@id", "@type"] and prop_name not in self._properties: + raise ValidationError( + f"Node {node.id} has an invalid property: {prop_name}" + ) + + # SHACL validation + for node_type in node_types: + shape = self.get_shape_for_class(node_type) + if shape: + data_graph = node.model_dump_json(by_alias=True) + schema_graph = self.get_schema_graph_json() + conforms, results_graph, results_text = validate( + data_graph, + shacl_graph=schema_graph, + data_graph_format="json-ld", + shacl_graph_format="json-ld", + ) + if not conforms: + raise ValidationError( + f"Node {node.id} failed SHACL validation:\n{results_text}" + ) + + def get_schema_graph_json(self) -> str: + """Returns the entire schema as a JSON string. + + This is mostly used internally for the validation tool (pyshacl). + """ + graph = [] + for cls in self._classes.values(): + graph.append(cls.model_dump(by_alias=True, exclude_none=True)) + for prop in self._properties.values(): + graph.append(prop.model_dump(by_alias=True, exclude_none=True)) + for shape in self._shapes.values(): + graph.append(shape.model_dump(by_alias=True, exclude_none=True)) + + return json.dumps({"@graph": graph}) diff --git a/packages/datacommons-schema/datacommons_schema/services/schema_service_test.py b/packages/datacommons-schema/datacommons_schema/services/schema_service_test.py new file mode 100644 index 0000000..9aa6c12 --- /dev/null +++ b/packages/datacommons-schema/datacommons_schema/services/schema_service_test.py @@ -0,0 +1,59 @@ +# Copyright 2025 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest +from io import StringIO +from datacommons_schema.models.jsonld import GraphNode +from datacommons_schema.services.schema_service import ( + SchemaService, + ValidationError, +) + +@pytest.fixture +def schema_service(): + service = SchemaService() + schema = """ + { + "@context": { + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#" + }, + "@graph": [ + { + "@id": "Person", + "@type": "rdfs:Class" + }, + { + "@id": "name", + "@type": "rdf:Property" + } + ] + } + """ + service.load_schema(StringIO(schema)) + return service + +def test_validate_node_with_valid_node(schema_service: SchemaService): + node = GraphNode(**{"@id": "test", "@type": "Person", "name": "test"}) + schema_service.validate_node(node) + +def test_validate_node_with_invalid_type(schema_service: SchemaService): + node = GraphNode(**{"@id": "test", "@type": "InvalidType", "name": "test"}) + with pytest.raises(ValidationError): + schema_service.validate_node(node) + +def test_validate_node_with_invalid_property(schema_service: SchemaService): + node = GraphNode(**{"@id": "test", "@type": "Person", "invalid_prop": "test"}) + with pytest.raises(ValidationError): + schema_service.validate_node(node) diff --git a/packages/datacommons-schema/pyproject.toml b/packages/datacommons-schema/pyproject.toml index 02625ce..4b43b2c 100644 --- a/packages/datacommons-schema/pyproject.toml +++ b/packages/datacommons-schema/pyproject.toml @@ -18,7 +18,8 @@ classifiers = [ dependencies = [ "click", "pydantic", - "pytest" + "pytest", + "pyshacl" ] [project.urls] From ca1b4bdec0dd2be680d75f055a45b674500ddca5 Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Thu, 20 Nov 2025 16:54:41 -0800 Subject: [PATCH 07/19] updated lockfile --- uv.lock | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/uv.lock b/uv.lock index ebe3245..cffbfb1 100644 --- a/uv.lock +++ b/uv.lock @@ -205,6 +205,7 @@ source = { editable = "packages/datacommons-schema" } dependencies = [ { name = "click" }, { name = "pydantic" }, + { name = "pyshacl" }, { name = "pytest" }, ] @@ -212,6 +213,7 @@ dependencies = [ requires-dist = [ { name = "click" }, { name = "pydantic" }, + { name = "pyshacl" }, { name = "pytest" }, ] @@ -417,6 +419,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] +[[package]] +name = "html5rdf" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/55/1b839c43f5ed8207e17a9a02d8b395179520b8b4f00c00a41e113bc205ca/html5rdf-1.2.1.tar.gz", hash = "sha256:ace9b420ce52995bb4f05e7425eedf19e433c981dfe7a831ab391e2fa2e1a195", size = 287899, upload-time = "2024-10-30T05:06:56.384Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/c9/f6e1e8567660bc5b0aba281f2b0017b2a7665fcad6bf3ed67286a0c72cd4/html5rdf-1.2.1-py2.py3-none-any.whl", hash = "sha256:1f519121bc366af3e485310dc8041d2e86e5173c1a320fac3dc9d2604069b83e", size = 109765, upload-time = "2024-10-30T05:06:52.507Z" }, +] + [[package]] name = "httptools" version = "0.6.4" @@ -498,6 +509,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, ] +[[package]] +name = "owlrl" +version = "7.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "rdflib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/fc/ce12482d096d65fff01af58f555a6f25e9dbf416fad5d99f91eaab0e11ca/owlrl-7.1.4.tar.gz", hash = "sha256:60bd4067e346b9111f0a2924565afe97ac6595b98b2bbe953928b5113971daf7", size = 44420, upload-time = "2025-07-29T00:17:27.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/78/f857ff1a7207e967dc5e8414bbcc15e0aa5cf45f693b1d2ebe2afb3eb1ce/owlrl-7.1.4-py3-none-any.whl", hash = "sha256:e78b46020169783345636da93a467d318f18700c483184dd15e885850cf64775", size = 51981, upload-time = "2025-07-29T00:17:26.229Z" }, +] + [[package]] name = "packaging" version = "25.0" @@ -541,6 +564,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" }, ] +[[package]] +name = "prettytable" +version = "3.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/45/b0847d88d6cfeb4413566738c8bbf1e1995fad3d42515327ff32cc1eb578/prettytable-3.17.0.tar.gz", hash = "sha256:59f2590776527f3c9e8cf9fe7b66dd215837cca96a9c39567414cbc632e8ddb0", size = 67892, upload-time = "2025-11-14T17:33:20.212Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl", hash = "sha256:aad69b294ddbe3e1f95ef8886a060ed1666a0b83018bbf56295f6f226c43d287", size = 34433, upload-time = "2025-11-14T17:33:19.093Z" }, +] + [[package]] name = "proto-plus" version = "1.26.1" @@ -637,6 +672,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] +[[package]] +name = "pyparsing" +version = "3.2.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" }, +] + +[[package]] +name = "pyshacl" +version = "0.30.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "owlrl" }, + { name = "packaging" }, + { name = "prettytable" }, + { name = "rdflib", extra = ["html"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/32/1602c3f4ab567b6d020bdd630c348fcf25d899f9e2b7a50385b10761673c/pyshacl-0.30.1.tar.gz", hash = "sha256:df712ac961b1ee7bcf0b27fa71ecab7785a135b2af69783ce30cb05fe41bcaf5", size = 1392238, upload-time = "2025-03-14T23:28:15.702Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/12/3747eff45147d416c054881b7c0902b21ac7767b905f666c714233688c81/pyshacl-0.30.1-py3-none-any.whl", hash = "sha256:d7e0c21b25e948bb643dbc5db6258da64a90a8ac89055c1fe562b469031072aa", size = 1290522, upload-time = "2025-03-14T23:28:12.72Z" }, +] + [[package]] name = "pytest" version = "8.4.1" @@ -693,6 +752,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, ] +[[package]] +name = "rdflib" +version = "7.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/ea/30bd9eb0d4a25dd0ab929153ed23698c907c6124389aa72eea5b7b703ab8/rdflib-7.4.0.tar.gz", hash = "sha256:c8ee16c31848c19c174aed96185327ea139ca3d392fac7fa882ddf5687f8f533", size = 4866588, upload-time = "2025-10-30T12:55:21.568Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/52/9d03e93f2e00d2a07749ee90f358d08c07822819d084f08c387b7ade8b56/rdflib-7.4.0-py3-none-any.whl", hash = "sha256:0af003470404ff21bc0eb04077cc97ee96da581f2429bf42a8e163fc1c2797bc", size = 569019, upload-time = "2025-10-30T12:55:14.462Z" }, +] + +[package.optional-dependencies] +html = [ + { name = "html5rdf" }, +] + [[package]] name = "requests" version = "2.32.4" @@ -926,6 +1002,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343, upload-time = "2025-06-15T19:05:37.5Z" }, ] +[[package]] +name = "wcwidth" +version = "0.2.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293, upload-time = "2025-09-22T16:29:53.023Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286, upload-time = "2025-09-22T16:29:51.641Z" }, +] + [[package]] name = "websockets" version = "15.0.1" From 2c25b957041436a181819b37a9d6a62e74ceae5b Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Mon, 26 Jan 2026 12:55:55 -0800 Subject: [PATCH 08/19] Added initial schema validation service and in memory knowledge graph utilities --- packages/datacommons-schema/README.md | 166 ++++- .../datacommons_schema/SCHEMA.md | 155 ++++ .../datacommons_schema/knowledge_graph.py | 71 ++ .../services/schema_service.py | 219 ------ .../services/schema_service_test.py | 59 -- .../services/schema_validation_service.py | 418 +++++++++++ .../tests/test_knowledge_graph.py | 177 +++++ .../schema_validation_service_test.py | 217 ++++++ packages/datacommons-schema/pyproject.toml | 6 +- uv.lock | 696 +++++++++--------- 10 files changed, 1551 insertions(+), 633 deletions(-) create mode 100644 packages/datacommons-schema/datacommons_schema/SCHEMA.md create mode 100644 packages/datacommons-schema/datacommons_schema/knowledge_graph.py delete mode 100644 packages/datacommons-schema/datacommons_schema/services/schema_service.py delete mode 100644 packages/datacommons-schema/datacommons_schema/services/schema_service_test.py create mode 100644 packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py create mode 100644 packages/datacommons-schema/datacommons_schema/tests/test_knowledge_graph.py create mode 100644 packages/datacommons-schema/datacommons_schema/tests/validator/schema_validation_service_test.py diff --git a/packages/datacommons-schema/README.md b/packages/datacommons-schema/README.md index 9bce043..350b096 100644 --- a/packages/datacommons-schema/README.md +++ b/packages/datacommons-schema/README.md @@ -1,29 +1,179 @@ -# Data Commons Schema Tools +# datacommons-schema -This module provides command-line utilities and libraries for working with Data Commons schema formats, particularly focusing on conversion between MCF (Machine-Readable Common Format) and JSON-LD formats. +This module provides library methods for validating and working with Data Commons schema. It supports validation of JSON-LD and MCF files, and conversion between the two formats. ## Features -- MCF to JSON-LD conversion - Schema validation and parsing - Namespace management +- MCF to JSON-LD conversion - Compact and expanded JSON-LD output options + +## Schema Validation + +### Core Concepts + +#### 1. Knowledge Graph (KG) +The Knowledge Graph is the central repository for all schema and data nodes. +- **Single Namespace**: The KG operates under a defined namespace (e.g., `https://knowledge-graph.example.org/`). +- **Unified Storage**: Stores both "Schema" (Classes, Properties) and "Data" (Instances) as nodes in the graph. +- **Strict Validation**: No data can be added to the KG without passing validation checks. + +#### 2. Schema Validator +The Validator ensures that any data entering the KG conforms to: +- **Standard Primitives**: [RDF](http://www.w3.org/2000/01/rdf-schema#), [RDFS](http://www.w3.org/2000/01/rdf-schema#), and [XSD](http://www.w3.org/2001/XMLSchema#) types. +- **Custom Schema rules**: Domain, Range, and Class existence checks defined within the KG itself. + +--- + +### Class Diagram + +```mermaid +classDiagram + + class KnowledgeGraph { + +str namespace + +str default_prefix + -rdflib.Graph _graph + -SchemaValidator _validator + +validate(nodes: List[Dict]) Report + +add(nodes: List[Dict]) void + } + + class SchemaValidator { + +validate_node(node, context_graph) + } + + KnowledgeGraph --> SchemaValidator : uses +``` + +## Component Design + +### 1. `KnowledgeGraph` +Knowledge graph implementation using `rdflib.Graph` in memory. + +**Storage:** +- Uses an instance of `rdflib.Graph` to store all triples. + +**Attributes:** +- `namespace`: The base URI for the KG. +- `default_prefix`: The default prefix label (e.g., "ex") that maps to the KG's namespace. + +**Methods:** +- `validate(nodes: Union[Dict, List[Dict]]) -> ValidationReport` + - Checks if the input JSON-LD nodes are valid against the *current* state of the KG. + - Does *not* modify the graph. +- `add(nodes: Union[Dict, List[Dict]]) -> None` + - First calls `validate()`. + - If valid, inserts the nodes into the underlying storage. + - Raises `ValueError` or custom exception if validation fails. + +**Logic:** +- **Add**: + 1. Parse input JSON-LD into a temporary graph. + 2. Run validation against the *combined* knowledge (Current Graph + New Data). + - *Note*: Validation often requires checking if a referenced Class exists. If we are adding a new Class *and* an instance of it simultaneously, the validator must verify them together. + 3. If valid, merge temporary graph into main `_graph`. + +### 3. `SchemaValidationService` (The Validator) +Responsible for the core logic of checking RDF/RDFS/XSD constraints. + +**Capabilities:** +- **Primitive Checks**: + - Ensures `rdf:`, `rdfs:`, `xsd:` terms are known and valid (e.g., rejects `rdf:SomeInvalidProperty`). +- **Integrity Checks (Schema)**: + - **Classes**: Referenced types must exist (e.g., `@type: "ex:Person"` requires `ex:Person` to be defined as `rdfs:Class`). + - **Properties**: Referenced predicates must exist (e.g., `"ex:age": 30` requires `ex:age` to be defined as `rdf:Property`). + - **Domains**: Subject must match the property's `rdfs:domain`. + - **Ranges**: Object must match the property's `rdfs:range` (either a Class or XSD datatype). + +## API & Usage Specification + +### Initialization +```python +from datacommons_schema.knowledge_graph import KnowledgeGraph + +# Initialize an empty KG with a specific namespace +kg = KnowledgeGraph(namespace="http://example.org/") +``` + +### Adding Schema +Schema is just data. You add it like any other node. +```python +schema_definition = { + "@context": { + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "ex": "http://example.org/" + }, + "@graph": [ + {"@id": "ex:Person", "@type": "rdfs:Class"}, + {"@id": "ex:name", "@type": "rdf:Property", "rdfs:domain": {"@id": "ex:Person"}, "rdfs:range": {"@id": "xsd:string"}} + ] +} + +# Validates that 'rdfs:Class' is a known primitive. +# Validates that 'xsd:string' is a known primitive. +kg.add(schema_definition) +``` + +### Adding Data +```python +person_node = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Alice", + "@type": "ex:Person", + "ex:name": "Alice" +} + +# 1. Checks if 'ex:Person' exists in KG (It was added above). +# 2. Checks if 'ex:name' exists in KG. +# 3. Checks if 'ex:Alice' satisfies domain of 'ex:name' (ex:Person). +# 4. Checks if "Alice" satisfies range of 'ex:name' (xsd:string). +kg.add(person_node) +``` + +### Validation Failure Example +```python +invalid_node = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Bob", + "ex:unknownProp": "Value" +} + +# Should raise ValidationException: +# "Property 'ex:unknownProp' is not defined in the Knowledge Graph." +kg.add(invalid_node) +``` + +## Implementation Roadmap + +1. **Refactor `SchemaValidationService`**: + * Decouple it from strictly taking a static schema in `__init__`. + * Allow it to accept a "Knowledge Store" interface or lookup function to check for existence of terms during validation. +2. **Implement `KnowledgeGraph` ABC**: + * Define the interface. +3. **Implement `KnowledgeGraph`**: + * Wire up `rdflib` and the Validator. + + ## Command Line Utilities ### MCF to JSON-LD Converter -The `mcf2jsonld` command converts MCF files to JSON-LD format, with support for custom namespaces and output formatting. +The `datacommons-schema mcf2jsonld` command converts MCF files to JSON-LD format, with support for custom namespaces and output formatting. ```bash # Basic usage -datacommons mcf2jsonld input.mcf +datacommons-schema mcf2jsonld input.mcf # With custom namespace datacommons mcf2jsonld input.mcf --namespace "schema:https://schema.org/" # Output to file with compact format -datacommons mcf2jsonld input.mcf -o output.jsonld -c +datacommons-schema mcf2jsonld input.mcf -o output.jsonld -c ``` #### Options @@ -100,3 +250,7 @@ When contributing to this module: ## License [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) + + + + diff --git a/packages/datacommons-schema/datacommons_schema/SCHEMA.md b/packages/datacommons-schema/datacommons_schema/SCHEMA.md new file mode 100644 index 0000000..767978d --- /dev/null +++ b/packages/datacommons-schema/datacommons_schema/SCHEMA.md @@ -0,0 +1,155 @@ +# Data Commons Knowledge Graph & Schema Validator Design + +## Overview +This document outlines the design for the Data Commons Knowledge Graph (KG) and Schema Validator. The system is designed to provide a flexible, validated graph storage mechanism that ensures data integrity based on Semantic Web standards (RDF, RDFS) and custom schema definitions. + +## Core Concepts + +### 1. Knowledge Graph (KG) +The Knowledge Graph is the central repository for all schema and data nodes. +- **Single Namespace**: The KG operates under a defined namespace (e.g., `https://knowledge-graph.example.org/`). +- **Unified Storage**: Stores both "Schema" (Classes, Properties) and "Data" (Instances) as nodes in the graph. +- **Strict Validation**: No data can be added to the KG without passing validation checks. + +### 2. Schema Validator +The Validator ensures that any data entering the KG conforms to: +- **Standard Primitives**: RDF, RDFS, and XSD types. +- **Custom Schema rules**: Domain, Range, and Class existence checks defined within the KG itself. + +--- + +## Architecture + +### Class Diagram + +```mermaid +classDiagram + + class KnowledgeGraph { + +str namespace + +str default_prefix + -rdflib.Graph _graph + -SchemaValidator _validator + +validate(nodes: List[Dict]) Report + +add(nodes: List[Dict]) void + } + + class SchemaValidator { + +validate_node(node, context_graph) + } + + KnowledgeGraph --> SchemaValidator : uses +``` + +## Component Design + +### 1. `KnowledgeGraph` (Abstract Base Class) +Defines the contract for all KG implementations. + +**Attributes:** +- `namespace`: The base URI for the KG. +- `default_prefix`: The default prefix label (e.g., "ex") that maps to the KG's namespace. + +**Methods:** +- `validate(nodes: Union[Dict, List[Dict]]) -> ValidationReport` + - Checks if the input JSON-LD nodes are valid against the *current* state of the KG. + - Does *not* modify the graph. +- `add(nodes: Union[Dict, List[Dict]]) -> None` + - First calls `validate()`. + - If valid, inserts the nodes into the underlying storage. + - Raises `ValueError` or custom exception if validation fails. + +### 2. `KnowledgeGraph` (Implementation) +A reference implementation using `rdflib.Graph` in memory. + +**Storage:** +- Uses an instance of `rdflib.Graph` to store all triples. + +**Logic:** +- **Add**: + 1. Parse input JSON-LD into a temporary graph. + 2. Run validation against the *combined* knowledge (Current Graph + New Data). + - *Note*: Validation often requires checking if a referenced Class exists. If we are adding a new Class *and* an instance of it simultaneously, the validator must verify them together. + 3. If valid, merge temporary graph into main `_graph`. + +### 3. `SchemaValidationService` (The Validator) +Responsible for the core logic of checking RDF/RDFS/XSD constraints. + +**Capabilities:** +- **Primitive Checks**: + - Ensures `rdf:`, `rdfs:`, `xsd:` terms are known and valid (e.g., rejects `rdf:SomeInvalidProperty`). +- **Integrity Checks (Schema)**: + - **Classes**: Referenced types must exist (e.g., `@type: "ex:Person"` requires `ex:Person` to be defined as `rdfs:Class`). + - **Properties**: Referenced predicates must exist (e.g., `"ex:age": 30` requires `ex:age` to be defined as `rdf:Property`). + - **Domains**: Subject must match the property's `rdfs:domain`. + - **Ranges**: Object must match the property's `rdfs:range` (either a Class or XSD datatype). + +## API & Usage Specification + +### Initialization +```python +from datacommons_schema.knowledge_graph import KnowledgeGraph + +# Initialize an empty KG with a specific namespace +kg = KnowledgeGraph(namespace="http://example.org/") +``` + +### Adding Schema +Schema is just data. You add it like any other node. +```python +schema_definition = { + "@context": { + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "ex": "http://example.org/" + }, + "@graph": [ + {"@id": "ex:Person", "@type": "rdfs:Class"}, + {"@id": "ex:name", "@type": "rdf:Property", "rdfs:domain": {"@id": "ex:Person"}, "rdfs:range": {"@id": "xsd:string"}} + ] +} + +# Validates that 'rdfs:Class' is a known primitive. +# Validates that 'xsd:string' is a known primitive. +kg.add(schema_definition) +``` + +### Adding Data +```python +person_node = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Alice", + "@type": "ex:Person", + "ex:name": "Alice" +} + +# 1. Checks if 'ex:Person' exists in KG (It was added above). +# 2. Checks if 'ex:name' exists in KG. +# 3. Checks if 'ex:Alice' satisfies domain of 'ex:name' (ex:Person). +# 4. Checks if "Alice" satisfies range of 'ex:name' (xsd:string). +kg.add(person_node) +``` + +### Validation Failure Example +```python +invalid_node = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Bob", + "ex:unknownProp": "Value" +} + +# Should raise ValidationException: +# "Property 'ex:unknownProp' is not defined in the Knowledge Graph." +kg.add(invalid_node) +``` + +## Implementation Roadmap + +1. **Refactor `SchemaValidationService`**: + * Decouple it from strictly taking a static schema in `__init__`. + * Allow it to accept a "Knowledge Store" interface or lookup function to check for existence of terms during validation. +2. **Implement `KnowledgeGraph` ABC**: + * Define the interface. +3. **Implement `KnowledgeGraph`**: + * Wire up `rdflib` and the Validator. diff --git a/packages/datacommons-schema/datacommons_schema/knowledge_graph.py b/packages/datacommons-schema/datacommons_schema/knowledge_graph.py new file mode 100644 index 0000000..4f63333 --- /dev/null +++ b/packages/datacommons-schema/datacommons_schema/knowledge_graph.py @@ -0,0 +1,71 @@ +from typing import List, Dict, Union, Optional +from rdflib import Graph +import json + +from datacommons_schema.services.schema_validation_service import SchemaValidationService, SchemaReport, ValidationReport, ValidationError + + +class KnowledgeGraph: + """ + An in-memory Knowledge Graph using rdflib. + """ + def __init__(self, namespace: str, default_prefix: str = "ex"): + self.namespace = namespace + self.default_prefix = default_prefix + self._graph = Graph() + # Bind the default prefix to the namespace + self._graph.bind(self.default_prefix, self.namespace) + + def validate(self, nodes: Union[Dict, List[Dict]]) -> ValidationReport: + # 1. Parse new nodes into a temp graph + temp_graph = self._load_graph(nodes) + + # 2. extract existing rules to serve as context + # Note: In a real high-perf scenario, we would cache the 'rules' + # instead of re-extracting them from self._graph every time. + main_validator = SchemaValidationService(self._graph) + + # 3. Check Schema Integrity of NEW nodes + # Use existing classes as context so we don't flag references to existing classes as "Undefined" + temp_validator = SchemaValidationService(temp_graph) + schema_report = temp_validator.validate_schema_integrity(context_classes=main_validator.rules.classes) + + if not schema_report.is_valid: + # Map schema errors to validation errors + schem_errors = [] + for se in schema_report.errors: + schem_errors.append(ValidationError( + subject=se.subject, + predicate="N/A", + object="N/A", + message=f"Schema Integrity Error: {se.issue} - {se.message}", + rule_type="SchemaIntegrity" + )) + return ValidationReport( + is_valid=False, + error_count=len(schem_errors), + errors=schem_errors + ) + + # 4. Check Data Validation + # Validates 'temp_graph' against 'self._graph' rules and context + return main_validator.validate(temp_graph, context_graph=self._graph) + + def add(self, nodes: Union[Dict, List[Dict]]) -> None: + report = self.validate(nodes) + if not report.is_valid: + error_msgs = "\n".join([f"{e.subject}: {e.message}" for e in report.errors]) + raise ValueError(f"Cannot add invalid nodes:\n{error_msgs}") + + # If valid, merge + temp_graph = self._load_graph(nodes) + self._graph += temp_graph + + def _load_graph(self, jsonld_input: Union[Dict, List[Dict], str]) -> Graph: + g = Graph() + if isinstance(jsonld_input, (dict, list)): + data = json.dumps(jsonld_input) + else: + data = jsonld_input + g.parse(data=data, format="json-ld") + return g diff --git a/packages/datacommons-schema/datacommons_schema/services/schema_service.py b/packages/datacommons-schema/datacommons_schema/services/schema_service.py deleted file mode 100644 index 4b75fcb..0000000 --- a/packages/datacommons-schema/datacommons_schema/services/schema_service.py +++ /dev/null @@ -1,219 +0,0 @@ -# Copyright 2025 Google LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Schema Service Module. - -This module handles the "rules" (schema) for Data Commons. It loads definitions of -what things are (Classes), what properties they can have (Properties), and rules -for validation (Shapes). - -Example: - service = SchemaService() - with open("my_schema.jsonld") as f: - service.load_schema(f) - - # Now you can check if data is valid - service.validate_node(my_data_node) -""" - -import json -from typing import IO - -from datacommons_schema.models.jsonld import GraphNode -from datacommons_schema.models.primitives import rdf, shacl -from datacommons_schema.parsers import jsonld_parser -from pyshacl import validate - - -class ValidationError(Exception): - """Raised when data doesn't match the schema rules.""" - - -class SchemaService: - """A service for defining and validating semantic data. - - This service defines classes, properties, and objects, and constraints - using RDF Graphs and SHACL. It manages definitions for: - - Classes (e.g., "Person", "City") - - Properties (e.g., "name", "population") - - Shapes (Validation rules, e.g., "A Person must have a name") - - Example: - from io import StringIO - from datacommons_schema.models.jsonld import GraphNode - - service = SchemaService() - - # Define a simple Person class and a shape for it - schema_jsonld = \"\"\"{ - "@context": { - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "rdfs": "http://www.w3.org/2000/01/rdf-schema#" - }, - "@graph": [ - { - "@id": "Person", - "@type": "rdfs:Class" - }, - { - "@id": "name", - "@type": "rdf:Property" - } - ] - }\"\"\" - service.load_schema(StringIO(schema_jsonld)) - - # Create a valid person node - valid_person_node = GraphNode(id="john_doe", type="Person", properties={"name": "John Doe"}) - is_valid, report = service.validate_node(valid_person_node) - print(f"Valid person node is valid: {is_valid}") # Expected: True - - # Create an invalid person node (missing name) - invalid_person_node = GraphNode(id="jane_doe", type="Person") - is_valid, report = service.validate_node(invalid_person_node) - print(f"Invalid person node is valid: {is_valid}") # Expected: False - # print(report) # Uncomment to see the validation details - """ - - def __init__(self) -> None: - self._classes: dict[str, rdf.RDFSClass] = {} - self._properties: dict[str, rdf.RDFProperty] = {} - self._shapes: dict[str, shacl.SHACLNodeShape] = {} - - def load_schema(self, stream: IO[str]) -> None: - """Reads a schema file and stores the definitions. - - Args: - stream: An open file containing the schema in JSON-LD format. - - Example Input (in the file): - [ - {"@id": "Person", "@type": "Class"}, - {"@id": "name", "@type": "Property"} - ] - """ - resources = jsonld_parser.parse_jsonld(stream) - for resource in resources: - if isinstance(resource, rdf.RDFSClass): - self._classes[resource.id] = resource - elif isinstance(resource, rdf.RDFProperty): - self._properties[resource.id] = resource - elif isinstance(resource, shacl.SHACLNodeShape): - self._shapes[resource.id] = resource - - def get_class(self, class_id: str) -> rdf.RDFSClass | None: - """Finds a Class definition by its ID. - - Args: - class_id: The ID to look up (e.g., "http://schema.org/Person"). - - Returns: - The Class object if found, otherwise None. - """ - return self._classes.get(class_id) - - def get_property(self, property_id: str) -> rdf.RDFProperty | None: - """Finds a Property definition by its ID. - - Args: - property_id: The ID to look up (e.g., "http://schema.org/name"). - - Returns: - The Property object if found, otherwise None. - """ - return self._properties.get(property_id) - - def get_shape_for_class(self, class_id: str) -> shacl.SHACLNodeShape | None: - """Finds the validation rules (Shape) for a specific Class. - - Args: - class_id: The Class ID (e.g., "Person"). - - Returns: - The Shape object if there are rules for this class, otherwise None. - """ - for shape in self._shapes.values(): - if shape.target_class == class_id: - return shape - return None - - def validate_node(self, node: GraphNode) -> None: - """Checks if a single data node follows all the schema rules. - - This does three main checks: - 1. Does the node have a valid type? (e.g., is "Person" a real class?) - 2. Are the properties valid? (e.g., is "age" a real property?) - 3. Does it pass specific rules? (e.g., "age" must be a number) - - Args: - node: The data node to check. - - Raises: - ValidationError: If the node breaks any rules. - - Example: - # If "Person" class requires a "name", this would raise an error: - node = GraphNode(id="bob", type="Person", age=30) - service.validate_node(node) # Raises ValidationError: "Person" must have a "name" - """ - if not node.type: - raise ValidationError(f"Node {node.id} has no type.") - - # Check if the node's type is a valid class - node_types = node.type if isinstance(node.type, list) else [node.type] - for node_type in node_types: - if node_type not in self._classes: - raise ValidationError( - f"Node {node.id} has an invalid type: {node_type}" - ) - - # Check if the node's properties are valid - for prop_name, _ in node.model_dump(by_alias=True, exclude_none=True).items(): - if prop_name not in ["@id", "@type"] and prop_name not in self._properties: - raise ValidationError( - f"Node {node.id} has an invalid property: {prop_name}" - ) - - # SHACL validation - for node_type in node_types: - shape = self.get_shape_for_class(node_type) - if shape: - data_graph = node.model_dump_json(by_alias=True) - schema_graph = self.get_schema_graph_json() - conforms, results_graph, results_text = validate( - data_graph, - shacl_graph=schema_graph, - data_graph_format="json-ld", - shacl_graph_format="json-ld", - ) - if not conforms: - raise ValidationError( - f"Node {node.id} failed SHACL validation:\n{results_text}" - ) - - def get_schema_graph_json(self) -> str: - """Returns the entire schema as a JSON string. - - This is mostly used internally for the validation tool (pyshacl). - """ - graph = [] - for cls in self._classes.values(): - graph.append(cls.model_dump(by_alias=True, exclude_none=True)) - for prop in self._properties.values(): - graph.append(prop.model_dump(by_alias=True, exclude_none=True)) - for shape in self._shapes.values(): - graph.append(shape.model_dump(by_alias=True, exclude_none=True)) - - return json.dumps({"@graph": graph}) diff --git a/packages/datacommons-schema/datacommons_schema/services/schema_service_test.py b/packages/datacommons-schema/datacommons_schema/services/schema_service_test.py deleted file mode 100644 index 9aa6c12..0000000 --- a/packages/datacommons-schema/datacommons_schema/services/schema_service_test.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2025 Google LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest -from io import StringIO -from datacommons_schema.models.jsonld import GraphNode -from datacommons_schema.services.schema_service import ( - SchemaService, - ValidationError, -) - -@pytest.fixture -def schema_service(): - service = SchemaService() - schema = """ - { - "@context": { - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "rdfs": "http://www.w3.org/2000/01/rdf-schema#" - }, - "@graph": [ - { - "@id": "Person", - "@type": "rdfs:Class" - }, - { - "@id": "name", - "@type": "rdf:Property" - } - ] - } - """ - service.load_schema(StringIO(schema)) - return service - -def test_validate_node_with_valid_node(schema_service: SchemaService): - node = GraphNode(**{"@id": "test", "@type": "Person", "name": "test"}) - schema_service.validate_node(node) - -def test_validate_node_with_invalid_type(schema_service: SchemaService): - node = GraphNode(**{"@id": "test", "@type": "InvalidType", "name": "test"}) - with pytest.raises(ValidationError): - schema_service.validate_node(node) - -def test_validate_node_with_invalid_property(schema_service: SchemaService): - node = GraphNode(**{"@id": "test", "@type": "Person", "invalid_prop": "test"}) - with pytest.raises(ValidationError): - schema_service.validate_node(node) diff --git a/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py b/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py new file mode 100644 index 0000000..6ea8609 --- /dev/null +++ b/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py @@ -0,0 +1,418 @@ +# Copyright 2025 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +from typing import List, Dict, Set, Any, Union, Optional +from pydantic import BaseModel, Field, ConfigDict +from rdflib import Graph, URIRef, Literal, RDF, RDFS, XSD + +class SchemaError(BaseModel): + subject: str # The property causing the issue (e.g., local:name) + issue: str # e.g., "Dangling Reference" + message: str # e.g., "Refers to class local:Ghost which is undefined" + +class SchemaReport(BaseModel): + is_valid: bool + errors: List[SchemaError] + +class ValidationError(BaseModel): + """Represents a single issue found in the data.""" + subject: str + predicate: str + object: str + message: str + rule_type: str = Field(..., description="The type of rule violated (e.g., 'Domain', 'Range', 'Undefined Property')") + +class ValidationReport(BaseModel): + """The final output of a validation run.""" + is_valid: bool + error_count: int + errors: List[ValidationError] = [] + +class SchemaDefinition(BaseModel): + """ + Internal storage for the rules extracted from the RDFS schema. + We allow arbitrary types to store rdflib objects (URIRef) directly for performance. + """ + model_config = ConfigDict(arbitrary_types_allowed=True) + + classes: Set[URIRef] = Field(default_factory=set) + properties: Set[URIRef] = Field(default_factory=set) + domains: Dict[URIRef, URIRef] = Field(default_factory=dict) # Property -> Class + ranges: Dict[URIRef, URIRef] = Field(default_factory=dict) # Property -> Class/Datatype + +class SchemaValidationService: + """ + A strict JSON-LD RDFS-based validator for ensuring data conformity against a defined schema. + + This validator operates on a 'Closed World' assumption for domain and range checks: + any Class or Property referenced in the data must be explicitly defined in the provided schema. + + Attributes: + schema_graph (rdflib.Graph): The raw RDF graph of the schema. + rules (SchemaDefinition): An internal lookup table of classes, properties, domains, and ranges. + + Examples: + >>> schema = { + ... "@context": {"local": "http://example.org/", "rdfs": "http://www.w3.org/2000/01/rdf-schema#"}, + ... "@graph": [ + ... {"@id": "local:Person", "@type": "rdfs:Class"}, + ... {"@id": "local:name", "@type": "rdf:Property", "rdfs:domain": {"@id": "local:Person"}} + ... ] + ... } + >>> validator = SchemaValidationService(schema) + >>> report = validator.validate_schema_integrity() + >>> print(report.is_valid) + True + """ + + def __init__(self, schema_input: Union[str, Dict[str, Any]]): + """ + Initializes the validator by parsing the schema and extracting validation rules. + + Args: + schema_input (Union[str, Dict[str, Any]]): The schema definition. Can be a + raw JSON-LD string or a pre-parsed dictionary. + + Example: + + >>> schema_input = """ + { + "@context": { + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#" + }, + "@graph": [ + { + "@id": "Person", + "@type": "rdfs:Class" + }, + { + "@id": "name", + "@type": "rdf:Property" + } + ] + } + """ + >>> validator = SchemaValidator(schema_input) + + Raises: + json.JSONDecodeError: If the input string is not valid JSON. + rdflib.plugin.PluginException: If the input format is not recognized as JSON-LD. + """ + self.schema_graph = self._load_graph(schema_input) + self.rules = self._extract_rules() + + def _load_graph(self, jsonld_input: Union[str, Dict[str, Any], Graph]) -> Graph: + """ + Parses raw JSON-LD input into an rdflib Graph. + + This helper handles both dictionary and string inputs, ensuring consistent + graph instantiation regardless of the source format. + + Args: + jsonld_input (Union[str, Dict[str, Any]]): The JSON-LD content. + + Returns: + rdflib.Graph: An in-memory RDF graph containing the parsed triples. + """ + if isinstance(jsonld_input, Graph): + return jsonld_input + + g = Graph() + data = json.dumps(jsonld_input) if isinstance(jsonld_input, dict) else jsonld_input + g.parse(data=data, format="json-ld") + return g + + def _extract_rules(self) -> SchemaDefinition: + """ + Compiles high-level validation rules from the raw schema graph. + + This method scans the schema for: + 1. All defined `rdfs:Class` entities. + 2. All `rdf:Property` entities. + 3. `rdfs:domain` and `rdfs:range` constraints for each property. + + Returns: + SchemaDefinition: A frozen dataclass containing sets of valid classes/properties + and dictionaries mapping properties to their domain/range constraints. + """ + rules = SchemaDefinition() + + # 1. Extract Classes + for s, _, _ in self.schema_graph.triples((None, RDF.type, RDFS.Class)): + rules.classes.add(s) + + # 2. Extract Properties and their Domain/Range + for s, _, _ in self.schema_graph.triples((None, RDF.type, RDF.Property)): + rules.properties.add(s) + + # Domain (Subject Type) + domain = self.schema_graph.value(s, RDFS.domain) + if domain: + rules.domains[s] = domain + + # Range (Object Type) + range_type = self.schema_graph.value(s, RDFS.range) + if range_type: + rules.ranges[s] = range_type + + return rules + + def validate_schema_integrity(self, context_classes: Optional[Set[URIRef]] = None) -> SchemaReport: + """ + Performs a self-check on the schema to identify internal inconsistencies. + + This method detects 'Dangling References'—instances where a property defines a + domain or range that points to a class not defined within the schema itself + (or standard XSD types). This enforces a strict schema definition where all + dependencies must be present. + + Args: + context_classes (Optional[Set[URIRef]]): A set of classes known to exist externally + (e.g., in the main Knowledge Graph). This allows validates partial schema updates + that depend on existing classes. + + Returns: + SchemaReport: A Pydantic model containing a boolean validity flag and a list + of specific `SchemaError` objects if inconsistencies are found. + + Examples: + >>> # Schema referencing undefined 'local:Ghost' class + >>> bad_schema = { + ... "@graph": [{ + ... "@id": "local:prop", + ... "@type": "rdf:Property", + ... "rdfs:domain": {"@id": "local:Ghost"} + ... }] + ... } + >>> v = SchemaValidationService(bad_schema) + >>> report = v.validate_schema_integrity() + >>> print(report.errors[0].issue) + Undefined Domain Target + """ + errors = [] + + # Standard XSD types we always accept as valid ranges + known_types = { + XSD.string, XSD.integer, XSD.float, XSD.boolean, + XSD.date, XSD.dateTime, RDFS.Literal + } + + # Combine internal classes with external context + valid_classes = self.rules.classes.copy() + if context_classes: + valid_classes.update(context_classes) + + # 1. Check Domains + # Every domain target must be a known Class in our schema + for prop, domain_class in self.rules.domains.items(): + if domain_class not in valid_classes: + errors.append(SchemaError( + subject=str(prop), + issue="Undefined Domain Target", + message=f"Property defines domain as <{domain_class}>, but that Class is not defined." + )) + + # 2. Check Ranges + # Every range target must be a known Class OR a standard XSD type + for prop, range_target in self.rules.ranges.items(): + is_defined_class = range_target in valid_classes + is_standard_type = range_target in known_types + + if not (is_defined_class or is_standard_type): + errors.append(SchemaError( + subject=str(prop), + issue="Undefined Range Target", + message=f"Property defines range as <{range_target}>, but that is not a defined Class or XSD type." + )) + + # 3. Check for Malformed URIs (Unexpanded CURIEs) + # If a prefix is missing in @context, terms like "rdf:Property" are parsed as URIs with scheme "rdf". + # We enforce that all URIs must use standard schemes (http, https, urn). + from urllib.parse import urlparse + + allowed_schemes = {"http", "https", "urn"} + + # We scan all unique URIs in the graph (subjects, predicates, objects) + all_uris = set() + for s, p, o in self.schema_graph: + if isinstance(s, URIRef): all_uris.add(s) + if isinstance(p, URIRef): all_uris.add(p) + if isinstance(o, URIRef): all_uris.add(o) + + for uri in all_uris: + parsed = urlparse(str(uri)) + if parsed.scheme not in allowed_schemes: + errors.append(SchemaError( + subject=str(uri), + issue="Malformed URI / Missing Prefix", + message=f"Term <{uri}> has unknown scheme '{parsed.scheme}'. This usually means a prefix (like '{parsed.scheme}:') is missing from @context." + )) + + # 4. Check for Unknown RDF/RDFS Terms + # Ensure that we are not using made-up terms in the RDF/RDFS namespaces (e.g. rdf:Propertyzzz) + rdf_ns = str(RDF) + rdfs_ns = str(RDFS) + + # Strict allowlist for this validator + valid_rdf_terms = { + "type", "Property", "List", "first", "rest", "nil", "Statement", "subject", "predicate", "object", "value" + } + valid_rdfs_terms = { + "Class", "subClassOf", "subPropertyOf", "domain", "range", "label", "comment", + "seeAlso", "isDefinedBy", "Literal", "Datatype", "Resource", "Container", "Member" + } + + for uri in all_uris: + uri_str = str(uri) + if uri_str.startswith(rdf_ns): + term = uri_str[len(rdf_ns):] + if term not in valid_rdf_terms: + errors.append(SchemaError( + subject=str(uri), + issue="Unknown RDF Term", + message=f"Term <{uri}> is not a recognized RDF term." + )) + elif uri_str.startswith(rdfs_ns): + term = uri_str[len(rdfs_ns):] + if term not in valid_rdfs_terms: + errors.append(SchemaError( + subject=str(uri), + issue="Unknown RDFS Term", + message=f"Term <{uri}> is not a recognized RDFS term." + )) + + # 5. Check for Unknown XSD Terms + xsd_ns = str(XSD) + valid_xsd_terms = { + "string", "boolean", "decimal", "float", "double", "duration", "dateTime", "time", "date", + "gYearMonth", "gYear", "gMonthDay", "gDay", "gMonth", "hexBinary", "base64Binary", + "anyURI", "QName", "NOTATION", "normalizedString", "token", "language", "NMTOKEN", + "NMTOKENS", "Name", "NCName", "ID", "IDREF", "IDREFS", "ENTITY", "ENTITIES", + "integer", "nonPositiveInteger", "negativeInteger", "long", "int", "short", "byte", + "nonNegativeInteger", "unsignedLong", "unsignedInt", "unsignedShort", "unsignedByte", + "positiveInteger", "yearMonthDuration", "dayTimeDuration", "dateTimeStamp" + } + + for uri in all_uris: + uri_str = str(uri) + if uri_str.startswith(xsd_ns): + term = uri_str[len(xsd_ns):] + if term not in valid_xsd_terms: + errors.append(SchemaError( + subject=str(uri), + issue="Unknown XSD Term", + message=f"Term <{uri}> is not a recognized XSD term." + )) + + return SchemaReport( + is_valid=(len(errors) == 0), + errors=errors + ) + + def validate(self, data_input: Union[str, Dict[str, Any], Graph], context_graph: Optional[Graph] = None) -> ValidationReport: + """ + Validates a data file against the loaded schema rules. + + The validation logic checks three primary conditions for every triple in the data: + 1. **Property Existence:** Is the predicate defined in the schema? + 2. **Domain Compliance:** Does the subject have the `rdf:type` required by the predicate's domain? + 3. **Range Compliance:** Does the object have the `rdf:type` (or datatype) required by the predicate's range? + + Note: RDFS descriptive properties (label, comment) are explicitly ignored during validation. + + Args: + data_input (Union[str, Dict[str, Any], Graph]): The data to validate, provided as a + JSON-LD string, dictionary, or rdflib.Graph. + context_graph (Optional[Graph]): An optional graph containing the existing Knowledge Graph + context. This allows verifying types/existence of nodes not in the new data batch. + + Returns: + ValidationReport: A detailed Pydantic report containing the overall validity status, + error counts, and a list of specific `ValidationError` objects for any violations found. + """ + data_graph = self._load_graph(data_input) + errors = [] + + def has_type(resource, type_uri): + # Check existence of (resource, rdf:type, type_uri) in: + # 1. The new data being added + # 2. The existing Knowledge Graph context (if provided) + # 3. The Schema itself (less likely for instances, but possible for meta-modeling) + + if (resource, RDF.type, type_uri) in data_graph: + return True + if context_graph and (resource, RDF.type, type_uri) in context_graph: + return True + if (resource, RDF.type, type_uri) in self.schema_graph: + return True + return False + + for s, p, o in data_graph: + # Skip validation for RDFS/RDF/XSD definition terms within the data + # This allows bootstrapping schema definitions without explicitly defining rdfs:domain etc. + p_str = str(p) + if p_str.startswith(str(RDF)) or p_str.startswith(str(RDFS)) or p_str.startswith(str(XSD)): + continue + + # Check 1: Is the Property Defined? + if p not in self.rules.properties: + errors.append(ValidationError( + subject=str(s), predicate=str(p), object=str(o), + message="Property not defined in schema.", + rule_type="Undefined Property" + )) + continue + + # Check 2: Domain Validation + if p in self.rules.domains: + required_domain = self.rules.domains[p] + # Query: Does Subject 's' have type 'required_domain'? + if not has_type(s, required_domain): + errors.append(ValidationError( + subject=str(s), predicate=str(p), object=str(o), + message=f"Subject must be of type <{required_domain}>", + rule_type="Domain Violation" + )) + + # Check 3: Range Validation + if p in self.rules.ranges: + required_range = self.rules.ranges[p] + + # Case A: Literal + if isinstance(o, Literal): + # For strict XSD validation, you might compare o.datatype + if o.datatype != required_range and required_range != XSD.string: + errors.append(ValidationError( + subject=str(s), predicate=str(p), object=str(o), + message=f"Literal value must be datatype <{required_range}>", + rule_type="Range Violation" + )) + + # Case B: Resource (URI) + elif isinstance(o, URIRef): + # Query: Does Object 'o' have type 'required_range'? + if not has_type(o, required_range): + errors.append(ValidationError( + subject=str(s), predicate=str(p), object=str(o), + message=f"Object must be of type <{required_range}>", + rule_type="Range Violation" + )) + + return ValidationReport( + is_valid=(len(errors) == 0), + error_count=len(errors), + errors=errors + ) \ No newline at end of file diff --git a/packages/datacommons-schema/datacommons_schema/tests/test_knowledge_graph.py b/packages/datacommons-schema/datacommons_schema/tests/test_knowledge_graph.py new file mode 100644 index 0000000..46468eb --- /dev/null +++ b/packages/datacommons-schema/datacommons_schema/tests/test_knowledge_graph.py @@ -0,0 +1,177 @@ +import pytest +from rdflib import Graph, URIRef, Literal, RDF, RDFS, XSD +from datacommons_schema.knowledge_graph import KnowledgeGraph +from datacommons_schema.services.schema_validation_service import ValidationError + +def test_kg_initialization(): + kg = KnowledgeGraph(namespace="http://example.org/", default_prefix="ex") + assert kg.namespace == "http://example.org/" + assert kg.default_prefix == "ex" + # Check strict prefix binding in internal graph + assert dict(kg._graph.namespaces())["ex"] == URIRef("http://example.org/") + +def test_add_valid_node(): + kg = KnowledgeGraph(namespace="http://example.org/", default_prefix="ex") + + # 1. Add Schema (Class and Property) + # Note: validation of schema primarily checks for "Undefined Property" (if we are checking property existence). + # But defining a Class uses 'rdf:type', which is ignored/allowed. + schema_node = { + "@context": {"ex": "http://example.org/", "rdf": str(RDF), "rdfs": str(RDFS), "xsd": str(XSD)}, + "@graph": [ + {"@id": "ex:Person", "@type": "rdfs:Class"}, + {"@id": "ex:name", "@type": "rdf:Property", "rdfs:domain": {"@id": "ex:Person"}, "rdfs:range": {"@id": "xsd:string"}} + ] + } + kg.add(schema_node) + + # 2. Add Valid Data + person_node = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Alice", + "@type": "ex:Person", + "ex:name": "Alice" + } + kg.add(person_node) + + # Verify triples exist + assert (URIRef("http://example.org/Alice"), RDF.type, URIRef("http://example.org/Person")) in kg._graph + assert (URIRef("http://example.org/Alice"), URIRef("http://example.org/name"), Literal("Alice")) in kg._graph + +def test_add_invalid_data_undefined_property(): + kg = KnowledgeGraph(namespace="http://example.org/") + + # Valid Schema + kg.add({ + "@context": {"ex": "http://example.org/", "rdfs": str(RDFS)}, + "@id": "ex:Person", "@type": "rdfs:Class" + }) + + # Invalid Data (Undefined Property) + invalid_node = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Bob", + "@type": "ex:Person", + "ex:unknownProp": "Value" + } + + with pytest.raises(ValueError) as excinfo: + kg.add(invalid_node) + assert "Property not defined in schema" in str(excinfo.value) + +def test_add_invalid_data_domain_violation(): + kg = KnowledgeGraph(namespace="http://example.org/") + + # Schema + kg.add({ + "@context": {"ex": "http://example.org/", "rdf": str(RDF), "rdfs": str(RDFS), "xsd": str(XSD)}, + "@graph": [ + {"@id": "ex:Person", "@type": "rdfs:Class"}, + {"@id": "ex:Dog", "@type": "rdfs:Class"}, + {"@id": "ex:name", "@type": "rdf:Property", "rdfs:domain": {"@id": "ex:Person"}, "rdfs:range": {"@id": "xsd:string"}} + ] + }) + + # Invalid Data (Subject is Dog, but domain requires Person) + dog_node = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Fido", + "@type": "ex:Dog", + "ex:name": "Fido" + } + + with pytest.raises(ValueError) as excinfo: + kg.add(dog_node) + assert "Subject must be of type " in str(excinfo.value) + +def test_context_aware_validation(): + """ + Test that validation uses existing KG context for type checks. + """ + kg = KnowledgeGraph(namespace="http://example.org/") + + # 1. Define Schema + kg.add({ + "@context": {"ex": "http://example.org/", "rdf": str(RDF), "rdfs": str(RDFS)}, + "@graph": [ + {"@id": "ex:Person", "@type": "rdfs:Class"}, + {"@id": "ex:knows", "@type": "rdf:Property", "rdfs:domain": {"@id": "ex:Person"}, "rdfs:range": {"@id": "ex:Person"}} + ] + }) + + # 2. Add Alice (Person) + kg.add({ + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Alice", + "@type": "ex:Person" + }) + + # 3. Add Bob (Person) - separately + kg.add({ + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Bob", + "@type": "ex:Person" + }) + + # 4. Add Relationship: Alice knows Bob + # Validation needs to know Alice and Bob are Persons. + # Alice is in the update batch (implicitly? No, just the relation). + # Wait, if we just add the relation, we need to know types of subject/object. + relation_node = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Alice", + "ex:knows": {"@id": "ex:Bob"} + } + + # This should pass because KG knows Alice and Bob are Persons + kg.add(relation_node) + +def test_schema_integrity_check_failure(): + """ + This test verifies if we are catching schema integrity issues. + """ + kg = KnowledgeGraph(namespace="http://example.org/") + + # Invalid Schema: Domain points to undefined class + bad_schema = { + "@context": {"ex": "http://example.org/", "rdf": str(RDF), "rdfs": str(RDFS)}, + "@id": "ex:prop", + "@type": "rdf:Property", + "rdfs:domain": {"@id": "ex:GhostClass"} # Undefined + } + + # IF we want this to fail, we must call validate_schema_integrity. + # Currently it might pass because 'validate' doesn't check domain target existence. + with pytest.raises(ValueError) as excinfo: + kg.add(bad_schema) + assert "Undefined Domain Target" in str(excinfo.value) + # If it passes, assert that (showing it allows bad schema). + # If users WANT it to fail, we must fix implementation. + + # Validated that add() blocked the invalid schema. + # So we do NOT expect the property to be in the graph. + assert (URIRef("http://example.org/prop"), RDF.type, RDF.Property) not in kg._graph + +def test_add_invalid_data_range_violation(): + kg = KnowledgeGraph(namespace="http://example.org/") + # Schema + kg.add({ + "@context": {"ex": "http://example.org/", "rdf": str(RDF), "rdfs": str(RDFS), "xsd": str(XSD)}, + "@graph": [ + {"@id": "ex:Person", "@type": "rdfs:Class"}, + {"@id": "ex:Dog", "@type": "rdfs:Class"}, + {"@id": "ex:owner", "@type": "rdf:Property", "rdfs:domain": {"@id": "ex:Dog"}, "rdfs:range": {"@id": "ex:Person"}} + ] + }) + + # Invalid Data (Subject is Dog, but domain requires Person) + dog_node = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Fido", + "@type": "ex:Dog", + "ex:owner": "FidosOwner" + } + + with pytest.raises(ValueError) as excinfo: + kg.add(dog_node) + assert "Literal value must be datatype " in str(excinfo.value) \ No newline at end of file diff --git a/packages/datacommons-schema/datacommons_schema/tests/validator/schema_validation_service_test.py b/packages/datacommons-schema/datacommons_schema/tests/validator/schema_validation_service_test.py new file mode 100644 index 0000000..1bd59df --- /dev/null +++ b/packages/datacommons-schema/datacommons_schema/tests/validator/schema_validation_service_test.py @@ -0,0 +1,217 @@ +import pytest +from rdflib import URIRef, XSD +from pydantic import BaseModel +from typing import List + +from datacommons_schema.services.schema_validation_service import SchemaValidationService + +# --- MOCKING MISSING CLASSES FOR THE TEST TO RUN --- +class SchemaError(BaseModel): + subject: str + issue: str + message: str + +class SchemaReport(BaseModel): + is_valid: bool + errors: List[SchemaError] = [] +# --------------------------------------------------- + +# ========================================== +# FIXTURES +# ========================================== + +@pytest.fixture +def sample_schema_dict() -> dict: + """ + A schema defining: + - Classes: Person, Organization + - Properties: name (string), age (int), worksFor (link to Organization) + """ + return { + "@context": { + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "ex": "http://example.org/" + }, + "@graph": [ + {"@id": "ex:Person", "@type": "rdfs:Class"}, + {"@id": "ex:Organization", "@type": "rdfs:Class"}, + { + "@id": "ex:name", + "@type": "rdf:Property", + "rdfs:domain": {"@id": "ex:Person"}, + "rdfs:range": {"@id": "xsd:string"} + }, + { + "@id": "ex:age", + "@type": "rdf:Property", + "rdfs:domain": {"@id": "ex:Person"}, + "rdfs:range": {"@id": "xsd:integer"} + }, + { + "@id": "ex:worksFor", + "@type": "rdf:Property", + "rdfs:domain": {"@id": "ex:Person"}, + "rdfs:range": {"@id": "ex:Organization"} + } + ] + } + +@pytest.fixture +def schema_service(sample_schema_dict: dict) -> SchemaValidationService: + """Initializes the service with the valid sample schema.""" + return SchemaValidationService(sample_schema_dict) + +@pytest.fixture +def valid_data_packet() -> dict: + """Data that perfectly conforms to sample_schema_dict.""" + return { + "@context": { + "ex": "http://example.org/", + "xsd": "http://www.w3.org/2001/XMLSchema#" + }, + "@id": "ex:Alice", + "@type": "ex:Person", + "ex:name": "Alice", + "ex:age": 30, + "ex:worksFor": { + "@id": "ex:Google", + "@type": "ex:Organization" + } + } + +# ========================================== +# TESTS +# ========================================== + +def test_initialization_extracts_rules(schema_service: SchemaValidationService): + """Ensure the constructor parses the graph and extracts rules correctly.""" + rules = schema_service.rules + + # Check Classes + assert URIRef("http://example.org/Person") in rules.classes + assert URIRef("http://example.org/Organization") in rules.classes + + # Check Properties + assert URIRef("http://example.org/name") in rules.properties + + # Check Domain/Range logic + name_prop = URIRef("http://example.org/name") + assert rules.domains[name_prop] == URIRef("http://example.org/Person") + assert rules.ranges[name_prop] == XSD.string + +def test_schema_integrity_valid(schema_service): + """Ensure a coherent schema passes integrity checks.""" + report = schema_service.validate_schema_integrity() + assert report.is_valid is True + assert len(report.errors) == 0 + +def test_schema_integrity_invalid(): + """Ensure a schema referencing undefined classes fails integrity checks.""" + bad_schema = { + "@context": { + "ex": "http://example.org/", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + }, + "@graph": [ + {"@id": "ex:hasPet", "@type": "rdf:Propertyzzz", "rdfs:domain": {"@id": "ex:GhostClass"}} + ] + } + service = SchemaValidationService(bad_schema) + report = service.validate_schema_integrity() + + assert report.is_valid is False + # We might get multiple errors (one for rdf:Property, maybe others), but at least one should be about malformed URI + issues = [e.issue for e in report.errors] + assert "Unknown RDF Term" in issues + +def test_schema_integrity_invalid_xsd(): + """Ensure a schema referencing undefined XSD types fails integrity checks.""" + bad_schema = { + "@context": { + "ex": "http://example.org/", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + "xsd": "http://www.w3.org/2001/XMLSchema#" + }, + "@graph": [ + { + "@id": "ex:age", + "@type": "rdf:Property", + "rdfs:range": {"@id": "xsd:InvalidType"} + } + ] + } + service = SchemaValidationService(bad_schema) + report = service.validate_schema_integrity() + + assert report.is_valid is False + issues = [e.issue for e in report.errors] + assert "Unknown XSD Term" in issues + +def test_validate_valid_data(schema_service, valid_data_packet): + """Happy path: Data conforms to schema.""" + report = schema_service.validate(valid_data_packet) + assert report.is_valid is True + assert report.error_count == 0 + +def test_validate_undefined_property(schema_service): + """Test data containing a property not in the schema.""" + data = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Bob", + "ex:favoriteColor": "Blue" # Not in schema + } + report = schema_service.validate(data) + + assert report.is_valid is False + assert report.errors[0].rule_type == "Undefined Property" + assert "favoriteColor" in report.errors[0].predicate + +def test_validate_domain_violation(schema_service): + """Test data where subject is wrong type (Organization has an age?).""" + data = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:MegaCorp", + "@type": "ex:Organization", # Schema says 'age' belongs to 'Person' + "ex:age": 100 + } + report = schema_service.validate(data) + + assert report.is_valid is False + error = report.errors[0] + assert error.rule_type == "Domain Violation" + assert "must be of type " in error.message + +def test_validate_range_violation_literal(schema_service): + """Test data where literal datatype is wrong (age is string instead of int).""" + data = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Bob", + "@type": "ex:Person", + "ex:age": "Thirty" # Should be integer + } + report = schema_service.validate(data) + + assert report.is_valid is False + assert report.errors[0].rule_type == "Range Violation" + # Note: RDFLib might interpret "Thirty" as xsd:string by default if un-typed + +def test_validate_range_violation_class(schema_service): + """Test data where object URI is the wrong class.""" + data = { + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Bob", + "@type": "ex:Person", + "ex:worksFor": { + "@id": "ex:AnotherPerson", + "@type": "ex:Person" # Schema says worksFor range is Organization + } + } + report = schema_service.validate(data) + + assert report.is_valid is False + assert report.errors[0].rule_type == "Range Violation" + assert "must be of type " in report.errors[0].message \ No newline at end of file diff --git a/packages/datacommons-schema/pyproject.toml b/packages/datacommons-schema/pyproject.toml index 4b43b2c..3b4251b 100644 --- a/packages/datacommons-schema/pyproject.toml +++ b/packages/datacommons-schema/pyproject.toml @@ -19,7 +19,9 @@ dependencies = [ "click", "pydantic", "pytest", - "pyshacl" + "pyshacl", + "rdflib", + ] [project.urls] @@ -31,7 +33,7 @@ Source = "https://github.com/datacommonsorg/datacommons" datacommons-schema = "datacommons_schema.schema_cli:cli" [build-system] -requires = ["uv", "setuptools"] +requires = ["setuptools"] build-backend = "setuptools.build_meta" [tool.setuptools.dynamic] diff --git a/uv.lock b/uv.lock index cffbfb1..b6b4903 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = "==3.12.*" [manifest] @@ -13,126 +13,126 @@ members = [ [[package]] name = "alembic" version = "1.16.4" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "mako" }, { name = "sqlalchemy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/52/72e791b75c6b1efa803e491f7cbab78e963695e76d4ada05385252927e76/alembic-1.16.4.tar.gz", hash = "sha256:efab6ada0dd0fae2c92060800e0bf5c1dc26af15a10e02fb4babff164b4725e2", size = 1968161, upload-time = "2025-07-10T16:17:20.192Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/alembic/alembic-1.16.4.tar.gz", hash = "sha256:efab6ada0dd0fae2c92060800e0bf5c1dc26af15a10e02fb4babff164b4725e2" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/62/96b5217b742805236614f05904541000f55422a6060a90d7fd4ce26c172d/alembic-1.16.4-py3-none-any.whl", hash = "sha256:b05e51e8e82efc1abd14ba2af6392897e145930c3e0a2faf2b0da2f7f7fd660d", size = 247026, upload-time = "2025-07-10T16:17:21.845Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/alembic/alembic-1.16.4-py3-none-any.whl", hash = "sha256:b05e51e8e82efc1abd14ba2af6392897e145930c3e0a2faf2b0da2f7f7fd660d" }, ] [[package]] name = "annotated-types" version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/annotated-types/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/annotated-types/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53" }, ] [[package]] name = "anyio" version = "4.10.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "idna" }, { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/anyio/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/anyio/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1" }, ] [[package]] name = "cachetools" version = "5.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/cachetools/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4" } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/cachetools/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a" }, ] [[package]] name = "certifi" version = "2025.8.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/certifi/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/certifi/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5" }, ] [[package]] name = "cfgv" version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/cfgv/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/cfgv/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9" }, ] [[package]] name = "charset-normalizer" version = "3.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" }, - { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" }, - { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" }, - { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" }, - { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" }, - { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" }, - { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" }, - { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" }, - { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" }, - { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" }, - { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/charset-normalizer/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a" }, ] [[package]] name = "click" version = "8.2.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/click/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/click/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b" }, ] [[package]] name = "colorama" version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/colorama/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/colorama/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" }, ] [[package]] name = "coverage" version = "7.10.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/2c/253cc41cd0f40b84c1c34c5363e0407d73d4a1cae005fed6db3b823175bd/coverage-7.10.3.tar.gz", hash = "sha256:812ba9250532e4a823b070b0420a36499859542335af3dca8f47fc6aa1a05619", size = 822936, upload-time = "2025-08-10T21:27:39.968Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3.tar.gz", hash = "sha256:812ba9250532e4a823b070b0420a36499859542335af3dca8f47fc6aa1a05619" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/62/13c0b66e966c43d7aa64dadc8cd2afa1f5a2bf9bb863bdabc21fb94e8b63/coverage-7.10.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:449c1e2d3a84d18bd204258a897a87bc57380072eb2aded6a5b5226046207b42", size = 216262, upload-time = "2025-08-10T21:25:55.367Z" }, - { url = "https://files.pythonhosted.org/packages/b5/f0/59fdf79be7ac2f0206fc739032f482cfd3f66b18f5248108ff192741beae/coverage-7.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d4f9ce50b9261ad196dc2b2e9f1fbbee21651b54c3097a25ad783679fd18294", size = 216496, upload-time = "2025-08-10T21:25:56.759Z" }, - { url = "https://files.pythonhosted.org/packages/34/b1/bc83788ba31bde6a0c02eb96bbc14b2d1eb083ee073beda18753fa2c4c66/coverage-7.10.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4dd4564207b160d0d45c36a10bc0a3d12563028e8b48cd6459ea322302a156d7", size = 247989, upload-time = "2025-08-10T21:25:58.067Z" }, - { url = "https://files.pythonhosted.org/packages/0c/29/f8bdf88357956c844bd872e87cb16748a37234f7f48c721dc7e981145eb7/coverage-7.10.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5ca3c9530ee072b7cb6a6ea7b640bcdff0ad3b334ae9687e521e59f79b1d0437", size = 250738, upload-time = "2025-08-10T21:25:59.406Z" }, - { url = "https://files.pythonhosted.org/packages/ae/df/6396301d332b71e42bbe624670af9376f63f73a455cc24723656afa95796/coverage-7.10.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b6df359e59fa243c9925ae6507e27f29c46698359f45e568fd51b9315dbbe587", size = 251868, upload-time = "2025-08-10T21:26:00.65Z" }, - { url = "https://files.pythonhosted.org/packages/91/21/d760b2df6139b6ef62c9cc03afb9bcdf7d6e36ed4d078baacffa618b4c1c/coverage-7.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a181e4c2c896c2ff64c6312db3bda38e9ade2e1aa67f86a5628ae85873786cea", size = 249790, upload-time = "2025-08-10T21:26:02.009Z" }, - { url = "https://files.pythonhosted.org/packages/69/91/5dcaa134568202397fa4023d7066d4318dc852b53b428052cd914faa05e1/coverage-7.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a374d4e923814e8b72b205ef6b3d3a647bb50e66f3558582eda074c976923613", size = 247907, upload-time = "2025-08-10T21:26:03.757Z" }, - { url = "https://files.pythonhosted.org/packages/38/ed/70c0e871cdfef75f27faceada461206c1cc2510c151e1ef8d60a6fedda39/coverage-7.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:daeefff05993e5e8c6e7499a8508e7bd94502b6b9a9159c84fd1fe6bce3151cb", size = 249344, upload-time = "2025-08-10T21:26:05.11Z" }, - { url = "https://files.pythonhosted.org/packages/5f/55/c8a273ed503cedc07f8a00dcd843daf28e849f0972e4c6be4c027f418ad6/coverage-7.10.3-cp312-cp312-win32.whl", hash = "sha256:187ecdcac21f9636d570e419773df7bd2fda2e7fa040f812e7f95d0bddf5f79a", size = 218693, upload-time = "2025-08-10T21:26:06.534Z" }, - { url = "https://files.pythonhosted.org/packages/94/58/dd3cfb2473b85be0b6eb8c5b6d80b6fc3f8f23611e69ef745cef8cf8bad5/coverage-7.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:4a50ad2524ee7e4c2a95e60d2b0b83283bdfc745fe82359d567e4f15d3823eb5", size = 219501, upload-time = "2025-08-10T21:26:08.195Z" }, - { url = "https://files.pythonhosted.org/packages/56/af/7cbcbf23d46de6f24246e3f76b30df099d05636b30c53c158a196f7da3ad/coverage-7.10.3-cp312-cp312-win_arm64.whl", hash = "sha256:c112f04e075d3495fa3ed2200f71317da99608cbb2e9345bdb6de8819fc30571", size = 218135, upload-time = "2025-08-10T21:26:09.584Z" }, - { url = "https://files.pythonhosted.org/packages/84/19/e67f4ae24e232c7f713337f3f4f7c9c58afd0c02866fb07c7b9255a19ed7/coverage-7.10.3-py3-none-any.whl", hash = "sha256:416a8d74dc0adfd33944ba2f405897bab87b7e9e84a391e09d241956bd953ce1", size = 207921, upload-time = "2025-08-10T21:27:38.254Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:449c1e2d3a84d18bd204258a897a87bc57380072eb2aded6a5b5226046207b42" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d4f9ce50b9261ad196dc2b2e9f1fbbee21651b54c3097a25ad783679fd18294" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4dd4564207b160d0d45c36a10bc0a3d12563028e8b48cd6459ea322302a156d7" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5ca3c9530ee072b7cb6a6ea7b640bcdff0ad3b334ae9687e521e59f79b1d0437" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b6df359e59fa243c9925ae6507e27f29c46698359f45e568fd51b9315dbbe587" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a181e4c2c896c2ff64c6312db3bda38e9ade2e1aa67f86a5628ae85873786cea" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a374d4e923814e8b72b205ef6b3d3a647bb50e66f3558582eda074c976923613" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:daeefff05993e5e8c6e7499a8508e7bd94502b6b9a9159c84fd1fe6bce3151cb" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3-cp312-cp312-win32.whl", hash = "sha256:187ecdcac21f9636d570e419773df7bd2fda2e7fa040f812e7f95d0bddf5f79a" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:4a50ad2524ee7e4c2a95e60d2b0b83283bdfc745fe82359d567e4f15d3823eb5" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3-cp312-cp312-win_arm64.whl", hash = "sha256:c112f04e075d3495fa3ed2200f71317da99608cbb2e9345bdb6de8819fc30571" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/coverage/coverage-7.10.3-py3-none-any.whl", hash = "sha256:416a8d74dc0adfd33944ba2f405897bab87b7e9e84a391e09d241956bd953ce1" }, ] [[package]] @@ -207,6 +207,7 @@ dependencies = [ { name = "pydantic" }, { name = "pyshacl" }, { name = "pytest" }, + { name = "rdflib" }, ] [package.metadata] @@ -215,44 +216,45 @@ requires-dist = [ { name = "pydantic" }, { name = "pyshacl" }, { name = "pytest" }, + { name = "rdflib" }, ] [[package]] name = "distlib" version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/distlib/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/distlib/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16" }, ] [[package]] name = "fastapi" version = "0.116.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/d7/6c8b3bfe33eeffa208183ec037fee0cce9f7f024089ab1c5d12ef04bd27c/fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143", size = 296485, upload-time = "2025-07-11T16:22:32.057Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/fastapi/fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631, upload-time = "2025-07-11T16:22:30.485Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/fastapi/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565" }, ] [[package]] name = "filelock" version = "3.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/filelock/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/filelock/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de" }, ] [[package]] name = "google-api-core" version = "2.25.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "google-auth" }, { name = "googleapis-common-protos" }, @@ -260,9 +262,9 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dc/21/e9d043e88222317afdbdb567165fdbc3b0aad90064c7e0c9eb0ad9955ad8/google_api_core-2.25.1.tar.gz", hash = "sha256:d2aaa0b13c78c61cb3f4282c464c046e45fbd75755683c9c525e6e8f7ed0a5e8", size = 165443, upload-time = "2025-06-12T20:52:20.439Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/google-api-core/google_api_core-2.25.1.tar.gz", hash = "sha256:d2aaa0b13c78c61cb3f4282c464c046e45fbd75755683c9c525e6e8f7ed0a5e8" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/4b/ead00905132820b623732b175d66354e9d3e69fcf2a5dcdab780664e7896/google_api_core-2.25.1-py3-none-any.whl", hash = "sha256:8a2a56c1fef82987a524371f99f3bd0143702fecc670c72e600c1cda6bf8dbb7", size = 160807, upload-time = "2025-06-12T20:52:19.334Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/google-api-core/google_api_core-2.25.1-py3-none-any.whl", hash = "sha256:8a2a56c1fef82987a524371f99f3bd0143702fecc670c72e600c1cda6bf8dbb7" }, ] [package.optional-dependencies] @@ -274,34 +276,34 @@ grpc = [ [[package]] name = "google-auth" version = "2.40.3" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "cachetools" }, { name = "pyasn1-modules" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/9b/e92ef23b84fa10a64ce4831390b7a4c2e53c0132568d99d4ae61d04c8855/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77", size = 281029, upload-time = "2025-06-04T18:04:57.577Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/google-auth/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca", size = 216137, upload-time = "2025-06-04T18:04:55.573Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/google-auth/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca" }, ] [[package]] name = "google-cloud-core" version = "2.4.3" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "google-api-core" }, { name = "google-auth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/b8/2b53838d2acd6ec6168fd284a990c76695e84c65deee79c9f3a4276f6b4f/google_cloud_core-2.4.3.tar.gz", hash = "sha256:1fab62d7102844b278fe6dead3af32408b1df3eb06f5c7e8634cbd40edc4da53", size = 35861, upload-time = "2025-03-10T21:05:38.948Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/google-cloud-core/google_cloud_core-2.4.3.tar.gz", hash = "sha256:1fab62d7102844b278fe6dead3af32408b1df3eb06f5c7e8634cbd40edc4da53" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/86/bda7241a8da2d28a754aad2ba0f6776e35b67e37c36ae0c45d49370f1014/google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e", size = 29348, upload-time = "2025-03-10T21:05:37.785Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/google-cloud-core/google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e" }, ] [[package]] name = "google-cloud-spanner" version = "3.56.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, { name = "google-cloud-core" }, @@ -311,21 +313,21 @@ dependencies = [ { name = "protobuf" }, { name = "sqlparse" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/67/33c120f21462173846b202c84bb9979f12ff8227cc6de4e0957c37fd9a60/google_cloud_spanner-3.56.0.tar.gz", hash = "sha256:bf7e4359d2f2148eda18a11f909813d07e794347a02f56dfbbd544418d30e5b2", size = 701321, upload-time = "2025-07-24T11:12:56.461Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/google-cloud-spanner/google_cloud_spanner-3.56.0.tar.gz", hash = "sha256:bf7e4359d2f2148eda18a11f909813d07e794347a02f56dfbbd544418d30e5b2" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/e9/60ac93f633e3ed78d399d3f0e2117c4e4e751a2ac8c87ae994d86cae7a2b/google_cloud_spanner-3.56.0-py3-none-any.whl", hash = "sha256:64f732a44f6a2892b5cc3be88e6e2cc92b5e52db883d1e946e1e3ff93c9745f0", size = 501309, upload-time = "2025-07-24T11:12:54.892Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/google-cloud-spanner/google_cloud_spanner-3.56.0-py3-none-any.whl", hash = "sha256:64f732a44f6a2892b5cc3be88e6e2cc92b5e52db883d1e946e1e3ff93c9745f0" }, ] [[package]] name = "googleapis-common-protos" version = "1.70.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903, upload-time = "2025-04-14T10:17:02.924Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/googleapis-common-protos/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257" } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530, upload-time = "2025-04-14T10:17:01.271Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/googleapis-common-protos/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8" }, ] [package.optional-dependencies] @@ -336,222 +338,222 @@ grpc = [ [[package]] name = "greenlet" version = "3.2.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/greenlet/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" }, - { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" }, - { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" }, - { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" }, - { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" }, - { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" }, - { url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142, upload-time = "2025-08-07T13:18:22.981Z" }, - { url = "https://files.pythonhosted.org/packages/27/45/80935968b53cfd3f33cf99ea5f08227f2646e044568c9b1555b58ffd61c2/greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0", size = 1564846, upload-time = "2025-11-04T12:42:15.191Z" }, - { url = "https://files.pythonhosted.org/packages/69/02/b7c30e5e04752cb4db6202a3858b149c0710e5453b71a3b2aec5d78a1aab/greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d", size = 1633814, upload-time = "2025-11-04T12:42:17.175Z" }, - { url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899, upload-time = "2025-08-07T13:38:53.448Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/greenlet/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/greenlet/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/greenlet/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/greenlet/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/greenlet/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/greenlet/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/greenlet/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/greenlet/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/greenlet/greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/greenlet/greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/greenlet/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02" }, ] [[package]] name = "grpc-google-iam-v1" version = "0.14.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "googleapis-common-protos", extra = ["grpc"] }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/4e/8d0ca3b035e41fe0b3f31ebbb638356af720335e5a11154c330169b40777/grpc_google_iam_v1-0.14.2.tar.gz", hash = "sha256:b3e1fc387a1a329e41672197d0ace9de22c78dd7d215048c4c78712073f7bd20", size = 16259, upload-time = "2025-03-17T11:40:23.586Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpc-google-iam-v1/grpc_google_iam_v1-0.14.2.tar.gz", hash = "sha256:b3e1fc387a1a329e41672197d0ace9de22c78dd7d215048c4c78712073f7bd20" } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/6f/dd9b178aee7835b96c2e63715aba6516a9d50f6bebbd1cc1d32c82a2a6c3/grpc_google_iam_v1-0.14.2-py3-none-any.whl", hash = "sha256:a3171468459770907926d56a440b2bb643eec1d7ba215f48f3ecece42b4d8351", size = 19242, upload-time = "2025-03-17T11:40:22.648Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpc-google-iam-v1/grpc_google_iam_v1-0.14.2-py3-none-any.whl", hash = "sha256:a3171468459770907926d56a440b2bb643eec1d7ba215f48f3ecece42b4d8351" }, ] [[package]] name = "grpc-interceptor" version = "0.15.4" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "grpcio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/28/57449d5567adf4c1d3e216aaca545913fbc21a915f2da6790d6734aac76e/grpc-interceptor-0.15.4.tar.gz", hash = "sha256:1f45c0bcb58b6f332f37c637632247c9b02bc6af0fdceb7ba7ce8d2ebbfb0926", size = 19322, upload-time = "2023-11-16T02:05:42.459Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpc-interceptor/grpc-interceptor-0.15.4.tar.gz", hash = "sha256:1f45c0bcb58b6f332f37c637632247c9b02bc6af0fdceb7ba7ce8d2ebbfb0926" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/ac/8d53f230a7443401ce81791ec50a3b0e54924bf615ad287654fa4a2f5cdc/grpc_interceptor-0.15.4-py3-none-any.whl", hash = "sha256:0035f33228693ed3767ee49d937bac424318db173fef4d2d0170b3215f254d9d", size = 20848, upload-time = "2023-11-16T02:05:40.913Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpc-interceptor/grpc_interceptor-0.15.4-py3-none-any.whl", hash = "sha256:0035f33228693ed3767ee49d937bac424318db173fef4d2d0170b3215f254d9d" }, ] [[package]] name = "grpcio" version = "1.74.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1", size = 12756048, upload-time = "2025-07-24T18:54:23.039Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/5d/e504d5d5c4469823504f65687d6c8fb97b7f7bf0b34873b7598f1df24630/grpcio-1.74.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8533e6e9c5bd630ca98062e3a1326249e6ada07d05acf191a77bc33f8948f3d8", size = 5445551, upload-time = "2025-07-24T18:53:23.641Z" }, - { url = "https://files.pythonhosted.org/packages/43/01/730e37056f96f2f6ce9f17999af1556df62ee8dab7fa48bceeaab5fd3008/grpcio-1.74.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:2918948864fec2a11721d91568effffbe0a02b23ecd57f281391d986847982f6", size = 10979810, upload-time = "2025-07-24T18:53:25.349Z" }, - { url = "https://files.pythonhosted.org/packages/79/3d/09fd100473ea5c47083889ca47ffd356576173ec134312f6aa0e13111dee/grpcio-1.74.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:60d2d48b0580e70d2e1954d0d19fa3c2e60dd7cbed826aca104fff518310d1c5", size = 5941946, upload-time = "2025-07-24T18:53:27.387Z" }, - { url = "https://files.pythonhosted.org/packages/8a/99/12d2cca0a63c874c6d3d195629dcd85cdf5d6f98a30d8db44271f8a97b93/grpcio-1.74.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3601274bc0523f6dc07666c0e01682c94472402ac2fd1226fd96e079863bfa49", size = 6621763, upload-time = "2025-07-24T18:53:29.193Z" }, - { url = "https://files.pythonhosted.org/packages/9d/2c/930b0e7a2f1029bbc193443c7bc4dc2a46fedb0203c8793dcd97081f1520/grpcio-1.74.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:176d60a5168d7948539def20b2a3adcce67d72454d9ae05969a2e73f3a0feee7", size = 6180664, upload-time = "2025-07-24T18:53:30.823Z" }, - { url = "https://files.pythonhosted.org/packages/db/d5/ff8a2442180ad0867717e670f5ec42bfd8d38b92158ad6bcd864e6d4b1ed/grpcio-1.74.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e759f9e8bc908aaae0412642afe5416c9f983a80499448fcc7fab8692ae044c3", size = 6301083, upload-time = "2025-07-24T18:53:32.454Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ba/b361d390451a37ca118e4ec7dccec690422e05bc85fba2ec72b06cefec9f/grpcio-1.74.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e7c4389771855a92934b2846bd807fc25a3dfa820fd912fe6bd8136026b2707", size = 6994132, upload-time = "2025-07-24T18:53:34.506Z" }, - { url = "https://files.pythonhosted.org/packages/3b/0c/3a5fa47d2437a44ced74141795ac0251bbddeae74bf81df3447edd767d27/grpcio-1.74.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cce634b10aeab37010449124814b05a62fb5f18928ca878f1bf4750d1f0c815b", size = 6489616, upload-time = "2025-07-24T18:53:36.217Z" }, - { url = "https://files.pythonhosted.org/packages/ae/95/ab64703b436d99dc5217228babc76047d60e9ad14df129e307b5fec81fd0/grpcio-1.74.0-cp312-cp312-win32.whl", hash = "sha256:885912559974df35d92219e2dc98f51a16a48395f37b92865ad45186f294096c", size = 3807083, upload-time = "2025-07-24T18:53:37.911Z" }, - { url = "https://files.pythonhosted.org/packages/84/59/900aa2445891fc47a33f7d2f76e00ca5d6ae6584b20d19af9c06fa09bf9a/grpcio-1.74.0-cp312-cp312-win_amd64.whl", hash = "sha256:42f8fee287427b94be63d916c90399ed310ed10aadbf9e2e5538b3e497d269bc", size = 4490123, upload-time = "2025-07-24T18:53:39.528Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio/grpcio-1.74.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8533e6e9c5bd630ca98062e3a1326249e6ada07d05acf191a77bc33f8948f3d8" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio/grpcio-1.74.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:2918948864fec2a11721d91568effffbe0a02b23ecd57f281391d986847982f6" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio/grpcio-1.74.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:60d2d48b0580e70d2e1954d0d19fa3c2e60dd7cbed826aca104fff518310d1c5" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio/grpcio-1.74.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3601274bc0523f6dc07666c0e01682c94472402ac2fd1226fd96e079863bfa49" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio/grpcio-1.74.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:176d60a5168d7948539def20b2a3adcce67d72454d9ae05969a2e73f3a0feee7" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio/grpcio-1.74.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e759f9e8bc908aaae0412642afe5416c9f983a80499448fcc7fab8692ae044c3" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio/grpcio-1.74.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e7c4389771855a92934b2846bd807fc25a3dfa820fd912fe6bd8136026b2707" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio/grpcio-1.74.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cce634b10aeab37010449124814b05a62fb5f18928ca878f1bf4750d1f0c815b" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio/grpcio-1.74.0-cp312-cp312-win32.whl", hash = "sha256:885912559974df35d92219e2dc98f51a16a48395f37b92865ad45186f294096c" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio/grpcio-1.74.0-cp312-cp312-win_amd64.whl", hash = "sha256:42f8fee287427b94be63d916c90399ed310ed10aadbf9e2e5538b3e497d269bc" }, ] [[package]] name = "grpcio-status" version = "1.74.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/22/238c5f01e6837df54494deb08d5c772bc3f5bf5fb80a15dce254892d1a81/grpcio_status-1.74.0.tar.gz", hash = "sha256:c58c1b24aa454e30f1fc6a7e0dbbc194c54a408143971a94b5f4e40bb5831432", size = 13662, upload-time = "2025-07-24T19:01:56.874Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio-status/grpcio_status-1.74.0.tar.gz", hash = "sha256:c58c1b24aa454e30f1fc6a7e0dbbc194c54a408143971a94b5f4e40bb5831432" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/aa/1b1fe7d8ab699e1ec26d3a36b91d3df9f83a30abc07d4c881d0296b17b67/grpcio_status-1.74.0-py3-none-any.whl", hash = "sha256:52cdbd759a6760fc8f668098a03f208f493dd5c76bf8e02598bbbaf1f6fc2876", size = 14425, upload-time = "2025-07-24T19:01:19.963Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/grpcio-status/grpcio_status-1.74.0-py3-none-any.whl", hash = "sha256:52cdbd759a6760fc8f668098a03f208f493dd5c76bf8e02598bbbaf1f6fc2876" }, ] [[package]] name = "h11" version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/h11/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/h11/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86" }, ] [[package]] name = "html5rdf" version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/55/1b839c43f5ed8207e17a9a02d8b395179520b8b4f00c00a41e113bc205ca/html5rdf-1.2.1.tar.gz", hash = "sha256:ace9b420ce52995bb4f05e7425eedf19e433c981dfe7a831ab391e2fa2e1a195", size = 287899, upload-time = "2024-10-30T05:06:56.384Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/html5rdf/html5rdf-1.2.1.tar.gz", hash = "sha256:ace9b420ce52995bb4f05e7425eedf19e433c981dfe7a831ab391e2fa2e1a195" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/c9/f6e1e8567660bc5b0aba281f2b0017b2a7665fcad6bf3ed67286a0c72cd4/html5rdf-1.2.1-py2.py3-none-any.whl", hash = "sha256:1f519121bc366af3e485310dc8041d2e86e5173c1a320fac3dc9d2604069b83e", size = 109765, upload-time = "2024-10-30T05:06:52.507Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/html5rdf/html5rdf-1.2.1-py2.py3-none-any.whl", hash = "sha256:1f519121bc366af3e485310dc8041d2e86e5173c1a320fac3dc9d2604069b83e" }, ] [[package]] name = "httptools" version = "0.6.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639, upload-time = "2024-10-16T19:45:08.902Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/httptools/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683, upload-time = "2024-10-16T19:44:30.175Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337, upload-time = "2024-10-16T19:44:31.786Z" }, - { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796, upload-time = "2024-10-16T19:44:32.825Z" }, - { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837, upload-time = "2024-10-16T19:44:33.974Z" }, - { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289, upload-time = "2024-10-16T19:44:35.111Z" }, - { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779, upload-time = "2024-10-16T19:44:36.253Z" }, - { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634, upload-time = "2024-10-16T19:44:37.357Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/httptools/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/httptools/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/httptools/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/httptools/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/httptools/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/httptools/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/httptools/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970" }, ] [[package]] name = "identify" version = "2.6.13" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/ca/ffbabe3635bb839aa36b3a893c91a9b0d368cb4d8073e03a12896970af82/identify-2.6.13.tar.gz", hash = "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32", size = 99243, upload-time = "2025-08-09T19:35:00.6Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/identify/identify-2.6.13.tar.gz", hash = "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/ce/461b60a3ee109518c055953729bf9ed089a04db895d47e95444071dcdef2/identify-2.6.13-py2.py3-none-any.whl", hash = "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b", size = 99153, upload-time = "2025-08-09T19:34:59.1Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/identify/identify-2.6.13-py2.py3-none-any.whl", hash = "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b" }, ] [[package]] name = "idna" version = "3.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/idna/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/idna/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" }, ] [[package]] name = "iniconfig" version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/iniconfig/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/iniconfig/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760" }, ] [[package]] name = "mako" version = "1.3.10" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/mako/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/mako/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59" }, ] [[package]] name = "markupsafe" version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/markupsafe/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/markupsafe/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/markupsafe/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/markupsafe/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/markupsafe/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/markupsafe/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/markupsafe/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/markupsafe/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/markupsafe/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/markupsafe/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/markupsafe/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87" }, ] [[package]] name = "nodeenv" version = "1.9.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/nodeenv/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/nodeenv/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9" }, ] [[package]] name = "owlrl" version = "7.1.4" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "rdflib" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/fc/ce12482d096d65fff01af58f555a6f25e9dbf416fad5d99f91eaab0e11ca/owlrl-7.1.4.tar.gz", hash = "sha256:60bd4067e346b9111f0a2924565afe97ac6595b98b2bbe953928b5113971daf7", size = 44420, upload-time = "2025-07-29T00:17:27.608Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/owlrl/owlrl-7.1.4.tar.gz", hash = "sha256:60bd4067e346b9111f0a2924565afe97ac6595b98b2bbe953928b5113971daf7" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/78/f857ff1a7207e967dc5e8414bbcc15e0aa5cf45f693b1d2ebe2afb3eb1ce/owlrl-7.1.4-py3-none-any.whl", hash = "sha256:e78b46020169783345636da93a467d318f18700c483184dd15e885850cf64775", size = 51981, upload-time = "2025-07-29T00:17:26.229Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/owlrl/owlrl-7.1.4-py3-none-any.whl", hash = "sha256:e78b46020169783345636da93a467d318f18700c483184dd15e885850cf64775" }, ] [[package]] name = "packaging" version = "25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/packaging/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/packaging/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484" }, ] [[package]] name = "platformdirs" version = "4.3.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/platformdirs/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/platformdirs/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4" }, ] [[package]] name = "pluggy" version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pluggy/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pluggy/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" }, ] [[package]] name = "pre-commit" version = "4.3.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "cfgv" }, { name = "identify" }, @@ -559,147 +561,147 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pre-commit/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pre-commit/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8" }, ] [[package]] name = "prettytable" version = "3.17.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/79/45/b0847d88d6cfeb4413566738c8bbf1e1995fad3d42515327ff32cc1eb578/prettytable-3.17.0.tar.gz", hash = "sha256:59f2590776527f3c9e8cf9fe7b66dd215837cca96a9c39567414cbc632e8ddb0", size = 67892, upload-time = "2025-11-14T17:33:20.212Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/prettytable/prettytable-3.17.0.tar.gz", hash = "sha256:59f2590776527f3c9e8cf9fe7b66dd215837cca96a9c39567414cbc632e8ddb0" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl", hash = "sha256:aad69b294ddbe3e1f95ef8886a060ed1666a0b83018bbf56295f6f226c43d287", size = 34433, upload-time = "2025-11-14T17:33:19.093Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/prettytable/prettytable-3.17.0-py3-none-any.whl", hash = "sha256:aad69b294ddbe3e1f95ef8886a060ed1666a0b83018bbf56295f6f226c43d287" }, ] [[package]] name = "proto-plus" version = "1.26.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142, upload-time = "2025-03-10T15:54:38.843Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/proto-plus/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163, upload-time = "2025-03-10T15:54:37.335Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/proto-plus/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66" }, ] [[package]] name = "protobuf" version = "6.31.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797, upload-time = "2025-05-28T19:25:54.947Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/protobuf/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603, upload-time = "2025-05-28T19:25:41.198Z" }, - { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283, upload-time = "2025-05-28T19:25:44.275Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604, upload-time = "2025-05-28T19:25:45.702Z" }, - { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115, upload-time = "2025-05-28T19:25:47.128Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070, upload-time = "2025-05-28T19:25:50.036Z" }, - { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724, upload-time = "2025-05-28T19:25:53.926Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/protobuf/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/protobuf/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/protobuf/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/protobuf/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/protobuf/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/protobuf/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e" }, ] [[package]] name = "pyasn1" version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyasn1/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyasn1/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629" }, ] [[package]] name = "pyasn1-modules" version = "0.4.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyasn1-modules/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyasn1-modules/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a" }, ] [[package]] name = "pydantic" version = "2.11.7" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b" }, ] [[package]] name = "pydantic-core" version = "2.33.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, - { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, - { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, - { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, - { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, - { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, - { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, - { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, - { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, - { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pydantic-core/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab" }, ] [[package]] name = "pygments" version = "2.19.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pygments/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pygments/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" }, ] [[package]] name = "pyparsing" version = "3.2.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyparsing/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyparsing/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e" }, ] [[package]] name = "pyshacl" version = "0.30.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "owlrl" }, { name = "packaging" }, { name = "prettytable" }, { name = "rdflib", extra = ["html"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/32/1602c3f4ab567b6d020bdd630c348fcf25d899f9e2b7a50385b10761673c/pyshacl-0.30.1.tar.gz", hash = "sha256:df712ac961b1ee7bcf0b27fa71ecab7785a135b2af69783ce30cb05fe41bcaf5", size = 1392238, upload-time = "2025-03-14T23:28:15.702Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyshacl/pyshacl-0.30.1.tar.gz", hash = "sha256:df712ac961b1ee7bcf0b27fa71ecab7785a135b2af69783ce30cb05fe41bcaf5" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d8/12/3747eff45147d416c054881b7c0902b21ac7767b905f666c714233688c81/pyshacl-0.30.1-py3-none-any.whl", hash = "sha256:d7e0c21b25e948bb643dbc5db6258da64a90a8ac89055c1fe562b469031072aa", size = 1290522, upload-time = "2025-03-14T23:28:12.72Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyshacl/pyshacl-0.30.1-py3-none-any.whl", hash = "sha256:d7e0c21b25e948bb643dbc5db6258da64a90a8ac89055c1fe562b469031072aa" }, ] [[package]] name = "pytest" version = "8.4.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "iniconfig" }, @@ -707,61 +709,61 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pytest/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pytest/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7" }, ] [[package]] name = "pytest-cov" version = "6.2.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "coverage" }, { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pytest-cov/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pytest-cov/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5" }, ] [[package]] name = "python-dotenv" version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/python-dotenv/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/python-dotenv/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc" }, ] [[package]] name = "pyyaml" version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyyaml/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e" } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyyaml/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyyaml/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyyaml/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyyaml/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyyaml/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyyaml/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyyaml/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyyaml/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/pyyaml/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8" }, ] [[package]] name = "rdflib" version = "7.4.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "pyparsing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/ea/30bd9eb0d4a25dd0ab929153ed23698c907c6124389aa72eea5b7b703ab8/rdflib-7.4.0.tar.gz", hash = "sha256:c8ee16c31848c19c174aed96185327ea139ca3d392fac7fa882ddf5687f8f533", size = 4866588, upload-time = "2025-10-30T12:55:21.568Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/rdflib/rdflib-7.4.0.tar.gz", hash = "sha256:c8ee16c31848c19c174aed96185327ea139ca3d392fac7fa882ddf5687f8f533" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/52/9d03e93f2e00d2a07749ee90f358d08c07822819d084f08c387b7ade8b56/rdflib-7.4.0-py3-none-any.whl", hash = "sha256:0af003470404ff21bc0eb04077cc97ee96da581f2429bf42a8e163fc1c2797bc", size = 569019, upload-time = "2025-10-30T12:55:14.462Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/rdflib/rdflib-7.4.0-py3-none-any.whl", hash = "sha256:0af003470404ff21bc0eb04077cc97ee96da581f2429bf42a8e163fc1c2797bc" }, ] [package.optional-dependencies] @@ -772,171 +774,171 @@ html = [ [[package]] name = "requests" version = "2.32.4" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "certifi" }, { name = "charset-normalizer" }, { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/requests/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/requests/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c" }, ] [[package]] name = "rsa" version = "4.9.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/rsa/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/rsa/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762" }, ] [[package]] name = "ruff" version = "0.12.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4b/da/5bd7565be729e86e1442dad2c9a364ceeff82227c2dece7c29697a9795eb/ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033", size = 5242373, upload-time = "2025-08-07T19:05:47.268Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/1e/c843bfa8ad1114fab3eb2b78235dda76acd66384c663a4e0415ecc13aa1e/ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513", size = 11675315, upload-time = "2025-08-07T19:05:06.15Z" }, - { url = "https://files.pythonhosted.org/packages/24/ee/af6e5c2a8ca3a81676d5480a1025494fd104b8896266502bb4de2a0e8388/ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc", size = 12456653, upload-time = "2025-08-07T19:05:09.759Z" }, - { url = "https://files.pythonhosted.org/packages/99/9d/e91f84dfe3866fa648c10512904991ecc326fd0b66578b324ee6ecb8f725/ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec", size = 11659690, upload-time = "2025-08-07T19:05:12.551Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ac/a363d25ec53040408ebdd4efcee929d48547665858ede0505d1d8041b2e5/ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53", size = 11896923, upload-time = "2025-08-07T19:05:14.821Z" }, - { url = "https://files.pythonhosted.org/packages/58/9f/ea356cd87c395f6ade9bb81365bd909ff60860975ca1bc39f0e59de3da37/ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f", size = 11477612, upload-time = "2025-08-07T19:05:16.712Z" }, - { url = "https://files.pythonhosted.org/packages/1a/46/92e8fa3c9dcfd49175225c09053916cb97bb7204f9f899c2f2baca69e450/ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f", size = 13182745, upload-time = "2025-08-07T19:05:18.709Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c4/f2176a310f26e6160deaf661ef60db6c3bb62b7a35e57ae28f27a09a7d63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609", size = 14206885, upload-time = "2025-08-07T19:05:21.025Z" }, - { url = "https://files.pythonhosted.org/packages/87/9d/98e162f3eeeb6689acbedbae5050b4b3220754554526c50c292b611d3a63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a", size = 13639381, upload-time = "2025-08-07T19:05:23.423Z" }, - { url = "https://files.pythonhosted.org/packages/81/4e/1b7478b072fcde5161b48f64774d6edd59d6d198e4ba8918d9f4702b8043/ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb", size = 12613271, upload-time = "2025-08-07T19:05:25.507Z" }, - { url = "https://files.pythonhosted.org/packages/e8/67/0c3c9179a3ad19791ef1b8f7138aa27d4578c78700551c60d9260b2c660d/ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818", size = 12847783, upload-time = "2025-08-07T19:05:28.14Z" }, - { url = "https://files.pythonhosted.org/packages/4e/2a/0b6ac3dd045acf8aa229b12c9c17bb35508191b71a14904baf99573a21bd/ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea", size = 11702672, upload-time = "2025-08-07T19:05:30.413Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ee/f9fdc9f341b0430110de8b39a6ee5fa68c5706dc7c0aa940817947d6937e/ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3", size = 11440626, upload-time = "2025-08-07T19:05:32.492Z" }, - { url = "https://files.pythonhosted.org/packages/89/fb/b3aa2d482d05f44e4d197d1de5e3863feb13067b22c571b9561085c999dc/ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161", size = 12462162, upload-time = "2025-08-07T19:05:34.449Z" }, - { url = "https://files.pythonhosted.org/packages/18/9f/5c5d93e1d00d854d5013c96e1a92c33b703a0332707a7cdbd0a4880a84fb/ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46", size = 12913212, upload-time = "2025-08-07T19:05:36.541Z" }, - { url = "https://files.pythonhosted.org/packages/71/13/ab9120add1c0e4604c71bfc2e4ef7d63bebece0cfe617013da289539cef8/ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3", size = 11694382, upload-time = "2025-08-07T19:05:38.468Z" }, - { url = "https://files.pythonhosted.org/packages/f6/dc/a2873b7c5001c62f46266685863bee2888caf469d1edac84bf3242074be2/ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e", size = 12740482, upload-time = "2025-08-07T19:05:40.391Z" }, - { url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718, upload-time = "2025-08-07T19:05:42.866Z" }, +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033" } +wheels = [ + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/ruff/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749" }, ] [[package]] name = "setuptools" version = "80.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/80/97e25f0f1e4067677806084b7382a6ff9979f3d15119375c475c288db9d7/setuptools-80.0.0.tar.gz", hash = "sha256:c40a5b3729d58dd749c0f08f1a07d134fb8a0a3d7f87dc33e7c5e1f762138650", size = 1354221, upload-time = "2025-04-27T17:21:10.806Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/setuptools/setuptools-80.0.0.tar.gz", hash = "sha256:c40a5b3729d58dd749c0f08f1a07d134fb8a0a3d7f87dc33e7c5e1f762138650" } wheels = [ - { url = "https://files.pythonhosted.org/packages/23/63/5517029d6696ddf2bd378d46f63f479be001c31b462303170a1da57650cb/setuptools-80.0.0-py3-none-any.whl", hash = "sha256:a38f898dcd6e5380f4da4381a87ec90bd0a7eec23d204a5552e80ee3cab6bd27", size = 1240907, upload-time = "2025-04-27T17:21:09.175Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/setuptools/setuptools-80.0.0-py3-none-any.whl", hash = "sha256:a38f898dcd6e5380f4da4381a87ec90bd0a7eec23d204a5552e80ee3cab6bd27" }, ] [[package]] name = "sniffio" version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sniffio/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sniffio/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" }, ] [[package]] name = "sqlalchemy" version = "2.0.43" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d7/bc/d59b5d97d27229b0e009bd9098cd81af71c2fa5549c580a0a67b9bed0496/sqlalchemy-2.0.43.tar.gz", hash = "sha256:788bfcef6787a7764169cfe9859fe425bf44559619e1d9f56f5bddf2ebf6f417", size = 9762949, upload-time = "2025-08-11T14:24:58.438Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlalchemy/sqlalchemy-2.0.43.tar.gz", hash = "sha256:788bfcef6787a7764169cfe9859fe425bf44559619e1d9f56f5bddf2ebf6f417" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/db/20c78f1081446095450bdc6ee6cc10045fce67a8e003a5876b6eaafc5cc4/sqlalchemy-2.0.43-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:20d81fc2736509d7a2bd33292e489b056cbae543661bb7de7ce9f1c0cd6e7f24", size = 2134891, upload-time = "2025-08-11T15:51:13.019Z" }, - { url = "https://files.pythonhosted.org/packages/45/0a/3d89034ae62b200b4396f0f95319f7d86e9945ee64d2343dcad857150fa2/sqlalchemy-2.0.43-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b9fc27650ff5a2c9d490c13c14906b918b0de1f8fcbb4c992712d8caf40e83", size = 2123061, upload-time = "2025-08-11T15:51:14.319Z" }, - { url = "https://files.pythonhosted.org/packages/cb/10/2711f7ff1805919221ad5bee205971254845c069ee2e7036847103ca1e4c/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6772e3ca8a43a65a37c88e2f3e2adfd511b0b1da37ef11ed78dea16aeae85bd9", size = 3320384, upload-time = "2025-08-11T15:52:35.088Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0e/3d155e264d2ed2778484006ef04647bc63f55b3e2d12e6a4f787747b5900/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a113da919c25f7f641ffbd07fbc9077abd4b3b75097c888ab818f962707eb48", size = 3329648, upload-time = "2025-08-11T15:56:34.153Z" }, - { url = "https://files.pythonhosted.org/packages/5b/81/635100fb19725c931622c673900da5efb1595c96ff5b441e07e3dd61f2be/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4286a1139f14b7d70141c67a8ae1582fc2b69105f1b09d9573494eb4bb4b2687", size = 3258030, upload-time = "2025-08-11T15:52:36.933Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ed/a99302716d62b4965fded12520c1cbb189f99b17a6d8cf77611d21442e47/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:529064085be2f4d8a6e5fab12d36ad44f1909a18848fcfbdb59cc6d4bbe48efe", size = 3294469, upload-time = "2025-08-11T15:56:35.553Z" }, - { url = "https://files.pythonhosted.org/packages/5d/a2/3a11b06715149bf3310b55a98b5c1e84a42cfb949a7b800bc75cb4e33abc/sqlalchemy-2.0.43-cp312-cp312-win32.whl", hash = "sha256:b535d35dea8bbb8195e7e2b40059e2253acb2b7579b73c1b432a35363694641d", size = 2098906, upload-time = "2025-08-11T15:55:00.645Z" }, - { url = "https://files.pythonhosted.org/packages/bc/09/405c915a974814b90aa591280623adc6ad6b322f61fd5cff80aeaef216c9/sqlalchemy-2.0.43-cp312-cp312-win_amd64.whl", hash = "sha256:1c6d85327ca688dbae7e2b06d7d84cfe4f3fffa5b5f9e21bb6ce9d0e1a0e0e0a", size = 2126260, upload-time = "2025-08-11T15:55:02.965Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d9/13bdde6521f322861fab67473cec4b1cc8999f3871953531cf61945fad92/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc", size = 1924759, upload-time = "2025-08-11T15:39:53.024Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlalchemy/sqlalchemy-2.0.43-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:20d81fc2736509d7a2bd33292e489b056cbae543661bb7de7ce9f1c0cd6e7f24" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlalchemy/sqlalchemy-2.0.43-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b9fc27650ff5a2c9d490c13c14906b918b0de1f8fcbb4c992712d8caf40e83" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlalchemy/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6772e3ca8a43a65a37c88e2f3e2adfd511b0b1da37ef11ed78dea16aeae85bd9" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlalchemy/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a113da919c25f7f641ffbd07fbc9077abd4b3b75097c888ab818f962707eb48" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlalchemy/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4286a1139f14b7d70141c67a8ae1582fc2b69105f1b09d9573494eb4bb4b2687" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlalchemy/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:529064085be2f4d8a6e5fab12d36ad44f1909a18848fcfbdb59cc6d4bbe48efe" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlalchemy/sqlalchemy-2.0.43-cp312-cp312-win32.whl", hash = "sha256:b535d35dea8bbb8195e7e2b40059e2253acb2b7579b73c1b432a35363694641d" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlalchemy/sqlalchemy-2.0.43-cp312-cp312-win_amd64.whl", hash = "sha256:1c6d85327ca688dbae7e2b06d7d84cfe4f3fffa5b5f9e21bb6ce9d0e1a0e0e0a" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlalchemy/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc" }, ] [[package]] name = "sqlalchemy-spanner" version = "1.14.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "alembic" }, { name = "google-cloud-spanner" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1d/ea/4dc8ba08c53410eb9d97d052c75b58a78b916f4df3a32f1df226dd232f72/sqlalchemy_spanner-1.14.0.tar.gz", hash = "sha256:5e64c393c3cd2b618a14581f0d67b57cc16d557cc57d5c6da07a1b6f9d035bc8", size = 82007, upload-time = "2025-06-27T13:43:19.331Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlalchemy-spanner/sqlalchemy_spanner-1.14.0.tar.gz", hash = "sha256:5e64c393c3cd2b618a14581f0d67b57cc16d557cc57d5c6da07a1b6f9d035bc8" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/6e/b8db9b9adcd9b700f4dcc0a9874e4de417ea3db34782d60e32e3ef96904b/sqlalchemy_spanner-1.14.0-py3-none-any.whl", hash = "sha256:1f632c90a761ca081adc09bfe0c8f06cfde12e4198bf17d234e25548e6fe3281", size = 31531, upload-time = "2025-06-27T13:43:18.285Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlalchemy-spanner/sqlalchemy_spanner-1.14.0-py3-none-any.whl", hash = "sha256:1f632c90a761ca081adc09bfe0c8f06cfde12e4198bf17d234e25548e6fe3281" }, ] [[package]] name = "sqlparse" version = "0.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/40/edede8dd6977b0d3da179a342c198ed100dd2aba4be081861ee5911e4da4/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", size = 84999, upload-time = "2024-12-10T12:05:30.728Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlparse/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload-time = "2024-12-10T12:05:27.824Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/sqlparse/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca" }, ] [[package]] name = "starlette" version = "0.47.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "anyio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/57/d062573f391d062710d4088fa1369428c38d51460ab6fedff920efef932e/starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8", size = 2583948, upload-time = "2025-07-20T17:31:58.522Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/starlette/starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984, upload-time = "2025-07-20T17:31:56.738Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/starlette/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b" }, ] [[package]] name = "typing-extensions" version = "4.14.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/typing-extensions/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/typing-extensions/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76" }, ] [[package]] name = "typing-inspection" version = "0.4.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/typing-inspection/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/typing-inspection/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51" }, ] [[package]] name = "urllib3" version = "2.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/urllib3/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/urllib3/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" }, ] [[package]] name = "uvicorn" version = "0.35.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/uvicorn/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/uvicorn/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a" }, ] [package.optional-dependencies] @@ -953,80 +955,80 @@ standard = [ [[package]] name = "uvloop" version = "0.21.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741, upload-time = "2024-10-14T23:38:35.489Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/uvloop/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/4c/03f93178830dc7ce8b4cdee1d36770d2f5ebb6f3d37d354e061eefc73545/uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c", size = 1471284, upload-time = "2024-10-14T23:37:47.833Z" }, - { url = "https://files.pythonhosted.org/packages/43/3e/92c03f4d05e50f09251bd8b2b2b584a2a7f8fe600008bcc4523337abe676/uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2", size = 821349, upload-time = "2024-10-14T23:37:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/a6/ef/a02ec5da49909dbbfb1fd205a9a1ac4e88ea92dcae885e7c961847cd51e2/uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d", size = 4580089, upload-time = "2024-10-14T23:37:51.703Z" }, - { url = "https://files.pythonhosted.org/packages/06/a7/b4e6a19925c900be9f98bec0a75e6e8f79bb53bdeb891916609ab3958967/uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc", size = 4693770, upload-time = "2024-10-14T23:37:54.122Z" }, - { url = "https://files.pythonhosted.org/packages/ce/0c/f07435a18a4b94ce6bd0677d8319cd3de61f3a9eeb1e5f8ab4e8b5edfcb3/uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb", size = 4451321, upload-time = "2024-10-14T23:37:55.766Z" }, - { url = "https://files.pythonhosted.org/packages/8f/eb/f7032be105877bcf924709c97b1bf3b90255b4ec251f9340cef912559f28/uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f", size = 4659022, upload-time = "2024-10-14T23:37:58.195Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/uvloop/uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/uvloop/uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/uvloop/uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/uvloop/uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/uvloop/uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/uvloop/uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f" }, ] [[package]] name = "virtualenv" version = "20.34.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808, upload-time = "2025-08-13T14:24:07.464Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/virtualenv/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", size = 5983279, upload-time = "2025-08-13T14:24:05.111Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/virtualenv/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026" }, ] [[package]] name = "watchfiles" version = "1.1.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406, upload-time = "2025-06-15T19:06:59.42Z" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339, upload-time = "2025-06-15T19:05:24.516Z" }, - { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409, upload-time = "2025-06-15T19:05:25.469Z" }, - { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939, upload-time = "2025-06-15T19:05:26.494Z" }, - { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270, upload-time = "2025-06-15T19:05:27.466Z" }, - { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370, upload-time = "2025-06-15T19:05:28.548Z" }, - { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654, upload-time = "2025-06-15T19:05:29.997Z" }, - { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667, upload-time = "2025-06-15T19:05:31.172Z" }, - { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213, upload-time = "2025-06-15T19:05:32.299Z" }, - { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718, upload-time = "2025-06-15T19:05:33.415Z" }, - { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098, upload-time = "2025-06-15T19:05:34.534Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209, upload-time = "2025-06-15T19:05:35.577Z" }, - { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786, upload-time = "2025-06-15T19:05:36.559Z" }, - { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343, upload-time = "2025-06-15T19:05:37.5Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/watchfiles/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6" }, ] [[package]] name = "wcwidth" version = "0.2.14" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293, upload-time = "2025-09-22T16:29:53.023Z" } +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/wcwidth/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286, upload-time = "2025-09-22T16:29:51.641Z" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/wcwidth/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1" }, ] [[package]] name = "websockets" version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, - { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, - { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, - { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, - { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, - { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, - { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, - { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, - { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, +source = { registry = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/simple/" } +sdist = { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee" } +wheels = [ + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7" }, + { url = "https://us-python.pkg.dev/artifact-foundry-prod/ah-3p-staging-python/websockets/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f" }, ] From 96bd55ceb389467ebc63020414bb41dfdf3a37da Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Mon, 26 Jan 2026 14:27:11 -0800 Subject: [PATCH 09/19] cleanup --- .../datacommons_schema/SCHEMA.md | 155 ------------------ .../models/primitives/rdf.py | 44 ----- .../models/primitives/shacl.py | 32 ---- .../parsers/jsonld_parser.py | 69 -------- 4 files changed, 300 deletions(-) delete mode 100644 packages/datacommons-schema/datacommons_schema/SCHEMA.md delete mode 100644 packages/datacommons-schema/datacommons_schema/models/primitives/rdf.py delete mode 100644 packages/datacommons-schema/datacommons_schema/models/primitives/shacl.py delete mode 100644 packages/datacommons-schema/datacommons_schema/parsers/jsonld_parser.py diff --git a/packages/datacommons-schema/datacommons_schema/SCHEMA.md b/packages/datacommons-schema/datacommons_schema/SCHEMA.md deleted file mode 100644 index 767978d..0000000 --- a/packages/datacommons-schema/datacommons_schema/SCHEMA.md +++ /dev/null @@ -1,155 +0,0 @@ -# Data Commons Knowledge Graph & Schema Validator Design - -## Overview -This document outlines the design for the Data Commons Knowledge Graph (KG) and Schema Validator. The system is designed to provide a flexible, validated graph storage mechanism that ensures data integrity based on Semantic Web standards (RDF, RDFS) and custom schema definitions. - -## Core Concepts - -### 1. Knowledge Graph (KG) -The Knowledge Graph is the central repository for all schema and data nodes. -- **Single Namespace**: The KG operates under a defined namespace (e.g., `https://knowledge-graph.example.org/`). -- **Unified Storage**: Stores both "Schema" (Classes, Properties) and "Data" (Instances) as nodes in the graph. -- **Strict Validation**: No data can be added to the KG without passing validation checks. - -### 2. Schema Validator -The Validator ensures that any data entering the KG conforms to: -- **Standard Primitives**: RDF, RDFS, and XSD types. -- **Custom Schema rules**: Domain, Range, and Class existence checks defined within the KG itself. - ---- - -## Architecture - -### Class Diagram - -```mermaid -classDiagram - - class KnowledgeGraph { - +str namespace - +str default_prefix - -rdflib.Graph _graph - -SchemaValidator _validator - +validate(nodes: List[Dict]) Report - +add(nodes: List[Dict]) void - } - - class SchemaValidator { - +validate_node(node, context_graph) - } - - KnowledgeGraph --> SchemaValidator : uses -``` - -## Component Design - -### 1. `KnowledgeGraph` (Abstract Base Class) -Defines the contract for all KG implementations. - -**Attributes:** -- `namespace`: The base URI for the KG. -- `default_prefix`: The default prefix label (e.g., "ex") that maps to the KG's namespace. - -**Methods:** -- `validate(nodes: Union[Dict, List[Dict]]) -> ValidationReport` - - Checks if the input JSON-LD nodes are valid against the *current* state of the KG. - - Does *not* modify the graph. -- `add(nodes: Union[Dict, List[Dict]]) -> None` - - First calls `validate()`. - - If valid, inserts the nodes into the underlying storage. - - Raises `ValueError` or custom exception if validation fails. - -### 2. `KnowledgeGraph` (Implementation) -A reference implementation using `rdflib.Graph` in memory. - -**Storage:** -- Uses an instance of `rdflib.Graph` to store all triples. - -**Logic:** -- **Add**: - 1. Parse input JSON-LD into a temporary graph. - 2. Run validation against the *combined* knowledge (Current Graph + New Data). - - *Note*: Validation often requires checking if a referenced Class exists. If we are adding a new Class *and* an instance of it simultaneously, the validator must verify them together. - 3. If valid, merge temporary graph into main `_graph`. - -### 3. `SchemaValidationService` (The Validator) -Responsible for the core logic of checking RDF/RDFS/XSD constraints. - -**Capabilities:** -- **Primitive Checks**: - - Ensures `rdf:`, `rdfs:`, `xsd:` terms are known and valid (e.g., rejects `rdf:SomeInvalidProperty`). -- **Integrity Checks (Schema)**: - - **Classes**: Referenced types must exist (e.g., `@type: "ex:Person"` requires `ex:Person` to be defined as `rdfs:Class`). - - **Properties**: Referenced predicates must exist (e.g., `"ex:age": 30` requires `ex:age` to be defined as `rdf:Property`). - - **Domains**: Subject must match the property's `rdfs:domain`. - - **Ranges**: Object must match the property's `rdfs:range` (either a Class or XSD datatype). - -## API & Usage Specification - -### Initialization -```python -from datacommons_schema.knowledge_graph import KnowledgeGraph - -# Initialize an empty KG with a specific namespace -kg = KnowledgeGraph(namespace="http://example.org/") -``` - -### Adding Schema -Schema is just data. You add it like any other node. -```python -schema_definition = { - "@context": { - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "rdfs": "http://www.w3.org/2000/01/rdf-schema#", - "xsd": "http://www.w3.org/2001/XMLSchema#", - "ex": "http://example.org/" - }, - "@graph": [ - {"@id": "ex:Person", "@type": "rdfs:Class"}, - {"@id": "ex:name", "@type": "rdf:Property", "rdfs:domain": {"@id": "ex:Person"}, "rdfs:range": {"@id": "xsd:string"}} - ] -} - -# Validates that 'rdfs:Class' is a known primitive. -# Validates that 'xsd:string' is a known primitive. -kg.add(schema_definition) -``` - -### Adding Data -```python -person_node = { - "@context": {"ex": "http://example.org/"}, - "@id": "ex:Alice", - "@type": "ex:Person", - "ex:name": "Alice" -} - -# 1. Checks if 'ex:Person' exists in KG (It was added above). -# 2. Checks if 'ex:name' exists in KG. -# 3. Checks if 'ex:Alice' satisfies domain of 'ex:name' (ex:Person). -# 4. Checks if "Alice" satisfies range of 'ex:name' (xsd:string). -kg.add(person_node) -``` - -### Validation Failure Example -```python -invalid_node = { - "@context": {"ex": "http://example.org/"}, - "@id": "ex:Bob", - "ex:unknownProp": "Value" -} - -# Should raise ValidationException: -# "Property 'ex:unknownProp' is not defined in the Knowledge Graph." -kg.add(invalid_node) -``` - -## Implementation Roadmap - -1. **Refactor `SchemaValidationService`**: - * Decouple it from strictly taking a static schema in `__init__`. - * Allow it to accept a "Knowledge Store" interface or lookup function to check for existence of terms during validation. -2. **Implement `KnowledgeGraph` ABC**: - * Define the interface. -3. **Implement `KnowledgeGraph`**: - * Wire up `rdflib` and the Validator. diff --git a/packages/datacommons-schema/datacommons_schema/models/primitives/rdf.py b/packages/datacommons-schema/datacommons_schema/models/primitives/rdf.py deleted file mode 100644 index ce28b40..0000000 --- a/packages/datacommons-schema/datacommons_schema/models/primitives/rdf.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2025 Google LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from pydantic import BaseModel, Field - -# RDF, RDFS, and XSD Namespaces -RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" -RDFS_NS = "http://www.w3.org/2000/01/rdf-schema#" -XSD_NS = "http://www.w3.org/2001/XMLSchema#" - - -class RDFResource(BaseModel): - id: str = Field(..., alias="@id") - - -class RDFSClass(RDFResource): - type: str = Field(default=f"{RDFS_NS}Class", alias="@type") - label: str | None = Field(None, alias=f"{RDFS_NS}label") - comment: str | None = Field(None, alias=f"{RDFS_NS}comment") - subclass_of: list[str] | None = Field(None, alias=f"{RDFS_NS}subClassOf") - - -class RDFProperty(RDFResource): - type: str = Field(default=f"{RDF_NS}Property", alias="@type") - label: str | None = Field(None, alias=f"{RDFS_NS}label") - comment: str | None = Field(None, alias=f"{RDFS_NS}comment") - domain: list[str] | None = Field(None, alias=f"{RDFS_NS}domain") - range: list[str] | None = Field(None, alias=f"{RDFS_NS}range") - - -class XSDDatatype(RDFResource): - type: str = Field(default=f"{RDFS_NS}Datatype", alias="@type") - label: str | None = Field(None, alias=f"{RDFS_NS}label") diff --git a/packages/datacommons-schema/datacommons_schema/models/primitives/shacl.py b/packages/datacommons-schema/datacommons_schema/models/primitives/shacl.py deleted file mode 100644 index 92f308a..0000000 --- a/packages/datacommons-schema/datacommons_schema/models/primitives/shacl.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2025 Google LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from pydantic import BaseModel, Field - -SHACL_NS = "http://www.w3.org/ns/shacl#" - - -class SHACLProperty(BaseModel): - path: str = Field(..., alias=f"{SHACL_NS}path") - datatype: str | None = Field(None, alias=f"{SHACL_NS}datatype") - node_kind: str | None = Field(None, alias=f"{SHACL_NS}nodeKind") - min_count: int | None = Field(None, alias=f"{SHACL_NS}minCount") - max_count: int | None = Field(None, alias=f"{SHACL_NS}maxCount") - - -class SHACLNodeShape(BaseModel): - id: str = Field(..., alias="@id") - type: str = Field(default=f"{SHACL_NS}NodeShape", alias="@type") - target_class: str | None = Field(None, alias=f"{SHACL_NS}targetClass") - properties: list[SHACLProperty] | None = Field(None, alias=f"{SHACL_NS}property") diff --git a/packages/datacommons-schema/datacommons_schema/parsers/jsonld_parser.py b/packages/datacommons-schema/datacommons_schema/parsers/jsonld_parser.py deleted file mode 100644 index 3101918..0000000 --- a/packages/datacommons-schema/datacommons_schema/parsers/jsonld_parser.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright 2025 Google LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import json -from typing import IO - -from datacommons_schema.models.primitives import rdf, shacl - - -class JSONLDParseError(Exception): - """Raised when JSON-LD parsing fails""" - - -def parse_jsonld(stream: IO[str]) -> list[rdf.RDFResource]: - """Parse a JSON-LD file and return a list of RDFResource objects. - - Args: - stream: A file-like object containing JSON-LD content. - - Returns: - A list of RDFResource objects. - - Raises: - JSONLDParseError: If the JSON-LD content is invalid. - """ - try: - data = json.load(stream) - except json.JSONDecodeError as e: - raise JSONLDParseError(f"Invalid JSON: {e}") from e - - if "@graph" not in data: - raise JSONLDParseError("Missing '@graph' key in JSON-LD file.") - - context = data.get("@context", {}) - - def expand_uri(uri: str) -> str: - """Expand a URI using the context.""" - if not isinstance(uri, str): - return uri - for prefix, namespace in context.items(): - if uri.startswith(f"{prefix}:"): - return uri.replace(f"{prefix}:", namespace, 1) - return uri - - resources = [] - for node in data["@graph"]: - node_type = expand_uri(node.get("@type")) - if node_type == f"{rdf.RDFS_NS}Class": - resources.append(rdf.RDFSClass(**node)) - elif node_type == f"{rdf.RDF_NS}Property": - resources.append(rdf.RDFProperty(**node)) - elif node_type == f"{shacl.SHACL_NS}NodeShape": - resources.append(shacl.SHACLNodeShape(**node)) - else: - # For now, we only support RDFSClass, RDFProperty, and SHACLNodeShape - continue - - return resources From c3c8b6668c5be4a007260c59627620008967abfa Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Mon, 26 Jan 2026 14:49:44 -0800 Subject: [PATCH 10/19] test fixes --- .../services/schema_validation_service.py | 39 ++++--------------- ...ledge_graph.py => knowledge_graph_test.py} | 0 .../schema_validation_service_test.py | 38 ++++++++++++------ 3 files changed, 35 insertions(+), 42 deletions(-) rename packages/datacommons-schema/datacommons_schema/tests/{test_knowledge_graph.py => knowledge_graph_test.py} (100%) diff --git a/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py b/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py index 6ea8609..7480ad1 100644 --- a/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py +++ b/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py @@ -77,17 +77,17 @@ class SchemaValidationService: True """ - def __init__(self, schema_input: Union[str, Dict[str, Any]]): + def __init__(self, graph: Graph): """ Initializes the validator by parsing the schema and extracting validation rules. Args: - schema_input (Union[str, Dict[str, Any]]): The schema definition. Can be a - raw JSON-LD string or a pre-parsed dictionary. + graph (Graph): The schema definition. Example: - >>> schema_input = """ + >>> graph = Graph() + >>> graph.parse(data=""" { "@context": { "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", @@ -104,37 +104,15 @@ def __init__(self, schema_input: Union[str, Dict[str, Any]]): } ] } - """ - >>> validator = SchemaValidator(schema_input) + """, format="json-ld") + >>> validator = SchemaValidator(graph) Raises: - json.JSONDecodeError: If the input string is not valid JSON. rdflib.plugin.PluginException: If the input format is not recognized as JSON-LD. """ - self.schema_graph = self._load_graph(schema_input) + self.schema_graph = graph self.rules = self._extract_rules() - def _load_graph(self, jsonld_input: Union[str, Dict[str, Any], Graph]) -> Graph: - """ - Parses raw JSON-LD input into an rdflib Graph. - - This helper handles both dictionary and string inputs, ensuring consistent - graph instantiation regardless of the source format. - - Args: - jsonld_input (Union[str, Dict[str, Any]]): The JSON-LD content. - - Returns: - rdflib.Graph: An in-memory RDF graph containing the parsed triples. - """ - if isinstance(jsonld_input, Graph): - return jsonld_input - - g = Graph() - data = json.dumps(jsonld_input) if isinstance(jsonld_input, dict) else jsonld_input - g.parse(data=data, format="json-ld") - return g - def _extract_rules(self) -> SchemaDefinition: """ Compiles high-level validation rules from the raw schema graph. @@ -322,7 +300,7 @@ def validate_schema_integrity(self, context_classes: Optional[Set[URIRef]] = Non errors=errors ) - def validate(self, data_input: Union[str, Dict[str, Any], Graph], context_graph: Optional[Graph] = None) -> ValidationReport: + def validate(self, data_graph: Graph, context_graph: Optional[Graph] = None) -> ValidationReport: """ Validates a data file against the loaded schema rules. @@ -343,7 +321,6 @@ def validate(self, data_input: Union[str, Dict[str, Any], Graph], context_graph: ValidationReport: A detailed Pydantic report containing the overall validity status, error counts, and a list of specific `ValidationError` objects for any violations found. """ - data_graph = self._load_graph(data_input) errors = [] def has_type(resource, type_uri): diff --git a/packages/datacommons-schema/datacommons_schema/tests/test_knowledge_graph.py b/packages/datacommons-schema/datacommons_schema/tests/knowledge_graph_test.py similarity index 100% rename from packages/datacommons-schema/datacommons_schema/tests/test_knowledge_graph.py rename to packages/datacommons-schema/datacommons_schema/tests/knowledge_graph_test.py diff --git a/packages/datacommons-schema/datacommons_schema/tests/validator/schema_validation_service_test.py b/packages/datacommons-schema/datacommons_schema/tests/validator/schema_validation_service_test.py index 1bd59df..17ac0a3 100644 --- a/packages/datacommons-schema/datacommons_schema/tests/validator/schema_validation_service_test.py +++ b/packages/datacommons-schema/datacommons_schema/tests/validator/schema_validation_service_test.py @@ -1,7 +1,8 @@ import pytest -from rdflib import URIRef, XSD +from rdflib import URIRef, XSD, Graph from pydantic import BaseModel from typing import List +import json from datacommons_schema.services.schema_validation_service import SchemaValidationService @@ -20,6 +21,11 @@ class SchemaReport(BaseModel): # FIXTURES # ========================================== +def load_graph(schema: dict) -> Graph: + graph = Graph() + graph.parse(data=json.dumps(schema), format="json-ld") + return graph + @pytest.fixture def sample_schema_dict() -> dict: """ @@ -61,12 +67,15 @@ def sample_schema_dict() -> dict: @pytest.fixture def schema_service(sample_schema_dict: dict) -> SchemaValidationService: """Initializes the service with the valid sample schema.""" - return SchemaValidationService(sample_schema_dict) + graph = Graph() + graph.parse(data=json.dumps(sample_schema_dict), format="json-ld") + return SchemaValidationService(graph) @pytest.fixture -def valid_data_packet() -> dict: +def valid_data_packet() -> Graph: """Data that perfectly conforms to sample_schema_dict.""" - return { + graph = Graph() + graph.parse(data=json.dumps({ "@context": { "ex": "http://example.org/", "xsd": "http://www.w3.org/2001/XMLSchema#" @@ -79,7 +88,8 @@ def valid_data_packet() -> dict: "@id": "ex:Google", "@type": "ex:Organization" } - } + }), format="json-ld") + return graph # ========================================== # TESTS @@ -119,7 +129,8 @@ def test_schema_integrity_invalid(): {"@id": "ex:hasPet", "@type": "rdf:Propertyzzz", "rdfs:domain": {"@id": "ex:GhostClass"}} ] } - service = SchemaValidationService(bad_schema) + graph = load_graph(bad_schema) + service = SchemaValidationService(graph) report = service.validate_schema_integrity() assert report.is_valid is False @@ -144,7 +155,8 @@ def test_schema_integrity_invalid_xsd(): } ] } - service = SchemaValidationService(bad_schema) + graph = load_graph(bad_schema) + service = SchemaValidationService(graph) report = service.validate_schema_integrity() assert report.is_valid is False @@ -164,7 +176,8 @@ def test_validate_undefined_property(schema_service): "@id": "ex:Bob", "ex:favoriteColor": "Blue" # Not in schema } - report = schema_service.validate(data) + graph = load_graph(data) + report = schema_service.validate(graph) assert report.is_valid is False assert report.errors[0].rule_type == "Undefined Property" @@ -178,7 +191,8 @@ def test_validate_domain_violation(schema_service): "@type": "ex:Organization", # Schema says 'age' belongs to 'Person' "ex:age": 100 } - report = schema_service.validate(data) + graph = load_graph(data) + report = schema_service.validate(graph) assert report.is_valid is False error = report.errors[0] @@ -193,7 +207,8 @@ def test_validate_range_violation_literal(schema_service): "@type": "ex:Person", "ex:age": "Thirty" # Should be integer } - report = schema_service.validate(data) + graph = load_graph(data) + report = schema_service.validate(graph) assert report.is_valid is False assert report.errors[0].rule_type == "Range Violation" @@ -210,7 +225,8 @@ def test_validate_range_violation_class(schema_service): "@type": "ex:Person" # Schema says worksFor range is Organization } } - report = schema_service.validate(data) + graph = load_graph(data) + report = schema_service.validate(graph) assert report.is_valid is False assert report.errors[0].rule_type == "Range Violation" From b0c5955367cddece2b640a40f7d833ba87f26c9d Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Mon, 26 Jan 2026 14:51:31 -0800 Subject: [PATCH 11/19] Apply suggestions from code review Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../services/schema_validation_service.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py b/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py index 7480ad1..c57e16d 100644 --- a/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py +++ b/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py @@ -370,11 +370,21 @@ def has_type(resource, type_uri): # Case A: Literal if isinstance(o, Literal): - # For strict XSD validation, you might compare o.datatype - if o.datatype != required_range and required_range != XSD.string: + # If the range is a class URI, a literal is invalid. + if not (str(required_range).startswith(str(XSD)) or required_range == RDFS.Literal): errors.append(ValidationError( subject=str(s), predicate=str(p), object=str(o), - message=f"Literal value must be datatype <{required_range}>", + message=f"Object must be a resource of type <{required_range}>, but a literal was found.", + rule_type="Range Violation" + )) + continue + + # An untyped literal is compatible with xsd:string. Otherwise, datatypes must match. + effective_datatype = o.datatype or XSD.string + if effective_datatype != required_range: + errors.append(ValidationError( + subject=str(s), predicate=str(p), object=str(o), + message=f"Literal has datatype <{effective_datatype}> but range requires <{required_range}>.", rule_type="Range Violation" )) From b6b33e2a46063545378666942d33f72627c0669d Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Mon, 26 Jan 2026 14:51:52 -0800 Subject: [PATCH 12/19] Apply suggestions from code review Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../services/schema_validation_service.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py b/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py index c57e16d..e343e37 100644 --- a/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py +++ b/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py @@ -390,8 +390,15 @@ def has_type(resource, type_uri): # Case B: Resource (URI) elif isinstance(o, URIRef): + # If the range expects a literal, a resource is invalid. + if str(required_range).startswith(str(XSD)) or required_range == RDFS.Literal: + errors.append(ValidationError( + subject=str(s), predicate=str(p), object=str(o), + message=f"Object must be a literal with datatype <{required_range}>, but a resource was found.", + rule_type="Range Violation" + )) # Query: Does Object 'o' have type 'required_range'? - if not has_type(o, required_range): + elif not has_type(o, required_range): errors.append(ValidationError( subject=str(s), predicate=str(p), object=str(o), message=f"Object must be of type <{required_range}>", From 08e07544bed86f391068cc67c5e45d071a0f0399 Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Mon, 26 Jan 2026 14:53:26 -0800 Subject: [PATCH 13/19] test cases --- .../datacommons_schema/tests/knowledge_graph_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/datacommons-schema/datacommons_schema/tests/knowledge_graph_test.py b/packages/datacommons-schema/datacommons_schema/tests/knowledge_graph_test.py index 46468eb..d691217 100644 --- a/packages/datacommons-schema/datacommons_schema/tests/knowledge_graph_test.py +++ b/packages/datacommons-schema/datacommons_schema/tests/knowledge_graph_test.py @@ -174,4 +174,4 @@ def test_add_invalid_data_range_violation(): with pytest.raises(ValueError) as excinfo: kg.add(dog_node) - assert "Literal value must be datatype " in str(excinfo.value) \ No newline at end of file + assert "Object must be a resource of type , but a literal was found" in str(excinfo.value) \ No newline at end of file From 05a9386369ba5f8aa65e737da11701d8128921e0 Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Mon, 26 Jan 2026 14:55:44 -0800 Subject: [PATCH 14/19] refactor --- .../datacommons_schema/knowledge_graph.py | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/datacommons-schema/datacommons_schema/knowledge_graph.py b/packages/datacommons-schema/datacommons_schema/knowledge_graph.py index 4f63333..e9e764f 100644 --- a/packages/datacommons-schema/datacommons_schema/knowledge_graph.py +++ b/packages/datacommons-schema/datacommons_schema/knowledge_graph.py @@ -16,18 +16,15 @@ def __init__(self, namespace: str, default_prefix: str = "ex"): # Bind the default prefix to the namespace self._graph.bind(self.default_prefix, self.namespace) - def validate(self, nodes: Union[Dict, List[Dict]]) -> ValidationReport: - # 1. Parse new nodes into a temp graph - temp_graph = self._load_graph(nodes) - - # 2. extract existing rules to serve as context + def validate(self, new_graph: Graph) -> ValidationReport: + # 1. Extract existing rules to serve as context # Note: In a real high-perf scenario, we would cache the 'rules' # instead of re-extracting them from self._graph every time. main_validator = SchemaValidationService(self._graph) - # 3. Check Schema Integrity of NEW nodes + # 2. Check Schema Integrity of NEW nodes # Use existing classes as context so we don't flag references to existing classes as "Undefined" - temp_validator = SchemaValidationService(temp_graph) + temp_validator = SchemaValidationService(new_graph) schema_report = temp_validator.validate_schema_integrity(context_classes=main_validator.rules.classes) if not schema_report.is_valid: @@ -47,18 +44,18 @@ def validate(self, nodes: Union[Dict, List[Dict]]) -> ValidationReport: errors=schem_errors ) - # 4. Check Data Validation - # Validates 'temp_graph' against 'self._graph' rules and context - return main_validator.validate(temp_graph, context_graph=self._graph) + # 3. Check Data Validation + # Validates 'new_graph' against 'self._graph' rules and context + return main_validator.validate(new_graph, context_graph=self._graph) def add(self, nodes: Union[Dict, List[Dict]]) -> None: - report = self.validate(nodes) + temp_graph = self._load_graph(nodes) + report = self.validate(temp_graph) if not report.is_valid: error_msgs = "\n".join([f"{e.subject}: {e.message}" for e in report.errors]) raise ValueError(f"Cannot add invalid nodes:\n{error_msgs}") # If valid, merge - temp_graph = self._load_graph(nodes) self._graph += temp_graph def _load_graph(self, jsonld_input: Union[Dict, List[Dict], str]) -> Graph: From c3884f0208edbe4fcaf3e698796a677166365c57 Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Mon, 26 Jan 2026 15:03:47 -0800 Subject: [PATCH 15/19] header --- .../datacommons_schema/knowledge_graph.py | 15 ++++++++++++++- .../services/schema_validation_service.py | 2 +- .../tests/knowledge_graph_test.py | 14 ++++++++++++++ .../validator/schema_validation_service_test.py | 14 ++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/packages/datacommons-schema/datacommons_schema/knowledge_graph.py b/packages/datacommons-schema/datacommons_schema/knowledge_graph.py index e9e764f..83aaea5 100644 --- a/packages/datacommons-schema/datacommons_schema/knowledge_graph.py +++ b/packages/datacommons-schema/datacommons_schema/knowledge_graph.py @@ -1,10 +1,23 @@ +# Copyright 2026 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + from typing import List, Dict, Union, Optional from rdflib import Graph import json from datacommons_schema.services.schema_validation_service import SchemaValidationService, SchemaReport, ValidationReport, ValidationError - class KnowledgeGraph: """ An in-memory Knowledge Graph using rdflib. diff --git a/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py b/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py index e343e37..b6e1a6e 100644 --- a/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py +++ b/packages/datacommons-schema/datacommons_schema/services/schema_validation_service.py @@ -1,4 +1,4 @@ -# Copyright 2025 Google LLC. +# Copyright 2026 Google LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/packages/datacommons-schema/datacommons_schema/tests/knowledge_graph_test.py b/packages/datacommons-schema/datacommons_schema/tests/knowledge_graph_test.py index d691217..d40c182 100644 --- a/packages/datacommons-schema/datacommons_schema/tests/knowledge_graph_test.py +++ b/packages/datacommons-schema/datacommons_schema/tests/knowledge_graph_test.py @@ -1,3 +1,17 @@ +# Copyright 2026 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import pytest from rdflib import Graph, URIRef, Literal, RDF, RDFS, XSD from datacommons_schema.knowledge_graph import KnowledgeGraph diff --git a/packages/datacommons-schema/datacommons_schema/tests/validator/schema_validation_service_test.py b/packages/datacommons-schema/datacommons_schema/tests/validator/schema_validation_service_test.py index 17ac0a3..85c38a4 100644 --- a/packages/datacommons-schema/datacommons_schema/tests/validator/schema_validation_service_test.py +++ b/packages/datacommons-schema/datacommons_schema/tests/validator/schema_validation_service_test.py @@ -1,3 +1,17 @@ +# Copyright 2026 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import pytest from rdflib import URIRef, XSD, Graph from pydantic import BaseModel From 3cf2e152fe5b4865ce454642bb329cdf21bca048 Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Mon, 26 Jan 2026 15:52:59 -0800 Subject: [PATCH 16/19] fixed readme --- packages/datacommons-schema/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/datacommons-schema/README.md b/packages/datacommons-schema/README.md index 350b096..e078a4c 100644 --- a/packages/datacommons-schema/README.md +++ b/packages/datacommons-schema/README.md @@ -76,7 +76,7 @@ Knowledge graph implementation using `rdflib.Graph` in memory. - *Note*: Validation often requires checking if a referenced Class exists. If we are adding a new Class *and* an instance of it simultaneously, the validator must verify them together. 3. If valid, merge temporary graph into main `_graph`. -### 3. `SchemaValidationService` (The Validator) +### 2. `SchemaValidationService` (The Validator) Responsible for the core logic of checking RDF/RDFS/XSD constraints. **Capabilities:** From 8ae1388c19d0116a4fd1f273f624271260bd463c Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Mon, 26 Jan 2026 15:53:38 -0800 Subject: [PATCH 17/19] readme fixes --- packages/datacommons-schema/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/datacommons-schema/README.md b/packages/datacommons-schema/README.md index e078a4c..2733c3f 100644 --- a/packages/datacommons-schema/README.md +++ b/packages/datacommons-schema/README.md @@ -170,7 +170,7 @@ The `datacommons-schema mcf2jsonld` command converts MCF files to JSON-LD format datacommons-schema mcf2jsonld input.mcf # With custom namespace -datacommons mcf2jsonld input.mcf --namespace "schema:https://schema.org/" +datacommons-schema mcf2jsonld input.mcf --namespace "schema:https://schema.org/" # Output to file with compact format datacommons-schema mcf2jsonld input.mcf -o output.jsonld -c From 0bce7e789366bbed9a9214d95266074d8ac3e64f Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Mon, 26 Jan 2026 15:53:52 -0800 Subject: [PATCH 18/19] readme fixes --- packages/datacommons-schema/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/datacommons-schema/README.md b/packages/datacommons-schema/README.md index 2733c3f..7c90ed1 100644 --- a/packages/datacommons-schema/README.md +++ b/packages/datacommons-schema/README.md @@ -250,7 +250,3 @@ When contributing to this module: ## License [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) - - - - From 334411a75831c26cd6d75592de3be305237018f8 Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Mon, 26 Jan 2026 15:54:51 -0800 Subject: [PATCH 19/19] readme fixes --- packages/datacommons-schema/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/datacommons-schema/README.md b/packages/datacommons-schema/README.md index 7c90ed1..183d635 100644 --- a/packages/datacommons-schema/README.md +++ b/packages/datacommons-schema/README.md @@ -224,13 +224,13 @@ jsonld = mcf_nodes_to_jsonld(mcf_nodes, compact=True) ```bash # Convert with default settings -datacommons mcf2jsonld data.mcf +datacommons-schema mcf2jsonld data.mcf # Convert with custom namespace and output file -datacommons mcf2jsonld data.mcf -n "dc:https://datacommons.org/" -o output.jsonld +datacommons-schema mcf2jsonld data.mcf -n "dc:https://datacommons.org/" -o output.jsonld # Generate compact output -datacommons mcf2jsonld data.mcf -c +datacommons-schema mcf2jsonld data.mcf -c ``` ## Dependencies