tusk-drift-python-sdk 0.1.22__tar.gz → 0.1.24__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/PKG-INFO +12 -10
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/README.md +5 -5
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/communication/__init__.py +0 -13
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/communication/communicator.py +73 -11
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/communication/types.py +3 -29
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/content_type_utils.py +1 -1
- tusk_drift_python_sdk-0.1.24/drift/core/coverage_server.py +274 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/drift_sdk.py +51 -16
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/mode_utils.py +3 -1
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/rust_core_binding.py +18 -7
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/tracing/otel_converter.py +3 -6
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/aiohttp/e2e-tests/src/app.py +23 -0
- tusk_drift_python_sdk-0.1.24/drift/instrumentation/django/e2e-tests/src/views.py +216 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/django/instrumentation.py +43 -5
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/django/middleware.py +3 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/e2e_common/base_runner.py +6 -6
- tusk_drift_python_sdk-0.1.24/drift/instrumentation/e2e_common/external_http.py +55 -0
- tusk_drift_python_sdk-0.1.24/drift/instrumentation/e2e_common/mock_upstream/mock_server.py +348 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/fastapi/e2e-tests/src/app.py +31 -11
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/fastapi/instrumentation.py +3 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/flask/e2e-tests/src/app.py +39 -9
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/grpc/e2e-tests/src/greeter_pb2_grpc.py +1 -1
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/httpx/e2e-tests/src/app.py +40 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/psycopg/e2e-tests/src/app.py +30 -8
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/psycopg/instrumentation.py +3 -1
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/psycopg2/e2e-tests/src/app.py +64 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/psycopg2/e2e-tests/src/test_requests.py +3 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/psycopg2/instrumentation.py +2 -1
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/requests/e2e-tests/src/app.py +25 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/socket/instrumentation.py +18 -3
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/urllib/e2e-tests/src/app.py +25 -2
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/urllib/instrumentation.py +11 -7
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/urllib3/e2e-tests/src/app.py +147 -22
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/urllib3/e2e-tests/src/test_requests.py +22 -11
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/urllib3/instrumentation.py +207 -90
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/utils/psycopg_utils.py +63 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/utils/serialization.py +11 -1
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/wsgi/handler.py +3 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/pyproject.toml +8 -5
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/tusk_drift_python_sdk.egg-info/PKG-INFO +12 -10
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/tusk_drift_python_sdk.egg-info/SOURCES.txt +3 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/tusk_drift_python_sdk.egg-info/requires.txt +7 -4
- tusk_drift_python_sdk-0.1.22/drift/instrumentation/django/e2e-tests/src/views.py +0 -153
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/LICENSE +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/batch_processor.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/config.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/data_normalization.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/json_schema_helper.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/logger.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/metrics.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/mock_utils.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/protobuf_utils.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/resilience.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/sampling.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/span_serialization.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/trace_blocking_manager.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/tracing/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/tracing/adapters/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/tracing/adapters/api.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/tracing/adapters/base.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/tracing/adapters/filesystem.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/tracing/adapters/memory.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/tracing/span_exporter.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/tracing/span_utils.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/tracing/td_attributes.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/tracing/td_span_processor.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/types.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/aiohttp/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/aiohttp/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/aiohttp/e2e-tests/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/aiohttp/instrumentation.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/base.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/datetime/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/datetime/instrumentation.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/django/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/django/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/django/e2e-tests/src/app.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/django/e2e-tests/src/settings.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/django/e2e-tests/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/django/e2e-tests/src/urls.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/django/html_utils.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/e2e_common/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/e2e_common/test_utils.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/fastapi/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/fastapi/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/fastapi/e2e-tests/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/flask/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/flask/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/flask/e2e-tests/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/flask/instrumentation.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/grpc/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/grpc/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/grpc/e2e-tests/src/app.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/grpc/e2e-tests/src/greeter_pb2.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/grpc/e2e-tests/src/grpc_server.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/grpc/e2e-tests/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/grpc/instrumentation.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/grpc/types.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/grpc/utils.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/http/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/http/transform_engine.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/httpx/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/httpx/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/httpx/e2e-tests/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/httpx/instrumentation.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/kinde/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/kinde/instrumentation.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/psycopg/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/psycopg/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/psycopg/e2e-tests/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/psycopg/mocks.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/psycopg/wrappers.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/psycopg2/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/psycopg2/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/pyjwt/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/pyjwt/instrumentation.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/redis/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/redis/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/redis/e2e-tests/src/app.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/redis/e2e-tests/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/redis/instrumentation.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/registry.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/requests/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/requests/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/requests/e2e-tests/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/requests/instrumentation.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/socket/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/sqlalchemy/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/sqlalchemy/context.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/sqlalchemy/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/sqlalchemy/e2e-tests/src/app.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/sqlalchemy/e2e-tests/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/sqlalchemy/instrumentation.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/urllib/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/urllib/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/urllib/e2e-tests/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/urllib3/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/urllib3/e2e-tests/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/wsgi/__init__.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/wsgi/instrumentation.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/wsgi/response_capture.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/instrumentation/wsgi/utilities.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/django-postgres/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/django-postgres/src/app.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/django-postgres/src/settings.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/django-postgres/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/django-postgres/src/urls.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/django-postgres/src/views.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/django-redis/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/django-redis/src/app.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/django-redis/src/settings.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/django-redis/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/django-redis/src/urls.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/django-redis/src/views.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/fastapi-postgres/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/fastapi-postgres/src/app.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/fastapi-postgres/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/fastapi-sqlalchemy/entrypoint.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/fastapi-sqlalchemy/src/app.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/stack-tests/fastapi-sqlalchemy/src/test_requests.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/version.py +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/setup.cfg +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/tusk_drift_python_sdk.egg-info/dependency_links.txt +0 -0
- {tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/tusk_drift_python_sdk.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tusk-drift-python-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.24
|
|
4
4
|
Summary: Python SDK for Tusk Drift instrumentation and replay
|
|
5
5
|
Author-email: Tusk <support@usetusk.ai>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -23,10 +23,10 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
23
23
|
Requires-Python: >=3.9
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
|
-
Requires-Dist: protobuf>=
|
|
26
|
+
Requires-Dist: protobuf>=3.20.0
|
|
27
27
|
Requires-Dist: PyYAML>=6.0
|
|
28
|
-
Requires-Dist: requests>=2.
|
|
29
|
-
Requires-Dist: tusk-drift-schemas>=0.1.
|
|
28
|
+
Requires-Dist: requests>=2.28.0
|
|
29
|
+
Requires-Dist: tusk-drift-schemas>=0.1.34
|
|
30
30
|
Requires-Dist: aiohttp>=3.9.0
|
|
31
31
|
Requires-Dist: aiofiles>=23.0.0
|
|
32
32
|
Requires-Dist: opentelemetry-api>=1.20.0
|
|
@@ -41,8 +41,10 @@ Requires-Dist: uvicorn>=0.34.2; extra == "fastapi"
|
|
|
41
41
|
Requires-Dist: starlette<0.42.0; extra == "fastapi"
|
|
42
42
|
Provides-Extra: django
|
|
43
43
|
Requires-Dist: Django>=4.2; extra == "django"
|
|
44
|
+
Provides-Extra: coverage
|
|
45
|
+
Requires-Dist: coverage>=7.0.0; extra == "coverage"
|
|
44
46
|
Provides-Extra: rust
|
|
45
|
-
Requires-Dist: drift-core-python>=0.1.
|
|
47
|
+
Requires-Dist: drift-core-python>=0.1.9; extra == "rust"
|
|
46
48
|
Provides-Extra: dev
|
|
47
49
|
Requires-Dist: Flask>=3.1.2; extra == "dev"
|
|
48
50
|
Requires-Dist: fastapi>=0.115.6; extra == "dev"
|
|
@@ -61,13 +63,13 @@ Dynamic: license-file
|
|
|
61
63
|
|
|
62
64
|
<p align="center">
|
|
63
65
|
<a href="https://pypi.org/project/tusk-drift-python-sdk/"><img src="https://img.shields.io/pypi/v/tusk-drift-python-sdk" alt="PyPI version"></a>
|
|
66
|
+
<a href="https://pypi.org/project/tusk-drift-python-sdk/"><img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/tusk-drift-python-sdk"></a>
|
|
64
67
|
<a href="https://opensource.org/licenses/Apache-2.0"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="License: Apache 2.0"></a>
|
|
65
|
-
<a href="https://github.com/Use-Tusk/drift-python-sdk/commits/main/"><img src="https://img.shields.io/github/last-commit/Use-Tusk/drift-python-sdk" alt="GitHub last commit"></a>
|
|
66
68
|
<a href="https://x.com/usetusk"><img src="https://img.shields.io/twitter/url?url=https%3A%2F%2Fx.com%2Fusetusk&style=flat&logo=x&label=Tusk&color=BF40BF" alt="Tusk X account"></a>
|
|
67
69
|
<a href="https://join.slack.com/t/tusk-community/shared_invite/zt-3fve1s7ie-NAAUn~UpHsf1m_2tdoGjsQ"><img src="https://img.shields.io/badge/slack-badge?style=flat&logo=slack&label=Tusk&color=BF40BF" alt="Tusk Community Slack"></a>
|
|
68
70
|
</p>
|
|
69
71
|
|
|
70
|
-
The Python Tusk Drift SDK enables fast and deterministic API testing by capturing and replaying API calls made to/from your service. Automatically record real-world API calls, then replay them as tests using the [Tusk CLI](https://github.com/Use-Tusk/tusk-
|
|
72
|
+
The Python Tusk Drift SDK enables fast and deterministic API testing by capturing and replaying API calls made to/from your service. Automatically record real-world API calls, then replay them as tests using the [Tusk CLI](https://github.com/Use-Tusk/tusk-cli) to find regressions. During replay, all outbound requests are intercepted with recorded data to ensure consistent behavior without side-effects.
|
|
71
73
|
|
|
72
74
|
<div align="center">
|
|
73
75
|
|
|
@@ -124,7 +126,7 @@ If you're using packages or versions not listed above, please create an issue wi
|
|
|
124
126
|
|
|
125
127
|
### Step 1: Install the CLI
|
|
126
128
|
|
|
127
|
-
First, install the Tusk Drift CLI by following our [CLI installation guide](https://github.com/Use-Tusk/tusk-
|
|
129
|
+
First, install the Tusk Drift CLI by following our [CLI installation guide](https://github.com/Use-Tusk/tusk-cli?tab=readme-ov-file#install).
|
|
128
130
|
|
|
129
131
|
### Step 2: Set up Tusk Drift
|
|
130
132
|
|
|
@@ -135,7 +137,7 @@ Use our AI agent to automatically set up Tusk Drift for your service:
|
|
|
135
137
|
```bash
|
|
136
138
|
cd path/to/your/service
|
|
137
139
|
export ANTHROPIC_API_KEY=your-api-key
|
|
138
|
-
tusk setup
|
|
140
|
+
tusk drift setup
|
|
139
141
|
```
|
|
140
142
|
|
|
141
143
|
The agent will analyze your codebase, install the SDK, instrument it into your application, create configuration files, and test the setup with recording and replay.
|
|
@@ -156,7 +158,7 @@ Alternatively, you can set up Tusk Drift manually:
|
|
|
156
158
|
|
|
157
159
|
*For more information about Rust acceleration, refer to [this doc](docs/rust-core-bindings).*
|
|
158
160
|
|
|
159
|
-
2. Create configuration: Run `tusk init` to create your `.tusk/config.yaml` config file interactively, or create it manually per the [configuration docs](https://github.com/Use-Tusk/tusk-
|
|
161
|
+
2. Create configuration: Run `tusk init` to create your `.tusk/config.yaml` config file interactively, or create it manually per the [configuration docs](https://github.com/Use-Tusk/tusk-cli/blob/main/docs/drift/configuration.md).
|
|
160
162
|
|
|
161
163
|
3. Initialize the SDK: Refer to the [initialization guide](docs/initialization.md) to instrument the SDK in your service.
|
|
162
164
|
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
<a href="https://pypi.org/project/tusk-drift-python-sdk/"><img src="https://img.shields.io/pypi/v/tusk-drift-python-sdk" alt="PyPI version"></a>
|
|
7
|
+
<a href="https://pypi.org/project/tusk-drift-python-sdk/"><img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/tusk-drift-python-sdk"></a>
|
|
7
8
|
<a href="https://opensource.org/licenses/Apache-2.0"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="License: Apache 2.0"></a>
|
|
8
|
-
<a href="https://github.com/Use-Tusk/drift-python-sdk/commits/main/"><img src="https://img.shields.io/github/last-commit/Use-Tusk/drift-python-sdk" alt="GitHub last commit"></a>
|
|
9
9
|
<a href="https://x.com/usetusk"><img src="https://img.shields.io/twitter/url?url=https%3A%2F%2Fx.com%2Fusetusk&style=flat&logo=x&label=Tusk&color=BF40BF" alt="Tusk X account"></a>
|
|
10
10
|
<a href="https://join.slack.com/t/tusk-community/shared_invite/zt-3fve1s7ie-NAAUn~UpHsf1m_2tdoGjsQ"><img src="https://img.shields.io/badge/slack-badge?style=flat&logo=slack&label=Tusk&color=BF40BF" alt="Tusk Community Slack"></a>
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
|
-
The Python Tusk Drift SDK enables fast and deterministic API testing by capturing and replaying API calls made to/from your service. Automatically record real-world API calls, then replay them as tests using the [Tusk CLI](https://github.com/Use-Tusk/tusk-
|
|
13
|
+
The Python Tusk Drift SDK enables fast and deterministic API testing by capturing and replaying API calls made to/from your service. Automatically record real-world API calls, then replay them as tests using the [Tusk CLI](https://github.com/Use-Tusk/tusk-cli) to find regressions. During replay, all outbound requests are intercepted with recorded data to ensure consistent behavior without side-effects.
|
|
14
14
|
|
|
15
15
|
<div align="center">
|
|
16
16
|
|
|
@@ -67,7 +67,7 @@ If you're using packages or versions not listed above, please create an issue wi
|
|
|
67
67
|
|
|
68
68
|
### Step 1: Install the CLI
|
|
69
69
|
|
|
70
|
-
First, install the Tusk Drift CLI by following our [CLI installation guide](https://github.com/Use-Tusk/tusk-
|
|
70
|
+
First, install the Tusk Drift CLI by following our [CLI installation guide](https://github.com/Use-Tusk/tusk-cli?tab=readme-ov-file#install).
|
|
71
71
|
|
|
72
72
|
### Step 2: Set up Tusk Drift
|
|
73
73
|
|
|
@@ -78,7 +78,7 @@ Use our AI agent to automatically set up Tusk Drift for your service:
|
|
|
78
78
|
```bash
|
|
79
79
|
cd path/to/your/service
|
|
80
80
|
export ANTHROPIC_API_KEY=your-api-key
|
|
81
|
-
tusk setup
|
|
81
|
+
tusk drift setup
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
The agent will analyze your codebase, install the SDK, instrument it into your application, create configuration files, and test the setup with recording and replay.
|
|
@@ -99,7 +99,7 @@ Alternatively, you can set up Tusk Drift manually:
|
|
|
99
99
|
|
|
100
100
|
*For more information about Rust acceleration, refer to [this doc](docs/rust-core-bindings).*
|
|
101
101
|
|
|
102
|
-
2. Create configuration: Run `tusk init` to create your `.tusk/config.yaml` config file interactively, or create it manually per the [configuration docs](https://github.com/Use-Tusk/tusk-
|
|
102
|
+
2. Create configuration: Run `tusk init` to create your `.tusk/config.yaml` config file interactively, or create it manually per the [configuration docs](https://github.com/Use-Tusk/tusk-cli/blob/main/docs/drift/configuration.md).
|
|
103
103
|
|
|
104
104
|
3. Initialize the SDK: Refer to the [initialization guide](docs/initialization.md) to instrument the SDK in your service.
|
|
105
105
|
|
{tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/communication/__init__.py
RENAMED
|
@@ -6,28 +6,18 @@ for replay testing. Communication uses Protocol Buffers over Unix sockets or TCP
|
|
|
6
6
|
|
|
7
7
|
from .communicator import CommunicatorConfig, ProtobufCommunicator
|
|
8
8
|
from .types import (
|
|
9
|
-
CliMessage,
|
|
10
|
-
CLIMessageType,
|
|
11
9
|
ConnectRequest,
|
|
12
10
|
ConnectResponse,
|
|
13
11
|
GetMockRequest,
|
|
14
12
|
GetMockResponse,
|
|
15
|
-
MessageType,
|
|
16
13
|
MockRequestInput,
|
|
17
14
|
MockResponseOutput,
|
|
18
|
-
# Protobuf types (re-exported)
|
|
19
|
-
SdkMessage,
|
|
20
|
-
SDKMessageType,
|
|
21
15
|
dict_to_span,
|
|
22
16
|
extract_response_data,
|
|
23
17
|
span_to_proto,
|
|
24
18
|
)
|
|
25
19
|
|
|
26
20
|
__all__ = [
|
|
27
|
-
# Message types
|
|
28
|
-
"MessageType",
|
|
29
|
-
"SDKMessageType",
|
|
30
|
-
"CLIMessageType",
|
|
31
21
|
# Request/Response types
|
|
32
22
|
"ConnectRequest",
|
|
33
23
|
"ConnectResponse",
|
|
@@ -35,9 +25,6 @@ __all__ = [
|
|
|
35
25
|
"GetMockResponse",
|
|
36
26
|
"MockRequestInput",
|
|
37
27
|
"MockResponseOutput",
|
|
38
|
-
# Protobuf types
|
|
39
|
-
"SdkMessage",
|
|
40
|
-
"CliMessage",
|
|
41
28
|
# Utilities
|
|
42
29
|
"span_to_proto",
|
|
43
30
|
"dict_to_span",
|
|
@@ -10,24 +10,31 @@ import traceback
|
|
|
10
10
|
from dataclasses import dataclass
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
|
-
from tusk.drift.core.v1 import
|
|
13
|
+
from tusk.drift.core.v1 import (
|
|
14
|
+
BranchInfo,
|
|
15
|
+
CliMessage,
|
|
16
|
+
CoverageSnapshotResponse,
|
|
17
|
+
FileCoverageData,
|
|
18
|
+
InstrumentationVersionMismatchAlert,
|
|
19
|
+
MessageType,
|
|
20
|
+
SdkMessage,
|
|
21
|
+
SendAlertRequest,
|
|
22
|
+
SendInboundSpanForReplayRequest,
|
|
23
|
+
SetTimeTravelResponse,
|
|
24
|
+
UnpatchedDependencyAlert,
|
|
25
|
+
)
|
|
26
|
+
from tusk.drift.core.v1 import (
|
|
27
|
+
GetMockRequest as ProtoGetMockRequest,
|
|
28
|
+
)
|
|
14
29
|
|
|
15
30
|
from ...version import MIN_CLI_VERSION, SDK_VERSION
|
|
16
31
|
from ..span_serialization import clean_span_to_proto
|
|
17
32
|
from ..types import CleanSpanData, calling_library_context
|
|
18
33
|
from .types import (
|
|
19
|
-
CliMessage,
|
|
20
34
|
ConnectRequest,
|
|
21
35
|
GetMockRequest,
|
|
22
|
-
InstrumentationVersionMismatchAlert,
|
|
23
|
-
MessageType,
|
|
24
36
|
MockRequestInput,
|
|
25
37
|
MockResponseOutput,
|
|
26
|
-
SdkMessage,
|
|
27
|
-
SendAlertRequest,
|
|
28
|
-
SendInboundSpanForReplayRequest,
|
|
29
|
-
SetTimeTravelResponse,
|
|
30
|
-
UnpatchedDependencyAlert,
|
|
31
38
|
span_to_proto,
|
|
32
39
|
)
|
|
33
40
|
|
|
@@ -750,6 +757,10 @@ class ProtobufCommunicator:
|
|
|
750
757
|
self._handle_set_time_travel_sync(cli_message)
|
|
751
758
|
continue
|
|
752
759
|
|
|
760
|
+
if cli_message.type == MessageType.COVERAGE_SNAPSHOT:
|
|
761
|
+
self._handle_coverage_snapshot_sync(cli_message)
|
|
762
|
+
continue
|
|
763
|
+
|
|
753
764
|
# Route responses to waiting callers by request_id
|
|
754
765
|
request_id = cli_message.request_id
|
|
755
766
|
if request_id:
|
|
@@ -774,8 +785,8 @@ class ProtobufCommunicator:
|
|
|
774
785
|
|
|
775
786
|
def _handle_set_time_travel_sync(self, cli_message: CliMessage) -> None:
|
|
776
787
|
"""Handle SetTimeTravel request from CLI and send response."""
|
|
777
|
-
request = cli_message
|
|
778
|
-
if
|
|
788
|
+
request = getattr(cli_message, "set_time_travel_request", None)
|
|
789
|
+
if request is None:
|
|
779
790
|
return
|
|
780
791
|
|
|
781
792
|
logger.debug(
|
|
@@ -809,6 +820,57 @@ class ProtobufCommunicator:
|
|
|
809
820
|
except Exception as e:
|
|
810
821
|
logger.error(f"Failed to send SetTimeTravel response: {e}")
|
|
811
822
|
|
|
823
|
+
def _handle_coverage_snapshot_sync(self, cli_message: CliMessage) -> None:
|
|
824
|
+
"""Handle CoverageSnapshot request from CLI and send response."""
|
|
825
|
+
request = getattr(cli_message, "coverage_snapshot_request", None)
|
|
826
|
+
if request is None:
|
|
827
|
+
return
|
|
828
|
+
|
|
829
|
+
logger.debug(f"Received CoverageSnapshot request: baseline={request.baseline}")
|
|
830
|
+
|
|
831
|
+
try:
|
|
832
|
+
from ..coverage_server import take_coverage_snapshot
|
|
833
|
+
|
|
834
|
+
result = take_coverage_snapshot(request.baseline)
|
|
835
|
+
|
|
836
|
+
# Convert to protobuf
|
|
837
|
+
coverage: dict[str, FileCoverageData] = {}
|
|
838
|
+
for file_path, file_data in result.items():
|
|
839
|
+
branches: dict[str, BranchInfo] = {}
|
|
840
|
+
for line, branch_info in file_data.get("branches", {}).items():
|
|
841
|
+
branches[line] = BranchInfo(
|
|
842
|
+
total=branch_info.get("total", 0),
|
|
843
|
+
covered=branch_info.get("covered", 0),
|
|
844
|
+
)
|
|
845
|
+
|
|
846
|
+
coverage[file_path] = FileCoverageData(
|
|
847
|
+
lines=file_data.get("lines", {}),
|
|
848
|
+
total_branches=file_data.get("totalBranches", 0),
|
|
849
|
+
covered_branches=file_data.get("coveredBranches", 0),
|
|
850
|
+
branches=branches,
|
|
851
|
+
)
|
|
852
|
+
|
|
853
|
+
response = CoverageSnapshotResponse(
|
|
854
|
+
success=True,
|
|
855
|
+
error="",
|
|
856
|
+
coverage=coverage,
|
|
857
|
+
)
|
|
858
|
+
except Exception as e:
|
|
859
|
+
logger.error(f"Failed to take coverage snapshot: {e}")
|
|
860
|
+
response = CoverageSnapshotResponse(success=False, error=str(e))
|
|
861
|
+
|
|
862
|
+
sdk_message = SdkMessage(
|
|
863
|
+
type=MessageType.COVERAGE_SNAPSHOT,
|
|
864
|
+
request_id=cli_message.request_id,
|
|
865
|
+
coverage_snapshot_response=response,
|
|
866
|
+
)
|
|
867
|
+
|
|
868
|
+
try:
|
|
869
|
+
self._send_message_sync(sdk_message)
|
|
870
|
+
logger.debug(f"Sent CoverageSnapshot response: success={response.success}")
|
|
871
|
+
except Exception as e:
|
|
872
|
+
logger.error(f"[coverage] Failed to send response: {e}")
|
|
873
|
+
|
|
812
874
|
def _send_message_sync(self, message: SdkMessage) -> None:
|
|
813
875
|
"""Send a message synchronously on the main socket."""
|
|
814
876
|
if not self._socket:
|
{tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/communication/types.py
RENAMED
|
@@ -12,20 +12,6 @@ The protocol uses:
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
14
|
__all__ = [
|
|
15
|
-
# Re-exported protobuf types
|
|
16
|
-
"CliMessage",
|
|
17
|
-
"InstrumentationVersionMismatchAlert",
|
|
18
|
-
"MessageType",
|
|
19
|
-
"Runtime",
|
|
20
|
-
"SdkMessage",
|
|
21
|
-
"SendAlertRequest",
|
|
22
|
-
"SendInboundSpanForReplayRequest",
|
|
23
|
-
"SetTimeTravelRequest",
|
|
24
|
-
"SetTimeTravelResponse",
|
|
25
|
-
"UnpatchedDependencyAlert",
|
|
26
|
-
# Aliases
|
|
27
|
-
"SDKMessageType",
|
|
28
|
-
"CLIMessageType",
|
|
29
15
|
# Dataclasses
|
|
30
16
|
"ConnectRequest",
|
|
31
17
|
"ConnectResponse",
|
|
@@ -42,18 +28,6 @@ __all__ = [
|
|
|
42
28
|
from dataclasses import dataclass, field
|
|
43
29
|
from typing import Any
|
|
44
30
|
|
|
45
|
-
from tusk.drift.core.v1 import (
|
|
46
|
-
CliMessage,
|
|
47
|
-
InstrumentationVersionMismatchAlert,
|
|
48
|
-
MessageType,
|
|
49
|
-
Runtime,
|
|
50
|
-
SdkMessage,
|
|
51
|
-
SendAlertRequest,
|
|
52
|
-
SendInboundSpanForReplayRequest,
|
|
53
|
-
SetTimeTravelRequest,
|
|
54
|
-
SetTimeTravelResponse,
|
|
55
|
-
UnpatchedDependencyAlert,
|
|
56
|
-
)
|
|
57
31
|
from tusk.drift.core.v1 import (
|
|
58
32
|
ConnectRequest as ProtoConnectRequest,
|
|
59
33
|
)
|
|
@@ -66,6 +40,9 @@ from tusk.drift.core.v1 import (
|
|
|
66
40
|
from tusk.drift.core.v1 import (
|
|
67
41
|
GetMockResponse as ProtoGetMockResponse,
|
|
68
42
|
)
|
|
43
|
+
from tusk.drift.core.v1 import (
|
|
44
|
+
Runtime,
|
|
45
|
+
)
|
|
69
46
|
from tusk.drift.core.v1 import (
|
|
70
47
|
Span as ProtoSpan,
|
|
71
48
|
)
|
|
@@ -79,9 +56,6 @@ from tusk.drift.core.v1 import (
|
|
|
79
56
|
StatusCode as ProtoStatusCode,
|
|
80
57
|
)
|
|
81
58
|
|
|
82
|
-
SDKMessageType = MessageType
|
|
83
|
-
CLIMessageType = MessageType
|
|
84
|
-
|
|
85
59
|
|
|
86
60
|
def _python_to_value(value: Any) -> Any:
|
|
87
61
|
"""Convert Python value to protobuf Value."""
|
{tusk_drift_python_sdk-0.1.22 → tusk_drift_python_sdk-0.1.24}/drift/core/content_type_utils.py
RENAMED
|
@@ -111,7 +111,7 @@ CONTENT_TYPE_MAPPING: dict[str, DecodedType] = {
|
|
|
111
111
|
"application/binary": DecodedType.BINARY,
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
ACCEPTABLE_DECODED_TYPES = {DecodedType.JSON, DecodedType.PLAIN_TEXT
|
|
114
|
+
ACCEPTABLE_DECODED_TYPES = {DecodedType.JSON, DecodedType.PLAIN_TEXT}
|
|
115
115
|
|
|
116
116
|
|
|
117
117
|
def get_decoded_type(content_type: str | None) -> DecodedType | None:
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""Coverage collection for Python SDK.
|
|
2
|
+
|
|
3
|
+
Manages coverage.py for collecting per-test code coverage data.
|
|
4
|
+
Coverage data is accessed via take_coverage_snapshot() which is called
|
|
5
|
+
from the protobuf communicator handler.
|
|
6
|
+
|
|
7
|
+
Flow:
|
|
8
|
+
1. start_coverage_collection() initializes coverage.py at SDK startup
|
|
9
|
+
2. Between tests, CLI sends CoverageSnapshotRequest via protobuf
|
|
10
|
+
3. Communicator calls take_coverage_snapshot(baseline)
|
|
11
|
+
4. For baseline: returns ALL coverable lines (including uncovered at count=0)
|
|
12
|
+
5. For per-test: returns only executed lines since last snapshot, then resets
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import os
|
|
19
|
+
import threading
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger("TuskDrift")
|
|
22
|
+
|
|
23
|
+
# Shared state for coverage collection
|
|
24
|
+
_cov_instance = None
|
|
25
|
+
_source_root: str | None = None
|
|
26
|
+
_lock = threading.Lock()
|
|
27
|
+
|
|
28
|
+
# Cache branch structure from baseline to ensure deterministic branch counts.
|
|
29
|
+
# Branch detection via _analyze() depends on observed arcs, which vary with
|
|
30
|
+
# thread timing. By caching from the baseline (which has the fullest data),
|
|
31
|
+
# per-test snapshots report consistent totals.
|
|
32
|
+
_branch_cache: dict[str, dict] | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def start_coverage_collection() -> bool:
|
|
36
|
+
"""Initialize coverage.py collection if TUSK_COVERAGE is set.
|
|
37
|
+
|
|
38
|
+
TUSK_COVERAGE is set by the CLI when coverage is enabled.
|
|
39
|
+
This is the language-agnostic signal (Node uses NODE_V8_COVERAGE additionally).
|
|
40
|
+
|
|
41
|
+
Returns True if coverage was started, False otherwise.
|
|
42
|
+
"""
|
|
43
|
+
global _cov_instance, _source_root
|
|
44
|
+
|
|
45
|
+
# TUSK_COVERAGE is the language-agnostic signal from the CLI
|
|
46
|
+
if not os.environ.get("TUSK_COVERAGE"):
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
# Coverage collection only makes sense in REPLAY mode.
|
|
50
|
+
# If TUSK_DRIFT_MODE is not set we still proceed for backwards compatibility.
|
|
51
|
+
mode = os.environ.get("TUSK_DRIFT_MODE", "").upper()
|
|
52
|
+
if mode and mode != "REPLAY":
|
|
53
|
+
logger.debug("Coverage collection skipped: not in REPLAY mode (mode=%s)", mode)
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
import coverage as coverage_module
|
|
58
|
+
except ImportError:
|
|
59
|
+
logger.warning(
|
|
60
|
+
"Coverage requested but 'coverage' package is not installed. Install it with: pip install coverage"
|
|
61
|
+
)
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
with _lock:
|
|
65
|
+
# Guard against double initialization — stop existing instance first
|
|
66
|
+
if _cov_instance is not None:
|
|
67
|
+
try:
|
|
68
|
+
_cov_instance.stop()
|
|
69
|
+
except Exception:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
_source_root = os.path.realpath(os.getcwd())
|
|
73
|
+
|
|
74
|
+
_cov_instance = coverage_module.Coverage(
|
|
75
|
+
source=[_source_root],
|
|
76
|
+
branch=True,
|
|
77
|
+
omit=[
|
|
78
|
+
"*/site-packages/*",
|
|
79
|
+
"*/venv/*",
|
|
80
|
+
"*/.venv/*",
|
|
81
|
+
"*/tests/*",
|
|
82
|
+
"*/test_*.py",
|
|
83
|
+
"*/__pycache__/*",
|
|
84
|
+
],
|
|
85
|
+
)
|
|
86
|
+
_cov_instance.start()
|
|
87
|
+
|
|
88
|
+
logger.info("Coverage collection started")
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def stop_coverage_collection() -> None:
|
|
93
|
+
"""Stop coverage collection and clean up. Thread-safe."""
|
|
94
|
+
global _cov_instance, _branch_cache, _source_root
|
|
95
|
+
with _lock:
|
|
96
|
+
if _cov_instance is not None:
|
|
97
|
+
try:
|
|
98
|
+
_cov_instance.stop()
|
|
99
|
+
except Exception:
|
|
100
|
+
pass
|
|
101
|
+
_cov_instance = None
|
|
102
|
+
_branch_cache = None
|
|
103
|
+
_source_root = None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def take_coverage_snapshot(baseline: bool = False) -> dict:
|
|
107
|
+
"""Take a coverage snapshot.
|
|
108
|
+
|
|
109
|
+
Called from the protobuf communicator handler between tests.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
baseline: If True, returns ALL coverable lines (including uncovered at count=0)
|
|
113
|
+
for computing the total coverage denominator.
|
|
114
|
+
If False, returns only lines executed since the last snapshot.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
dict of { filePath: { "lines": {...}, "totalBranches": N, "coveredBranches": N, "branches": {...} } }
|
|
118
|
+
"""
|
|
119
|
+
with _lock:
|
|
120
|
+
if _cov_instance is None:
|
|
121
|
+
raise RuntimeError("Coverage not initialized")
|
|
122
|
+
_cov_instance.stop()
|
|
123
|
+
coverage = {}
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
global _branch_cache
|
|
127
|
+
if baseline:
|
|
128
|
+
# Baseline: compute fresh branch data and cache it for per-test reuse
|
|
129
|
+
_branch_cache = {}
|
|
130
|
+
data = _cov_instance.get_data()
|
|
131
|
+
for filename in data.measured_files():
|
|
132
|
+
if not _is_user_file(filename):
|
|
133
|
+
continue
|
|
134
|
+
try:
|
|
135
|
+
_, statements, _, missing, _ = _cov_instance.analysis2(filename)
|
|
136
|
+
missing_set = set(missing)
|
|
137
|
+
lines_map = {}
|
|
138
|
+
for line in statements:
|
|
139
|
+
lines_map[str(line)] = 0 if line in missing_set else 1
|
|
140
|
+
branch_data = _get_branch_data(data, filename)
|
|
141
|
+
_branch_cache[filename] = branch_data
|
|
142
|
+
if lines_map:
|
|
143
|
+
coverage[filename] = {"lines": lines_map, **branch_data}
|
|
144
|
+
except Exception as e:
|
|
145
|
+
logger.debug(f"Failed to analyze {filename}: {e}")
|
|
146
|
+
continue
|
|
147
|
+
else:
|
|
148
|
+
data = _cov_instance.get_data()
|
|
149
|
+
for filename in data.measured_files():
|
|
150
|
+
if not _is_user_file(filename):
|
|
151
|
+
continue
|
|
152
|
+
lines = data.lines(filename)
|
|
153
|
+
if lines:
|
|
154
|
+
# Use cached branch data from baseline for stable totals.
|
|
155
|
+
# Fall back to live _analyze() if no cache (e.g., no baseline taken).
|
|
156
|
+
if _branch_cache is not None and filename in _branch_cache:
|
|
157
|
+
branch_data = _get_per_test_branch_data(data, filename, _branch_cache[filename])
|
|
158
|
+
else:
|
|
159
|
+
branch_data = _get_branch_data(data, filename)
|
|
160
|
+
coverage[filename] = {
|
|
161
|
+
"lines": {str(line): 1 for line in lines},
|
|
162
|
+
**branch_data,
|
|
163
|
+
}
|
|
164
|
+
finally:
|
|
165
|
+
_cov_instance.erase()
|
|
166
|
+
_cov_instance.start()
|
|
167
|
+
|
|
168
|
+
return coverage
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _is_user_file(filename: str) -> bool:
|
|
172
|
+
"""Check if a file is a user source file (not third-party)."""
|
|
173
|
+
if "site-packages" in filename or "lib/python" in filename:
|
|
174
|
+
return False
|
|
175
|
+
# Resolve symlinks for consistent path comparison
|
|
176
|
+
resolved = os.path.realpath(filename)
|
|
177
|
+
# Use trailing separator to avoid prefix collisions (/app matching /application)
|
|
178
|
+
return not _source_root or resolved.startswith(_source_root + os.sep) or resolved == _source_root
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _group_arcs_by_line(arcs: set) -> dict[int, list[int]]:
|
|
182
|
+
"""Group executed arcs by from_line, skipping negative entry arcs."""
|
|
183
|
+
by_line: dict[int, list[int]] = {}
|
|
184
|
+
for from_line, to_line in arcs:
|
|
185
|
+
if from_line < 0:
|
|
186
|
+
continue
|
|
187
|
+
by_line.setdefault(from_line, []).append(to_line)
|
|
188
|
+
return by_line
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _get_branch_data(data, filename: str) -> dict:
|
|
192
|
+
"""Extract branch coverage data for a file.
|
|
193
|
+
|
|
194
|
+
Uses coverage.py's arc tracking (from_line, to_line) to compute
|
|
195
|
+
per-line branch coverage.
|
|
196
|
+
"""
|
|
197
|
+
try:
|
|
198
|
+
if not data.has_arcs():
|
|
199
|
+
return {"totalBranches": 0, "coveredBranches": 0, "branches": {}}
|
|
200
|
+
|
|
201
|
+
if _cov_instance is None:
|
|
202
|
+
return {"totalBranches": 0, "coveredBranches": 0, "branches": {}}
|
|
203
|
+
|
|
204
|
+
analysis = _cov_instance._analyze(filename)
|
|
205
|
+
numbers = analysis.numbers
|
|
206
|
+
|
|
207
|
+
total_branches = numbers.n_branches
|
|
208
|
+
covered_branches = max(0, total_branches - numbers.n_missing_branches)
|
|
209
|
+
|
|
210
|
+
missing_arcs = analysis.missing_branch_arcs()
|
|
211
|
+
executed_arcs = set(data.arcs(filename) or [])
|
|
212
|
+
executed_by_line = _group_arcs_by_line(executed_arcs)
|
|
213
|
+
|
|
214
|
+
# A line is a branch point if:
|
|
215
|
+
# - it appears in missing_arcs (at least one path wasn't taken), OR
|
|
216
|
+
# - it has multiple executed arcs (multiple paths from same line)
|
|
217
|
+
branch_point_lines = set(missing_arcs.keys())
|
|
218
|
+
for from_line, to_lines in executed_by_line.items():
|
|
219
|
+
if len(to_lines) > 1:
|
|
220
|
+
branch_point_lines.add(from_line)
|
|
221
|
+
|
|
222
|
+
branch_lines: dict[int, dict] = {}
|
|
223
|
+
|
|
224
|
+
for from_line in branch_point_lines:
|
|
225
|
+
executed_count = len(executed_by_line.get(from_line, []))
|
|
226
|
+
missing_count = len(missing_arcs.get(from_line, []))
|
|
227
|
+
branch_lines[from_line] = {
|
|
228
|
+
"total": executed_count + missing_count,
|
|
229
|
+
"covered": executed_count,
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
branches = {str(line): info for line, info in branch_lines.items()}
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
"totalBranches": total_branches,
|
|
236
|
+
"coveredBranches": covered_branches,
|
|
237
|
+
"branches": branches,
|
|
238
|
+
}
|
|
239
|
+
except Exception:
|
|
240
|
+
return {"totalBranches": 0, "coveredBranches": 0, "branches": {}}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _get_per_test_branch_data(data, filename: str, cached: dict) -> dict:
|
|
244
|
+
"""Compute per-test branch coverage using cached branch structure from baseline.
|
|
245
|
+
|
|
246
|
+
Uses the cached branch point set (from baseline) for stable totals,
|
|
247
|
+
but computes covered counts from the current test's executed arcs.
|
|
248
|
+
This avoids flaky branch totals caused by non-deterministic arc detection.
|
|
249
|
+
"""
|
|
250
|
+
try:
|
|
251
|
+
if not data.has_arcs():
|
|
252
|
+
return {"totalBranches": 0, "coveredBranches": 0, "branches": {}}
|
|
253
|
+
|
|
254
|
+
executed_arcs = set(data.arcs(filename) or [])
|
|
255
|
+
executed_by_line = _group_arcs_by_line(executed_arcs)
|
|
256
|
+
|
|
257
|
+
# Use cached branch points — only compute covered from current arcs
|
|
258
|
+
cached_branches = cached.get("branches", {})
|
|
259
|
+
branches: dict[str, dict] = {}
|
|
260
|
+
total_covered = 0
|
|
261
|
+
|
|
262
|
+
for line_str, info in cached_branches.items():
|
|
263
|
+
total = info["total"]
|
|
264
|
+
covered = min(len(executed_by_line.get(int(line_str), [])), total)
|
|
265
|
+
branches[line_str] = {"total": total, "covered": covered}
|
|
266
|
+
total_covered += covered
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
"totalBranches": cached.get("totalBranches", 0),
|
|
270
|
+
"coveredBranches": total_covered,
|
|
271
|
+
"branches": branches,
|
|
272
|
+
}
|
|
273
|
+
except Exception:
|
|
274
|
+
return {"totalBranches": 0, "coveredBranches": 0, "branches": {}}
|