summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changelog/.gitignore1
-rw-r--r--.git-blame-ignore-revs2
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yaml6
-rw-r--r--.github/workflows/add-to-project.yml28
-rw-r--r--.github/workflows/changelog.yml74
-rw-r--r--.github/workflows/check-links.yml53
-rw-r--r--.github/workflows/ci.yml1
-rw-r--r--.github/workflows/generate_workflows.py5
-rw-r--r--.github/workflows/lint.yml38
-rw-r--r--.github/workflows/misc.yml4
-rw-r--r--.github/workflows/prepare-patch-release.yml43
-rw-r--r--.github/workflows/prepare-release-branch.yml26
-rw-r--r--.github/workflows/release.yml2
-rw-r--r--.github/workflows/templates/ci.yml.j21
-rw-r--r--.github/workflows/templates/misc.yml.j24
-rw-r--r--.github/workflows/templates/test.yml.j29
-rw-r--r--.github/workflows/test.yml1455
-rw-r--r--.pylintrc2
-rw-r--r--.readthedocs.yml4
-rw-r--r--AGENTS.md76
-rw-r--r--CHANGELOG.md153
-rw-r--r--CLAUDE.md1
-rw-r--r--CONTRIBUTING.md54
-rw-r--r--README.md14
-rw-r--r--RELEASING.md11
-rw-r--r--codegen/opentelemetry-codegen-json/pyproject.toml5
-rw-r--r--codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/generator.py43
-rw-r--r--codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/plugin.py16
-rw-r--r--codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/runtime/json_codec.py39
-rw-r--r--codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/types.py13
-rw-r--r--codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/version/__init__.py15
-rw-r--r--codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/writer.py51
-rw-r--r--codegen/opentelemetry-codegen-json/test-requirements.txt2
-rw-r--r--codegen/opentelemetry-codegen-json/tests/conftest.py13
-rw-r--r--codegen/opentelemetry-codegen-json/tests/test_json_codec.py28
-rw-r--r--codegen/opentelemetry-codegen-json/tests/test_protobuf_compatibility.py31
-rw-r--r--codegen/opentelemetry-codegen-json/tests/test_serde.py25
-rw-r--r--codegen/opentelemetry-codegen-json/tests/test_types.py13
-rw-r--r--codegen/opentelemetry-codegen-json/tests/test_writer.py23
-rw-r--r--debian/changelog9
-rw-r--r--debian/patches/Don-t-try-using-importlib_metadata-library.patch51
-rw-r--r--debian/patches/Use-local-intersphinx-resources-where-possible.patch4
-rw-r--r--debian/patches/series1
-rw-r--r--dev-requirements.txt5
-rw-r--r--docs/api/context.context.rst7
-rw-r--r--docs/api/context.rst10
-rw-r--r--docs/api/trace.rst13
-rw-r--r--docs/api/trace.span.rst7
-rw-r--r--docs/api/trace.status.rst7
-rw-r--r--docs/conf.py7
-rw-r--r--docs/examples/auto-instrumentation/client.py13
-rw-r--r--docs/examples/auto-instrumentation/server_automatic.py13
-rw-r--r--docs/examples/auto-instrumentation/server_manual.py13
-rw-r--r--docs/examples/auto-instrumentation/server_programmatic.py13
-rw-r--r--docs/examples/basic_context/async_context.py13
-rw-r--r--docs/examples/basic_context/child_context.py13
-rw-r--r--docs/examples/basic_context/implicit_context.py13
-rw-r--r--docs/examples/basic_tracer/basic_trace.py13
-rw-r--r--docs/examples/basic_tracer/resources.py13
-rw-r--r--docs/examples/django/client.py13
-rw-r--r--docs/examples/django/instrumentation_example/asgi.py13
-rw-r--r--docs/examples/django/instrumentation_example/settings.py13
-rw-r--r--docs/examples/django/instrumentation_example/urls.py13
-rw-r--r--docs/examples/django/instrumentation_example/wsgi.py13
-rwxr-xr-xdocs/examples/django/manage.py16
-rw-r--r--docs/examples/django/pages/__init__.py13
-rw-r--r--docs/examples/django/pages/apps.py13
-rw-r--r--docs/examples/django/pages/urls.py13
-rw-r--r--docs/examples/django/pages/views.py13
-rw-r--r--docs/examples/error_handler/error_handler_0/pyproject.toml3
-rw-r--r--docs/examples/error_handler/error_handler_0/src/error_handler_0/__init__.py13
-rw-r--r--docs/examples/error_handler/error_handler_0/src/error_handler_0/version/__init__.py13
-rw-r--r--docs/examples/error_handler/error_handler_1/pyproject.toml3
-rw-r--r--docs/examples/error_handler/error_handler_1/src/error_handler_1/__init__.py13
-rw-r--r--docs/examples/error_handler/error_handler_1/src/error_handler_1/version/__init__.py13
-rw-r--r--docs/examples/error_handler/example.py3
-rw-r--r--docs/examples/fork-process-model/flask-gunicorn/app.py13
-rw-r--r--docs/examples/fork-process-model/flask-gunicorn/gunicorn.conf.py13
-rw-r--r--docs/examples/fork-process-model/flask-uwsgi/app.py13
-rw-r--r--docs/examples/logs/example.py3
-rw-r--r--docs/examples/metrics/instruments/example.py5
-rw-r--r--docs/examples/metrics/prometheus-grafana/prometheus-monitor.py3
-rw-r--r--docs/examples/metrics/reader/preferred_aggregation.py13
-rw-r--r--docs/examples/metrics/reader/preferred_exemplarfilter.py13
-rw-r--r--docs/examples/metrics/reader/preferred_temporality.py13
-rw-r--r--docs/examples/metrics/reader/synchronous_gauge_read.py15
-rw-r--r--docs/examples/metrics/views/change_aggregation.py13
-rw-r--r--docs/examples/metrics/views/change_name.py13
-rw-r--r--docs/examples/metrics/views/change_reservoir_factory.py16
-rw-r--r--docs/examples/metrics/views/disable_default_aggregation.py13
-rw-r--r--docs/examples/metrics/views/drop_metrics_from_instrument.py13
-rw-r--r--docs/examples/metrics/views/limit_num_of_attrs.py15
-rw-r--r--docs/examples/multi-destination-exporting/README.rst67
-rw-r--r--docs/examples/multi-destination-exporting/multi_destination_logs.py59
-rw-r--r--docs/examples/multi-destination-exporting/multi_destination_metrics.py52
-rw-r--r--docs/examples/multi-destination-exporting/multi_destination_traces.py45
-rw-r--r--docs/examples/opencensus-exporter-tracer/collector.py13
-rw-r--r--docs/examples/opencensus-shim/app.py13
-rwxr-xr-xdocs/examples/opentracing/main.py2
-rw-r--r--docs/examples/opentracing/rediscache.py3
-rw-r--r--docs/examples/sqlcommenter/instrumented_query.py13
-rw-r--r--docs/exporter/otlp/otlp.rst18
-rw-r--r--docs/getting_started/flask_example.py13
-rw-r--r--docs/getting_started/metrics_example.py15
-rw-r--r--docs/getting_started/otlpcollector_example.py13
-rw-r--r--docs/getting_started/tests/__init__.py13
-rw-r--r--docs/getting_started/tests/requirements.txt2
-rw-r--r--docs/getting_started/tests/test_flask.py13
-rw-r--r--docs/getting_started/tests/test_metrics.py13
-rw-r--r--docs/getting_started/tests/test_otlpcollector.py13
-rw-r--r--docs/getting_started/tests/test_tracing.py13
-rw-r--r--docs/getting_started/tracing_example.py13
-rw-r--r--eachdist.ini5
-rw-r--r--exporter/opentelemetry-exporter-opencensus/pyproject.toml5
-rw-r--r--exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/__init__.py13
-rw-r--r--exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/trace_exporter/__init__.py15
-rw-r--r--exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/util.py13
-rw-r--r--exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/version/__init__.py15
-rw-r--r--exporter/opentelemetry-exporter-opencensus/test-requirements.txt2
-rw-r--r--exporter/opentelemetry-exporter-opencensus/tests/test_otcollector_trace_exporter.py13
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/LICENSE201
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/README.rst25
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/benchmark-requirements.txt2
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/benchmarks/conftest.py13
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/benchmarks/test_benchmark_log_encoder.py26
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/benchmarks/test_benchmark_metrics_encoder.py66
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/benchmarks/test_benchmark_trace_encoder.py47
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/pyproject.toml47
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/__init__.py2
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/__init__.py135
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/_log_encoder/__init__.py102
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/metrics_encoder/__init__.py270
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/trace_encoder/__init__.py173
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_log_encoder.py8
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/metrics_encoder.py8
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/py.typed0
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/trace_encoder.py8
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/version/__init__.py4
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/test-requirements.txt18
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/tests/__init__.py359
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/tests/test_common_encoder.py328
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/tests/test_log_encoder.py361
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/tests/test_metrics_encoder.py272
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/tests/test_proto_json_compatibility.py290
-rw-r--r--exporter/opentelemetry-exporter-otlp-json-common/tests/test_trace_encoder.py359
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/pyproject.toml5
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/__init__.py13
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_exporter_metrics.py153
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py30
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py17
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py24
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py27
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_log_encoder.py13
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/metrics_encoder.py13
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/trace_encoder.py13
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/version/__init__.py15
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/test-requirements.txt2
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py13
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/tests/test_exporter_metrics.py59
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py20
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/tests/test_metrics_encoder.py13
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py18
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/benchmarks/test_benchmark_trace_exporter.py13
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/pyproject.toml9
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/__init__.py13
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py52
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py266
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py56
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py51
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/version/__init__.py15
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.latest.txt36
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.oldest.txt18
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/tests/logs/test_otlp_logs_exporter.py26
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py267
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_metrics_exporter.py89
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py24
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-http/pyproject.toml9
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py13
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_common/__init__.py17
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py162
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/metric_exporter/__init__.py185
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py164
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py15
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version/__init__.py15
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-http/test-requirements.txt2
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-http/tests/metrics/test_otlp_metrics_exporter.py195
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py137
-rw-r--r--exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py145
-rw-r--r--exporter/opentelemetry-exporter-otlp/pyproject.toml7
-rw-r--r--exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/version/__init__.py15
-rw-r--r--exporter/opentelemetry-exporter-otlp/test-requirements.txt2
-rw-r--r--exporter/opentelemetry-exporter-otlp/tests/test_otlp.py13
-rw-r--r--exporter/opentelemetry-exporter-prometheus/pyproject.toml5
-rw-r--r--exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py38
-rw-r--r--exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/_mapping.py13
-rw-r--r--exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/version/__init__.py15
-rw-r--r--exporter/opentelemetry-exporter-prometheus/test-requirements.txt2
-rw-r--r--exporter/opentelemetry-exporter-prometheus/tests/__init__.py13
-rw-r--r--exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py13
-rw-r--r--exporter/opentelemetry-exporter-prometheus/tests/test_mapping.py13
-rw-r--r--exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py30
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/pyproject.toml3
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/encoder/__init__.py30
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/__init__.py25
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v1/__init__.py21
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v2/__init__.py17
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version/__init__.py15
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/node_endpoint.py22
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/test-requirements.txt2
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/tests/__init__.py13
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/tests/encoder/common_tests.py18
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v1_json.py13
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v2_json.py13
-rw-r--r--exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py13
-rw-r--r--exporter/opentelemetry-exporter-zipkin-proto-http/pyproject.toml5
-rw-r--r--exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py25
-rw-r--r--exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/__init__.py19
-rw-r--r--exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version/__init__.py15
-rw-r--r--exporter/opentelemetry-exporter-zipkin-proto-http/test-requirements.txt2
-rw-r--r--exporter/opentelemetry-exporter-zipkin-proto-http/tests/__init__.py13
-rw-r--r--exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/common_tests.py18
-rw-r--r--exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/test_v2_protobuf.py13
-rw-r--r--exporter/opentelemetry-exporter-zipkin-proto-http/tests/test_zipkin_exporter.py13
-rw-r--r--exporter/opentelemetry-exporter-zipkin/pyproject.toml7
-rw-r--r--exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/version/__init__.py15
-rw-r--r--exporter/opentelemetry-exporter-zipkin/test-requirements.txt2
-rw-r--r--exporter/opentelemetry-exporter-zipkin/tests/test_zipkin.py13
-rw-r--r--opentelemetry-api/pyproject.toml6
-rw-r--r--opentelemetry-api/src/opentelemetry/_events/__init__.py67
-rw-r--r--opentelemetry-api/src/opentelemetry/_logs/__init__.py13
-rw-r--r--opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py115
-rw-r--r--opentelemetry-api/src/opentelemetry/_logs/severity/__init__.py13
-rw-r--r--opentelemetry-api/src/opentelemetry/attributes/__init__.py42
-rw-r--r--opentelemetry-api/src/opentelemetry/baggage/__init__.py29
-rw-r--r--opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py131
-rw-r--r--opentelemetry-api/src/opentelemetry/context/__init__.py18
-rw-r--r--opentelemetry-api/src/opentelemetry/context/context.py16
-rw-r--r--opentelemetry-api/src/opentelemetry/context/contextvars_context.py13
-rw-r--r--opentelemetry-api/src/opentelemetry/environment_variables/__init__.py13
-rw-r--r--opentelemetry-api/src/opentelemetry/metrics/__init__.py13
-rw-r--r--opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py112
-rw-r--r--opentelemetry-api/src/opentelemetry/metrics/_internal/instrument.py125
-rw-r--r--opentelemetry-api/src/opentelemetry/metrics/_internal/observation.py24
-rw-r--r--opentelemetry-api/src/opentelemetry/propagate/__init__.py20
-rw-r--r--opentelemetry-api/src/opentelemetry/propagators/_envcarrier.py41
-rw-r--r--opentelemetry-api/src/opentelemetry/propagators/composite.py19
-rw-r--r--opentelemetry-api/src/opentelemetry/propagators/textmap.py33
-rw-r--r--opentelemetry-api/src/opentelemetry/trace/__init__.py71
-rw-r--r--opentelemetry-api/src/opentelemetry/trace/propagation/__init__.py20
-rw-r--r--opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py20
-rw-r--r--opentelemetry-api/src/opentelemetry/trace/span.py59
-rw-r--r--opentelemetry-api/src/opentelemetry/trace/status.py18
-rw-r--r--opentelemetry-api/src/opentelemetry/util/_decorator.py16
-rw-r--r--opentelemetry-api/src/opentelemetry/util/_importlib_metadata.py48
-rw-r--r--opentelemetry-api/src/opentelemetry/util/_once.py15
-rw-r--r--opentelemetry-api/src/opentelemetry/util/_providers.py13
-rw-r--r--opentelemetry-api/src/opentelemetry/util/re.py19
-rw-r--r--opentelemetry-api/src/opentelemetry/util/types.py79
-rw-r--r--opentelemetry-api/src/opentelemetry/version/__init__.py15
-rw-r--r--opentelemetry-api/test-requirements.txt2
-rw-r--r--opentelemetry-api/tests/__init__.py13
-rw-r--r--opentelemetry-api/tests/attributes/test_attributes.py15
-rw-r--r--opentelemetry-api/tests/baggage/propagation/test_propagation.py13
-rw-r--r--opentelemetry-api/tests/baggage/test_baggage.py13
-rw-r--r--opentelemetry-api/tests/context/base_context.py13
-rw-r--r--opentelemetry-api/tests/context/test_context.py13
-rw-r--r--opentelemetry-api/tests/context/test_contextvars_context.py13
-rw-r--r--opentelemetry-api/tests/distributedcontext/__init__.py13
-rw-r--r--opentelemetry-api/tests/events/test_event.py3
-rw-r--r--opentelemetry-api/tests/events/test_event_logger_provider.py3
-rw-r--r--opentelemetry-api/tests/events/test_proxy_event.py10
-rw-r--r--opentelemetry-api/tests/logs/test_log_record.py18
-rw-r--r--opentelemetry-api/tests/logs/test_logger_provider.py13
-rw-r--r--opentelemetry-api/tests/logs/test_proxy.py34
-rw-r--r--opentelemetry-api/tests/metrics/test_instruments.py13
-rw-r--r--opentelemetry-api/tests/metrics/test_meter.py13
-rw-r--r--opentelemetry-api/tests/metrics/test_meter_provider.py13
-rw-r--r--opentelemetry-api/tests/metrics/test_observation.py13
-rw-r--r--opentelemetry-api/tests/metrics/test_subclass_instantiation.py18
-rw-r--r--opentelemetry-api/tests/mypysmoke.py13
-rw-r--r--opentelemetry-api/tests/propagators/test__envcarrier.py42
-rw-r--r--opentelemetry-api/tests/propagators/test_composite.py13
-rw-r--r--opentelemetry-api/tests/propagators/test_global_httptextformat.py13
-rw-r--r--opentelemetry-api/tests/propagators/test_propagators.py13
-rw-r--r--opentelemetry-api/tests/propagators/test_w3cbaggagepropagator.py172
-rw-r--r--opentelemetry-api/tests/test_implementation.py13
-rw-r--r--opentelemetry-api/tests/trace/__init__.py13
-rw-r--r--opentelemetry-api/tests/trace/propagation/test_textmap.py13
-rw-r--r--opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py22
-rw-r--r--opentelemetry-api/tests/trace/test_defaultspan.py13
-rw-r--r--opentelemetry-api/tests/trace/test_globals.py13
-rw-r--r--opentelemetry-api/tests/trace/test_immutablespancontext.py13
-rw-r--r--opentelemetry-api/tests/trace/test_proxy.py20
-rw-r--r--opentelemetry-api/tests/trace/test_span_context.py13
-rw-r--r--opentelemetry-api/tests/trace/test_status.py13
-rw-r--r--opentelemetry-api/tests/trace/test_tracer.py13
-rw-r--r--opentelemetry-api/tests/trace/test_tracestate.py13
-rw-r--r--opentelemetry-api/tests/util/test__importlib_metadata.py30
-rw-r--r--opentelemetry-api/tests/util/test__providers.py13
-rw-r--r--opentelemetry-api/tests/util/test_contextmanager.py15
-rw-r--r--opentelemetry-api/tests/util/test_once.py13
-rw-r--r--opentelemetry-api/tests/util/test_re.py13
-rw-r--r--opentelemetry-proto-json/pyproject.toml3
-rw-r--r--opentelemetry-proto-json/src/opentelemetry/proto_json/_json_codec.py13
-rw-r--r--opentelemetry-proto-json/src/opentelemetry/proto_json/collector/logs/v1/logs_service.py19
-rw-r--r--opentelemetry-proto-json/src/opentelemetry/proto_json/collector/metrics/v1/metrics_service.py19
-rw-r--r--opentelemetry-proto-json/src/opentelemetry/proto_json/collector/profiles/v1development/profiles_service.py19
-rw-r--r--opentelemetry-proto-json/src/opentelemetry/proto_json/collector/trace/v1/trace_service.py19
-rw-r--r--opentelemetry-proto-json/src/opentelemetry/proto_json/common/v1/common.py19
-rw-r--r--opentelemetry-proto-json/src/opentelemetry/proto_json/logs/v1/logs.py19
-rw-r--r--opentelemetry-proto-json/src/opentelemetry/proto_json/metrics/v1/metrics.py19
-rw-r--r--opentelemetry-proto-json/src/opentelemetry/proto_json/profiles/v1development/profiles.py19
-rw-r--r--opentelemetry-proto-json/src/opentelemetry/proto_json/resource/v1/resource.py19
-rw-r--r--opentelemetry-proto-json/src/opentelemetry/proto_json/trace/v1/trace.py19
-rw-r--r--opentelemetry-proto-json/src/opentelemetry/proto_json/version/__init__.py15
-rw-r--r--opentelemetry-proto-json/test-requirements.latest.txt10
-rw-r--r--opentelemetry-proto-json/test-requirements.oldest.txt2
-rw-r--r--opentelemetry-proto-json/tests/test_proto.py13
-rw-r--r--opentelemetry-proto/pyproject.toml3
-rw-r--r--opentelemetry-proto/src/opentelemetry/proto/version/__init__.py15
-rw-r--r--opentelemetry-proto/test-requirements.in2
-rw-r--r--opentelemetry-proto/test-requirements.latest.txt14
-rw-r--r--opentelemetry-proto/test-requirements.oldest.txt12
-rw-r--r--opentelemetry-proto/tests/test_proto.py13
-rw-r--r--opentelemetry-sdk/benchmarks/logs/test_benchmark_logging_handler.py13
-rw-r--r--opentelemetry-sdk/benchmarks/logs/test_benchmark_logs.py13
-rw-r--r--opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics.py13
-rw-r--r--opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics_histogram.py13
-rw-r--r--opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics_histogram_steady.py13
-rw-r--r--opentelemetry-sdk/benchmarks/test_baggage.py3
-rw-r--r--opentelemetry-sdk/benchmarks/trace/test_benchmark_trace.py15
-rw-r--r--opentelemetry-sdk/codegen/README.md24
-rw-r--r--opentelemetry-sdk/codegen/dataclass.jinja273
-rw-r--r--opentelemetry-sdk/pyproject.toml7
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/README.md2
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py44
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_common.py119
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_exceptions.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_logger_provider.py266
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_meter_provider.py88
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_propagator.py82
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_resource.py101
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_tracer_provider.py91
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/__init__.py19
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/_env_substitution.py30
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/_loader.py15
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py596
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_events/__init__.py28
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_logs/__init__.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py60
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/_exceptions.py121
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/_logger_metrics.py34
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py36
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py15
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py20
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/_processor_metrics.py55
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py44
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/_internal.py28
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/error_handler/__init__.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/__init__.py31
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py42
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/aggregation.py76
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exceptions.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/__init__.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar.py20
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_filter.py22
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_reservoir.py57
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/buckets.py19
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__init__.py20
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/errors.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/exponent_mapping.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/logarithm_mapping.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py44
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/_metric_reader_metrics.py24
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/instrument.py55
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/measurement.py25
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/measurement_consumer.py23
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/metric_reader_storage.py42
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py55
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/sdk_configuration.py15
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/view.py47
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/metrics/view/__init__.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py161
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py216
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/__init__.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_always_off.py15
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_always_on.py15
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_composable.py16
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_parent_threshold.py15
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_rule_based.py16
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_sampler.py15
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_trace_state.py15
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_traceid_ratio.py15
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_util.py13
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/_tracer_metrics.py42
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py38
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py17
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/id_generator.py38
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/trace/sampling.py34
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/util/__init__.py18
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/util/__init__.pyi11
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/util/_configurator.py16
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py41
-rw-r--r--opentelemetry-sdk/src/opentelemetry/sdk/version/__init__.py15
-rw-r--r--opentelemetry-sdk/test-requirements.txt3
-rw-r--r--opentelemetry-sdk/tests/__init__.py13
-rw-r--r--opentelemetry-sdk/tests/_configuration/__init__.py13
-rw-r--r--opentelemetry-sdk/tests/_configuration/file/__init__.py13
-rw-r--r--opentelemetry-sdk/tests/_configuration/file/test_env_substitution.py57
-rw-r--r--opentelemetry-sdk/tests/_configuration/file/test_loader.py13
-rw-r--r--opentelemetry-sdk/tests/_configuration/test_common.py236
-rw-r--r--opentelemetry-sdk/tests/_configuration/test_logger_provider.py413
-rw-r--r--opentelemetry-sdk/tests/_configuration/test_meter_provider.py67
-rw-r--r--opentelemetry-sdk/tests/_configuration/test_models.py13
-rw-r--r--opentelemetry-sdk/tests/_configuration/test_propagator.py65
-rw-r--r--opentelemetry-sdk/tests/_configuration/test_resource.py148
-rw-r--r--opentelemetry-sdk/tests/_configuration/test_tracer_provider.py92
-rw-r--r--opentelemetry-sdk/tests/conftest.py13
-rw-r--r--opentelemetry-sdk/tests/context/test_asyncio.py13
-rw-r--r--opentelemetry-sdk/tests/error_handler/test_error_handler.py13
-rw-r--r--opentelemetry-sdk/tests/events/__init__.py13
-rw-r--r--opentelemetry-sdk/tests/events/test_events.py13
-rw-r--r--opentelemetry-sdk/tests/logs/__init__.py13
-rw-r--r--opentelemetry-sdk/tests/logs/test_export.py22
-rw-r--r--opentelemetry-sdk/tests/logs/test_handler.py13
-rw-r--r--opentelemetry-sdk/tests/logs/test_log_limits.py13
-rw-r--r--opentelemetry-sdk/tests/logs/test_log_record.py13
-rw-r--r--opentelemetry-sdk/tests/logs/test_logger_provider_cache.py3
-rw-r--r--opentelemetry-sdk/tests/logs/test_logs.py127
-rw-r--r--opentelemetry-sdk/tests/logs/test_multi_log_processor.py79
-rw-r--r--opentelemetry-sdk/tests/logs/test_sdk_metrics.py31
-rw-r--r--opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponent_mapping.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponential_bucket_histogram_aggregation.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/exponential_histogram/test_logarithm_mapping.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/integration_test/test_cpu_time.py17
-rw-r--r--opentelemetry-sdk/tests/metrics/integration_test/test_disable_default_views.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/integration_test/test_exemplars.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/integration_test/test_explicit_bucket_histogram_aggregation.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/integration_test/test_exponential_bucket_histogram.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/integration_test/test_exporter_concurrency.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/integration_test/test_histogram_advisory_explicit_buckets.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/integration_test/test_histogram_export.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/integration_test/test_provider_shutdown.py15
-rw-r--r--opentelemetry-sdk/tests/metrics/integration_test/test_sum_aggregation.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/integration_test/test_time_align.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/test_aggregation.py16
-rw-r--r--opentelemetry-sdk/tests/metrics/test_backward_compat.py15
-rw-r--r--opentelemetry-sdk/tests/metrics/test_exemplarfilter.py3
-rw-r--r--opentelemetry-sdk/tests/metrics/test_exemplarreservoir.py3
-rw-r--r--opentelemetry-sdk/tests/metrics/test_import.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/test_in_memory_metric_reader.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/test_instrument.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/test_measurement_consumer.py34
-rw-r--r--opentelemetry-sdk/tests/metrics/test_metric_reader.py19
-rw-r--r--opentelemetry-sdk/tests/metrics/test_metric_reader_storage.py23
-rw-r--r--opentelemetry-sdk/tests/metrics/test_metrics.py17
-rw-r--r--opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py27
-rw-r--r--opentelemetry-sdk/tests/metrics/test_point.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/test_view.py13
-rw-r--r--opentelemetry-sdk/tests/metrics/test_view_instrument_match.py62
-rw-r--r--opentelemetry-sdk/tests/resources/test_resources.py108
-rw-r--r--opentelemetry-sdk/tests/shared_internal/__init__.py13
-rw-r--r--opentelemetry-sdk/tests/shared_internal/test_batch_processor.py13
-rw-r--r--opentelemetry-sdk/tests/test_configurator.py20
-rw-r--r--opentelemetry-sdk/tests/test_environment_variables_internal.py62
-rw-r--r--opentelemetry-sdk/tests/test_util.py13
-rw-r--r--opentelemetry-sdk/tests/trace/__init__.py13
-rw-r--r--opentelemetry-sdk/tests/trace/composite_sampler/test_always_off.py13
-rw-r--r--opentelemetry-sdk/tests/trace/composite_sampler/test_always_on.py13
-rw-r--r--opentelemetry-sdk/tests/trace/composite_sampler/test_rule_based.py13
-rw-r--r--opentelemetry-sdk/tests/trace/composite_sampler/test_sampler.py13
-rw-r--r--opentelemetry-sdk/tests/trace/composite_sampler/test_traceid_ratio.py13
-rw-r--r--opentelemetry-sdk/tests/trace/composite_sampler/test_tracestate.py13
-rw-r--r--opentelemetry-sdk/tests/trace/export/__init__.py13
-rw-r--r--opentelemetry-sdk/tests/trace/export/test_export.py20
-rw-r--r--opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py13
-rw-r--r--opentelemetry-sdk/tests/trace/test_globals.py3
-rw-r--r--opentelemetry-sdk/tests/trace/test_implementation.py13
-rw-r--r--opentelemetry-sdk/tests/trace/test_sampling.py16
-rw-r--r--opentelemetry-sdk/tests/trace/test_sdk_metrics.py35
-rw-r--r--opentelemetry-sdk/tests/trace/test_span_processor.py81
-rw-r--r--opentelemetry-sdk/tests/trace/test_trace.py97
-rw-r--r--opentelemetry-semantic-conventions/.pylintrc2
-rw-r--r--opentelemetry-semantic-conventions/pyproject.toml5
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/app_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/artifact_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/aws_attributes.py17
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/az_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/azure_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/browser_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cassandra_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cicd_attributes.py14
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/client_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloud_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloudevents_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloudfoundry_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/code_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/container_attributes.py15
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cpu_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cpython_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/db_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/deployment_attributes.py38
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/destination_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/device_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/disk_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/dns_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/elasticsearch_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/enduser_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/error_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/event_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/exception_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/faas_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/feature_flag_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/file_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/gcp_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/gen_ai_attributes.py57
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/geo_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/graphql_attributes.py15
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/heroku_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/host_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/http_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/hw_attributes.py15
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/jsonrpc_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/k8s_attributes.py144
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/linux_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/log_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/mainframe_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/mcp_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/message_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/messaging_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/net_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/network_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/nfs_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oci_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/onc_rpc_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/openai_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/openshift_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/opentracing_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oracle_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oracle_cloud_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/os_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/otel_attributes.py16
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/other_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/peer_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/pool_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/pprof_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/process_attributes.py28
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/profile_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/rpc_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/security_rule_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/server_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/service_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/session_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/source_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/system_attributes.py27
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/telemetry_attributes.py19
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/test_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/thread_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/tls_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/url_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/user_agent_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/user_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/vcs_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/webengine_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/zos_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/azure_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cicd_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/container_metrics.py41
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cpu_metrics.py41
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cpython_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/db_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/dns_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/faas_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/gen_ai_metrics.py57
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/http_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/hw_metrics.py85
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/k8s_metrics.py505
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/mcp_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/messaging_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/nfs_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/openshift_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/otel_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/process_metrics.py47
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/rpc_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/system_metrics.py178
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/vcs_metrics.py51
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/client_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/code_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/db_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/deployment_attributes.py28
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/error_attributes.py19
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/exception_attributes.py18
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/http_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/network_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/otel_attributes.py19
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/server_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/service_attributes.py16
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/telemetry_attributes.py25
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/url_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/user_agent_attributes.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/__init__.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/db_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/http_metrics.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/resource/__init__.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/schemas.py23
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/trace/__init__.py13
-rw-r--r--opentelemetry-semantic-conventions/src/opentelemetry/semconv/version/__init__.py15
-rw-r--r--opentelemetry-semantic-conventions/test-requirements.txt1
-rw-r--r--opentelemetry-semantic-conventions/tests/test_semconv.py13
-rw-r--r--propagator/opentelemetry-propagator-b3/benchmarks/trace/propagation/test_benchmark_b3_format.py13
-rw-r--r--propagator/opentelemetry-propagator-b3/pyproject.toml3
-rw-r--r--propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/__init__.py25
-rw-r--r--propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/version/__init__.py15
-rw-r--r--propagator/opentelemetry-propagator-b3/test-requirements.txt2
-rw-r--r--propagator/opentelemetry-propagator-b3/tests/__init__.py13
-rw-r--r--propagator/opentelemetry-propagator-b3/tests/test_b3_format.py13
-rw-r--r--propagator/opentelemetry-propagator-jaeger/pyproject.toml3
-rw-r--r--propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py27
-rw-r--r--propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/version/__init__.py15
-rw-r--r--propagator/opentelemetry-propagator-jaeger/test-requirements.txt2
-rw-r--r--propagator/opentelemetry-propagator-jaeger/tests/__init__.py13
-rw-r--r--propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py13
-rw-r--r--pyproject.toml76
-rw-r--r--scripts/add_required_checks.py3
-rw-r--r--scripts/changelog_template.j224
-rw-r--r--scripts/check_for_valid_readme.py3
-rw-r--r--scripts/check_license_header.py102
-rwxr-xr-xscripts/eachdist.py2
-rw-r--r--scripts/fix_typealias.py54
-rw-r--r--scripts/griffe_check.py3
-rw-r--r--scripts/public_symbols_checker.py13
-rwxr-xr-xscripts/semconv/generate.sh4
-rw-r--r--scripts/semconv/templates/registry/common.j213
-rw-r--r--scripts/semconv/templates/registry/semantic_attributes.j213
-rw-r--r--scripts/semconv/templates/registry/semantic_metrics.j227
-rw-r--r--scripts/semconv/templates/registry/weaver.yaml6
-rwxr-xr-xscripts/tracecontext-integration-test.sh32
-rw-r--r--scripts/update_sha.py65
-rw-r--r--shim/opentelemetry-opencensus-shim/pyproject.toml3
-rw-r--r--shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/__init__.py13
-rw-r--r--shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_patch.py16
-rw-r--r--shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_shim_span.py13
-rw-r--r--shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_shim_tracer.py13
-rw-r--r--shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/version/__init__.py15
-rw-r--r--shim/opentelemetry-opencensus-shim/test-requirements.txt2
-rw-r--r--shim/opentelemetry-opencensus-shim/tests/test_patch.py13
-rw-r--r--shim/opentelemetry-opencensus-shim/tests/test_shim.py13
-rw-r--r--shim/opentelemetry-opencensus-shim/tests/test_shim_with_sdk.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/pyproject.toml3
-rw-r--r--shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/__init__.py23
-rw-r--r--shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/util.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/version/__init__.py15
-rw-r--r--shim/opentelemetry-opentracing-shim/test-requirements.txt2
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/test_shim.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/test_util.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/otel_ot_shim_tracer.py3
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_active_span_replacement/test_asyncio.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_active_span_replacement/test_threads.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_client_server/test_asyncio.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_client_server/test_threads.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/request_handler.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/test_asyncio.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/test_threads.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_late_span_finish/test_asyncio.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_late_span_finish/test_threads.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/response_listener.py4
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/test_asyncio.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/test_threads.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_multiple_callbacks/test_asyncio.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_multiple_callbacks/test_threads.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_nested_callbacks/test_asyncio.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_nested_callbacks/test_threads.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_asyncio.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_threads.py13
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/testcase.py3
-rw-r--r--shim/opentelemetry-opentracing-shim/tests/testbed/utils.py3
-rw-r--r--tests/opentelemetry-docker-tests/tests/docker-compose.yml6
-rw-r--r--tests/opentelemetry-docker-tests/tests/opencensus/test_opencensusexporter_functional.py13
-rw-r--r--tests/opentelemetry-docker-tests/tests/otlpexporter/__init__.py144
-rw-r--r--tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_grpc_exporter_functional.py71
-rw-r--r--tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_http_exporter_functional.py70
-rw-r--r--tests/opentelemetry-test-utils/pyproject.toml8
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/__init__.py13
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/asgitestutil.py13
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/concurrency_test.py20
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/globals_test.py13
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/httptest.py13
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/metrictestutil.py19
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/mock_test_classes.py13
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/mock_textmap.py24
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/spantestutil.py13
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py25
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/version/__init__.py5
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/weaver_live_check.py496
-rw-r--r--tests/opentelemetry-test-utils/src/opentelemetry/test/wsgitestutil.py16
-rw-r--r--tests/opentelemetry-test-utils/test-requirements.txt9
-rw-r--r--tests/opentelemetry-test-utils/tests/__init__.py13
-rw-r--r--tests/opentelemetry-test-utils/tests/test_base.py13
-rw-r--r--tests/opentelemetry-test-utils/tests/test_utils.py13
-rw-r--r--tests/opentelemetry-test-utils/tests/test_weaver_live_check.py192
-rw-r--r--tests/opentelemetry-test-utils/tests/testdata/registry/attributes.yaml27
-rw-r--r--tests/opentelemetry-test-utils/tests/testdata/test-policy.rego16
-rw-r--r--tests/w3c_tracecontext_validation_server.py13
-rw-r--r--tox.ini69
-rw-r--r--uv.lock2646
710 files changed, 14257 insertions, 11830 deletions
diff --git a/.changelog/.gitignore b/.changelog/.gitignore
new file mode 100644
index 0000000..f935021
--- /dev/null
+++ b/.changelog/.gitignore
@@ -0,0 +1 @@
+!.gitignore
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000..66de199
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,2 @@
+# Switch to SPDX license headers (#5177)
+4d978892d3253da1d6f4e6e6abfbe1e1bee87b05
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
index c0afcba..0f1d4d7 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yaml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -10,7 +10,7 @@ body:
Before filing a bug, please be sure you have searched through [existing bugs](https://github.com/open-telemetry/opentelemetry-python/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Abug) to see if your bug is already addressed.
If your bug is related to an instrumentation or plugin in [opentelemetry-python-contrib](https://github.com/open-telemetry/opentelemetry-python-contrib) please be sure to file it there.
-
+
- type: textarea
id: environment
attributes:
@@ -19,10 +19,10 @@ body:
Please describe any aspect of your environment relevant to the problem, including your Python version, [platform](https://docs.python.org/3/library/platform.html), version numbers of installed dependencies, information about your cloud hosting provider, etc. If you're reporting a problem with a specific version of a library in this repo, please check whether the problem has been fixed on main.
value: |
OS: (e.g, Ubuntu)
- Python version: (e.g., Python 3.9.10)
+ Python version: (e.g., Python 3.10.0)
SDK version: (e.g., 1.25.0)
API version: (e.g., 1.25.0)
-
+
- type: textarea
attributes:
label: What happened?
diff --git a/.github/workflows/add-to-project.yml b/.github/workflows/add-to-project.yml
new file mode 100644
index 0000000..3787654
--- /dev/null
+++ b/.github/workflows/add-to-project.yml
@@ -0,0 +1,28 @@
+name: Add PR to project board
+
+on:
+ pull_request_target:
+ types: [opened, reopened, ready_for_review]
+
+permissions:
+ contents: read
+
+jobs:
+ add-to-project:
+ name: add to project board
+ runs-on: ubuntu-latest
+ if: github.event.pull_request.draft == false
+ steps:
+ # NOTE: do NOT add an actions/checkout step here. This workflow uses
+ # pull_request_target (which has access to secrets) but must never
+ # execute code from the fork branch. See PR #4955 for context.
+ - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
+ id: otelbot-token
+ with:
+ app-id: ${{ vars.OTELBOT_PYTHON_APP_ID }}
+ private-key: ${{ secrets.OTELBOT_PYTHON_PRIVATE_KEY }}
+
+ - uses: actions/add-to-project@v1.0.2
+ with:
+ project-url: https://github.com/orgs/open-telemetry/projects/88
+ github-token: ${{ steps.otelbot-token.outputs.token }}
diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml
index 39fc5f8..d234521 100644
--- a/.github/workflows/changelog.yml
+++ b/.github/workflows/changelog.yml
@@ -1,6 +1,6 @@
-# This action requires that any PR targeting the main branch should touch at
-# least one CHANGELOG file. If a CHANGELOG entry is not required, add the "Skip
-# Changelog" label to disable this action.
+# This action requires that any PR targeting the main branch should add a
+# changelog fragment file in the .changelog/ directory. If a changelog entry
+# is not required, add the "Skip Changelog" label to disable this action.
name: changelog
@@ -9,6 +9,7 @@ on:
types: [opened, synchronize, reopened, labeled, unlabeled]
branches:
- main
+ merge_group:
permissions:
contents: read
@@ -17,23 +18,70 @@ jobs:
changelog:
runs-on: ubuntu-latest
if: |
+ github.event_name != 'merge_group' &&
!contains(github.event.pull_request.labels.*.name, 'Skip Changelog')
&& github.actor != 'otelbot[bot]'
steps:
- uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
- - name: Check for CHANGELOG changes
+ - name: Fetch base branch
+ run: git fetch origin ${{ github.base_ref }} --depth=1
+
+ - name: Ensure no direct changes to CHANGELOG.md
run: |
- # Only the latest commit of the feature branch is available
- # automatically. To diff with the base branch, we need to
- # fetch that too (and we only need its latest commit).
- git fetch origin ${{ github.base_ref }} --depth=1
- if [[ $(git diff --name-only FETCH_HEAD | grep CHANGELOG) ]]
+ if [[ $(git diff --name-only FETCH_HEAD -- 'CHANGELOG.md') ]]
then
- echo "A CHANGELOG was modified. Looks good!"
- else
- echo "No CHANGELOG was modified."
- echo "Please add a CHANGELOG entry, or add the \"Skip Changelog\" label if not required."
+ echo "CHANGELOG.md should not be directly modified."
+ echo "Please add a changelog fragment file to the .changelog/ directory instead."
+ echo "See CONTRIBUTING.md for details."
+ echo ""
+ echo "Or add the \"Skip Changelog\" label if this job should be skipped."
+ false
+ fi
+
+ - name: Install towncrier
+ run: pip install towncrier==25.8.0
+
+ - name: Check for changelog fragment
+ run: |
+ if ! towncrier check --compare-with origin/${{ github.base_ref }}; then
+ echo ""
+ echo "No changelog fragment found for this PR."
+ echo "Add a file named .changelog/${{ github.event.pull_request.number }}.<type>"
+ echo "where <type> is one of: added, changed, deprecated, removed, fixed"
+ echo "See CONTRIBUTING.md for details."
+ echo ""
+ echo "Or add the \"Skip Changelog\" label if this job should be skipped."
false
fi
+
+ - name: Validate fragment filenames
+ env:
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ run: |
+ fragments=$(git diff --diff-filter=A --name-only origin/${{ github.base_ref }} -- '.changelog/*' | grep -v '/\.gitignore$' || true)
+ [ -z "$fragments" ] && exit 0
+ invalid=()
+ while IFS= read -r f; do
+ base=$(basename "$f")
+ if [[ ! "$base" =~ ^([0-9]+)\.(added|changed|deprecated|removed|fixed)$ ]]; then
+ invalid+=("$f (expected <PR_NUMBER>.<type>; type one of added, changed, deprecated, removed, fixed)")
+ continue
+ fi
+ if [[ "${BASH_REMATCH[1]}" != "${PR_NUMBER}" ]]; then
+ invalid+=("$f (PR number ${BASH_REMATCH[1]} does not match this PR's number ${PR_NUMBER})")
+ fi
+ done <<< "$fragments"
+ if (( ${#invalid[@]} > 0 )); then
+ echo "Invalid changelog fragment(s):"
+ for msg in "${invalid[@]}"; do
+ echo " $msg"
+ done
+ exit 1
+ fi
+
+ - name: Preview changelog
+ run: towncrier build --draft --version Unreleased
diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml
index b783f25..940cdcf 100644
--- a/.github/workflows/check-links.yml
+++ b/.github/workflows/check-links.yml
@@ -4,11 +4,19 @@ on:
branches: [ main ]
paths:
- '**/*.md'
+ - '**/*.rst'
- '.github/workflows/check-links.yml'
- '.github/workflows/check_links_config.json'
pull_request:
paths:
- '**/*.md'
+ - '**/*.rst'
+ - '.github/workflows/check-links.yml'
+ - '.github/workflows/check_links_config.json'
+ merge_group:
+ paths:
+ - '**/*.md'
+ - '**/*.rst'
- '.github/workflows/check-links.yml'
- '.github/workflows/check_links_config.json'
@@ -16,9 +24,13 @@ permissions:
contents: read
concurrency:
- group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.merge_group.head_sha || github.run_id }}
cancel-in-progress: true
+env:
+ # Diff against merge group SHAs in queue, or PR base branch otherwise
+ DIFF_RANGE: ${{ github.event_name == 'merge_group' && format('{0}...{1}', github.event.merge_group.base_sha, github.event.merge_group.head_sha) || format('origin/{0}...HEAD', github.base_ref) }}
+
jobs:
check-links:
runs-on: ubuntu-latest
@@ -27,6 +39,8 @@ jobs:
steps:
- name: Checkout Repo
uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
- name: Get changed markdown files
id: changed-files
@@ -34,16 +48,47 @@ jobs:
with:
files: |
**/*.md
+ **/*.rst
- name: Install markdown-link-check
if: steps.changed-files.outputs.any_changed == 'true'
run: npm install -g markdown-link-check@v3.12.2
- - name: Run markdown-link-check
- if: steps.changed-files.outputs.any_changed == 'true'
+ - name: Check links on push to main
+ if: steps.changed-files.outputs.any_changed == 'true' && github.event_name == 'push'
run: |
markdown-link-check \
--verbose \
--config .github/workflows/check_links_config.json \
${{ steps.changed-files.outputs.all_changed_files }} \
- || { echo "Check that anchor links are lowercase"; exit 1; } \ No newline at end of file
+ || { echo "Check that anchor links are lowercase"; exit 1; }
+
+ - name: Check new links only on pull requests and merge groups
+ if: steps.changed-files.outputs.any_changed == 'true' && (github.event_name == 'pull_request' || github.event_name == 'merge_group')
+ run: |
+ # Extract URLs only from added lines in the diff to avoid
+ # rate limiting when checking all links in large files like
+ # CHANGELOG.md. Only new/changed links are checked on PRs;
+ # pushes to main still check all links in changed files.
+ git diff "$DIFF_RANGE" -- \
+ ${{ steps.changed-files.outputs.all_changed_files }} \
+ | grep '^+' | grep -v '^+++' \
+ | grep -oP 'https?://[^\s\)\]\"'"'"'`>]+' \
+ | sort -u > /tmp/new_links.txt
+
+ if [ ! -s /tmp/new_links.txt ]; then
+ echo "No new links found in diff, skipping check"
+ exit 0
+ fi
+
+ echo "Checking $(wc -l < /tmp/new_links.txt) new links:"
+ cat /tmp/new_links.txt
+
+ # Write links as markdown so markdown-link-check can parse them
+ awk '{print "- <" $0 ">"}' /tmp/new_links.txt > /tmp/new_links.md
+
+ markdown-link-check \
+ --verbose \
+ --config .github/workflows/check_links_config.json \
+ /tmp/new_links.md \
+ || { echo "Check that anchor links are lowercase"; exit 1; }
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f781b40..48c5726 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,6 +8,7 @@ on:
branches:
- 'main'
pull_request:
+ merge_group:
permissions:
contents: read
diff --git a/.github/workflows/generate_workflows.py b/.github/workflows/generate_workflows.py
index 21c532c..5b53c21 100644
--- a/.github/workflows/generate_workflows.py
+++ b/.github/workflows/generate_workflows.py
@@ -14,7 +14,7 @@ _tox_test_env_regex = re_compile(
)
_tox_lint_env_regex = re_compile(r"lint-(?P<name>[-\w]+)")
_tox_contrib_env_regex = re_compile(
- r"py39-test-(?P<name>[-\w]+\w)-?(?P<contrib_requirements>\d+)?"
+ r"py310-test-(?P<name>[-\w]+\w)-?(?P<contrib_requirements>\d+)?"
)
@@ -47,8 +47,7 @@ def get_test_job_datas(tox_envs: list, operating_systems: list) -> list:
os_alias = {"ubuntu-latest": "Ubuntu", "windows-latest": "Windows"}
python_version_alias = {
- "pypy3": "pypy-3.9",
- "py39": "3.9",
+ "pypy3": "pypy-3.10",
"py310": "3.10",
"py311": "3.11",
"py312": "3.12",
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 81f7bb4..f3fe967 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -233,6 +233,25 @@ jobs:
- name: Run tests
run: tox -e lint-opentelemetry-exporter-otlp-proto-common
+ lint-opentelemetry-exporter-otlp-json-common:
+ name: opentelemetry-exporter-otlp-json-common
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ steps:
+ - name: Checkout repo @ SHA - ${{ github.sha }}
+ uses: actions/checkout@v4
+
+ - name: Set up Python 3.14
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.14"
+
+ - name: Install tox
+ run: pip install tox-uv
+
+ - name: Run tests
+ run: tox -e lint-opentelemetry-exporter-otlp-json-common
+
lint-opentelemetry-exporter-otlp-combined:
name: opentelemetry-exporter-otlp-combined
runs-on: ubuntu-latest
@@ -422,3 +441,22 @@ jobs:
- name: Run tests
run: tox -e lint-opentelemetry-test-utils
+
+ lint-license-header-check:
+ name: license-header-check
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ steps:
+ - name: Checkout repo @ SHA - ${{ github.sha }}
+ uses: actions/checkout@v4
+
+ - name: Set up Python 3.14
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.14"
+
+ - name: Install tox
+ run: pip install tox-uv
+
+ - name: Run tests
+ run: tox -e lint-license-header-check
diff --git a/.github/workflows/misc.yml b/.github/workflows/misc.yml
index 7c5d428..88c19a0 100644
--- a/.github/workflows/misc.yml
+++ b/.github/workflows/misc.yml
@@ -98,7 +98,9 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
if: |
- github.event.pull_request.user.login != 'otelbot[bot]' && github.event_name == 'pull_request'
+ github.event_name == 'merge_group' ||
+ (github.event_name == 'pull_request' &&
+ github.event.pull_request.user.login != 'otelbot[bot]')
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-patch-release.yml
index 8414d82..a4856ea 100644
--- a/.github/workflows/prepare-patch-release.yml
+++ b/.github/workflows/prepare-patch-release.yml
@@ -14,8 +14,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- - name: Install toml
- run: pip install toml
+ - name: Install dependencies
+ run: pip install toml towncrier
- run: |
if [[ ! $GITHUB_REF_NAME =~ ^release/v[0-9]+\.[0-9]+\.x-0\.[0-9]+bx$ ]]; then
@@ -23,11 +23,6 @@ jobs:
exit 1
fi
- if ! grep --quiet "^## Unreleased$" CHANGELOG.md; then
- echo the change log is missing an \"Unreleased\" section
- exit 1
- fi
-
- name: Set environment variables
run: |
stable_version=$(./scripts/eachdist.py version --mode stable)
@@ -62,10 +57,8 @@ jobs:
- name: Update version
run: .github/scripts/update-version-patch.sh $STABLE_VERSION $UNSTABLE_VERSION $STABLE_VERSION_PREV $UNSTABLE_VERSION_PREV
- - name: Update the change log with the approximate release date
- run: |
- date=$(date "+%Y-%m-%d")
- sed -Ei "s/^## Unreleased$/## Version ${STABLE_VERSION}\/${UNSTABLE_VERSION} ($date)/" CHANGELOG.md
+ - name: Generate changelog
+ run: towncrier build --yes --version "$STABLE_VERSION/$UNSTABLE_VERSION"
- name: Use CLA approved github bot
run: .github/scripts/use-cla-approved-github-bot.sh
@@ -99,3 +92,31 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr edit ${{ steps.create_pr.outputs.pr_url }} --add-label "prepare-release"
+
+ - uses: actions/checkout@v4
+ with:
+ ref: main
+
+ - name: Use CLA approved github bot
+ run: .github/scripts/use-cla-approved-github-bot.sh
+
+ - name: Backport patch release changelog to main
+ env:
+ GITHUB_TOKEN: ${{ steps.otelbot-token.outputs.token }}
+ run: |
+ release_branch="otelbot/prepare-release-${STABLE_VERSION}-${UNSTABLE_VERSION}"
+ message="Backport ${STABLE_VERSION}/${UNSTABLE_VERSION} changelog"
+ body="Backport \`${STABLE_VERSION}/${UNSTABLE_VERSION}\` changelog"
+ branch="otelbot/backport-changelog-from-${STABLE_VERSION}-${UNSTABLE_VERSION}"
+
+ git fetch origin $release_branch
+
+ # Copy the updated CHANGELOG.md from the release branch
+ git checkout $release_branch -- CHANGELOG.md
+ git commit -m "$message"
+
+ git push origin HEAD:$branch
+ gh pr create --title "$message" \
+ --body "$body" \
+ --head $branch \
+ --base main
diff --git a/.github/workflows/prepare-release-branch.yml b/.github/workflows/prepare-release-branch.yml
index ee8e971..4787458 100644
--- a/.github/workflows/prepare-release-branch.yml
+++ b/.github/workflows/prepare-release-branch.yml
@@ -27,11 +27,6 @@ jobs:
exit 1
fi
- if ! grep --quiet "^## Unreleased$" CHANGELOG.md; then
- echo the change log is missing an \"Unreleased\" section
- exit 1
- fi
-
if [[ ! -z $PRERELEASE_VERSION ]]; then
stable_version=$(./scripts/eachdist.py version --mode stable)
stable_version=${stable_version//.dev/}
@@ -50,8 +45,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- - name: Install toml
- run: pip install toml
+ - name: Install dependencies
+ run: pip install toml towncrier
- name: Create release branch
env:
@@ -88,10 +83,8 @@ jobs:
- name: Update version
run: .github/scripts/update-version.sh $STABLE_VERSION $UNSTABLE_VERSION
- - name: Update the change log with the approximate release date
- run: |
- date=$(date "+%Y-%m-%d")
- sed -Ei "s/^## Unreleased$/## Version ${STABLE_VERSION}\/${UNSTABLE_VERSION} ($date)/" CHANGELOG.md
+ - name: Generate changelog
+ run: towncrier build --yes --version "$STABLE_VERSION/$UNSTABLE_VERSION"
- name: Use CLA approved github bot
run: .github/scripts/use-cla-approved-github-bot.sh
@@ -135,8 +128,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- - name: Install toml
- run: pip install toml
+ - name: Install dependencies
+ run: pip install toml towncrier
- name: Set environment variables
env:
@@ -184,11 +177,8 @@ jobs:
- name: Update version
run: .github/scripts/update-version.sh $STABLE_NEXT_VERSION $UNSTABLE_NEXT_VERSION
- - name: Update the change log on main
- run: |
- # the actual release date on main will be updated at the end of the release workflow
- date=$(date "+%Y-%m-%d")
- sed -Ei "s/^## Unreleased$/## Unreleased\n\n## Version ${STABLE_VERSION}\/${UNSTABLE_VERSION} ($date)/" CHANGELOG.md
+ - name: Generate changelog
+ run: towncrier build --yes --version "$STABLE_VERSION/$UNSTABLE_VERSION"
- name: Use CLA approved github bot
run: .github/scripts/use-cla-approved-github-bot.sh
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6b1bd7c..3179fc3 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -74,7 +74,7 @@ jobs:
# next few steps publish to pypi
- uses: actions/setup-python@v5
with:
- python-version: '3.9'
+ python-version: '3.10'
- name: Build wheels
run: ./scripts/build.sh
diff --git a/.github/workflows/templates/ci.yml.j2 b/.github/workflows/templates/ci.yml.j2
index 9094f84..3590cde 100644
--- a/.github/workflows/templates/ci.yml.j2
+++ b/.github/workflows/templates/ci.yml.j2
@@ -8,6 +8,7 @@ on:
branches:
- 'main'
pull_request:
+ merge_group:
permissions:
contents: read
diff --git a/.github/workflows/templates/misc.yml.j2 b/.github/workflows/templates/misc.yml.j2
index 941c593..b8b381b 100644
--- a/.github/workflows/templates/misc.yml.j2
+++ b/.github/workflows/templates/misc.yml.j2
@@ -46,7 +46,9 @@ jobs:
{%- endif %}
{%- if job_data == "docs" %}
if: |
- github.event.pull_request.user.login != 'otelbot[bot]' && github.event_name == 'pull_request'
+ github.event_name == 'merge_group' ||
+ (github.event_name == 'pull_request' &&
+ github.event.pull_request.user.login != 'otelbot[bot]')
{%- endif %}
steps:
- name: Checkout repo @ SHA - ${% raw %}{{ github.sha }}{% endraw %}
diff --git a/.github/workflows/templates/test.yml.j2 b/.github/workflows/templates/test.yml.j2
index 8ca5482..d0c66c2 100644
--- a/.github/workflows/templates/test.yml.j2
+++ b/.github/workflows/templates/test.yml.j2
@@ -20,6 +20,7 @@ env:
contains(github.event.pull_request.labels.*.name, 'backport') && github.event.pull_request.base.ref ||
'main'
) || 'main' }}{% endraw %}
+ WEAVER_VERSION: "v0.22.1"
PIP_EXISTS_ACTION: w
CONTRIB_REPO_UTIL_HTTP: ${% raw %}{{ github.workspace }}{% endraw %}/opentelemetry-python-contrib/util/opentelemetry-util-http
CONTRIB_REPO_INSTRUMENTATION: ${% raw %}{{ github.workspace }}{% endraw %}/opentelemetry-python-contrib/opentelemetry-instrumentation
@@ -51,6 +52,14 @@ jobs:
ref: ${% raw %}{{ env.CONTRIB_REPO_SHA }}{% endraw %}
path: opentelemetry-python-contrib
{%- endif %}
+ {%- if "test-opentelemetry-test-utils" in job_data.tox_env and job_data.os != "windows-latest" %}
+
+ - name: Install weaver
+ run: |
+ WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${% raw %}{{ env.WEAVER_VERSION }}{% endraw %}/weaver-x86_64-unknown-linux-gnu.tar.xz"
+ curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
+ sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
+ {%- endif %}
- name: Set up Python {{ job_data.python_version }}
uses: actions/setup-python@v5
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 4a32329..b481fbf 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -20,6 +20,7 @@ env:
contains(github.event.pull_request.labels.*.name, 'backport') && github.event.pull_request.base.ref ||
'main'
) || 'main' }}
+ WEAVER_VERSION: "v0.22.1"
PIP_EXISTS_ACTION: w
CONTRIB_REPO_UTIL_HTTP: ${{ github.workspace }}/opentelemetry-python-contrib/util/opentelemetry-util-http
CONTRIB_REPO_INSTRUMENTATION: ${{ github.workspace }}/opentelemetry-python-contrib/opentelemetry-instrumentation
@@ -29,25 +30,6 @@ env:
jobs:
- py39-test-opentelemetry-api_ubuntu-latest:
- name: opentelemetry-api 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-api -- -ra
-
py310-test-opentelemetry-api_ubuntu-latest:
name: opentelemetry-api 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -163,17 +145,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-api -- -ra
pypy3-test-opentelemetry-api_ubuntu-latest:
- name: opentelemetry-api pypy-3.9 Ubuntu
+ name: opentelemetry-api pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -181,44 +163,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-api -- -ra
- py39-test-opentelemetry-proto-gen-oldest_ubuntu-latest:
- name: opentelemetry-proto-gen-oldest 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-proto-gen-oldest -- -ra
-
- py39-test-opentelemetry-proto-gen-latest_ubuntu-latest:
- name: opentelemetry-proto-gen-latest 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-proto-gen-latest -- -ra
-
py310-test-opentelemetry-proto-gen-oldest_ubuntu-latest:
name: opentelemetry-proto-gen-oldest 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -448,17 +392,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-proto-gen-latest -- -ra
pypy3-test-opentelemetry-proto-gen-oldest_ubuntu-latest:
- name: opentelemetry-proto-gen-oldest pypy-3.9 Ubuntu
+ name: opentelemetry-proto-gen-oldest pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -467,17 +411,17 @@ jobs:
run: tox -e pypy3-test-opentelemetry-proto-gen-oldest -- -ra
pypy3-test-opentelemetry-proto-gen-latest_ubuntu-latest:
- name: opentelemetry-proto-gen-latest pypy-3.9 Ubuntu
+ name: opentelemetry-proto-gen-latest pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -485,44 +429,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-proto-gen-latest -- -ra
- py39-test-opentelemetry-protojson-gen-oldest_ubuntu-latest:
- name: opentelemetry-protojson-gen-oldest 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-protojson-gen-oldest -- -ra
-
- py39-test-opentelemetry-protojson-gen-latest_ubuntu-latest:
- name: opentelemetry-protojson-gen-latest 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-protojson-gen-latest -- -ra
-
py310-test-opentelemetry-protojson-gen-oldest_ubuntu-latest:
name: opentelemetry-protojson-gen-oldest 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -752,17 +658,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-protojson-gen-latest -- -ra
pypy3-test-opentelemetry-protojson-gen-oldest_ubuntu-latest:
- name: opentelemetry-protojson-gen-oldest pypy-3.9 Ubuntu
+ name: opentelemetry-protojson-gen-oldest pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -771,17 +677,17 @@ jobs:
run: tox -e pypy3-test-opentelemetry-protojson-gen-oldest -- -ra
pypy3-test-opentelemetry-protojson-gen-latest_ubuntu-latest:
- name: opentelemetry-protojson-gen-latest pypy-3.9 Ubuntu
+ name: opentelemetry-protojson-gen-latest pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -789,25 +695,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-protojson-gen-latest -- -ra
- py39-test-opentelemetry-codegen-json_ubuntu-latest:
- name: opentelemetry-codegen-json 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-codegen-json -- -ra
-
py310-test-opentelemetry-codegen-json_ubuntu-latest:
name: opentelemetry-codegen-json 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -903,25 +790,6 @@ jobs:
- name: Run tests
run: tox -e py314-test-opentelemetry-codegen-json -- -ra
- py39-test-opentelemetry-sdk_ubuntu-latest:
- name: opentelemetry-sdk 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-sdk -- -ra
-
py310-test-opentelemetry-sdk_ubuntu-latest:
name: opentelemetry-sdk 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -1037,17 +905,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-sdk -- -ra
pypy3-test-opentelemetry-sdk_ubuntu-latest:
- name: opentelemetry-sdk pypy-3.9 Ubuntu
+ name: opentelemetry-sdk pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -1055,25 +923,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-sdk -- -ra
- py39-test-opentelemetry-semantic-conventions_ubuntu-latest:
- name: opentelemetry-semantic-conventions 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-semantic-conventions -- -ra
-
py310-test-opentelemetry-semantic-conventions_ubuntu-latest:
name: opentelemetry-semantic-conventions 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -1189,17 +1038,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-semantic-conventions -- -ra
pypy3-test-opentelemetry-semantic-conventions_ubuntu-latest:
- name: opentelemetry-semantic-conventions pypy-3.9 Ubuntu
+ name: opentelemetry-semantic-conventions pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -1207,32 +1056,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-semantic-conventions -- -ra
- py39-test-opentelemetry-getting-started_ubuntu-latest:
- name: opentelemetry-getting-started 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Checkout contrib repo @ ${{ env.CONTRIB_REPO_SHA }}
- uses: actions/checkout@v4
- with:
- repository: open-telemetry/opentelemetry-python-contrib
- ref: ${{ env.CONTRIB_REPO_SHA }}
- path: opentelemetry-python-contrib
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-getting-started -- -ra
-
py310-test-opentelemetry-getting-started_ubuntu-latest:
name: opentelemetry-getting-started 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -1363,25 +1186,6 @@ jobs:
- name: Run tests
run: tox -e py314-test-opentelemetry-getting-started -- -ra
- py39-test-opentelemetry-opentracing-shim_ubuntu-latest:
- name: opentelemetry-opentracing-shim 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-opentracing-shim -- -ra
-
py310-test-opentelemetry-opentracing-shim_ubuntu-latest:
name: opentelemetry-opentracing-shim 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -1497,17 +1301,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-opentracing-shim -- -ra
pypy3-test-opentelemetry-opentracing-shim_ubuntu-latest:
- name: opentelemetry-opentracing-shim pypy-3.9 Ubuntu
+ name: opentelemetry-opentracing-shim pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -1515,25 +1319,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-opentracing-shim -- -ra
- py39-test-opentelemetry-opencensus-shim_ubuntu-latest:
- name: opentelemetry-opencensus-shim 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-opencensus-shim -- -ra
-
py310-test-opentelemetry-opencensus-shim_ubuntu-latest:
name: opentelemetry-opencensus-shim 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -1629,25 +1414,6 @@ jobs:
- name: Run tests
run: tox -e py314-test-opentelemetry-opencensus-shim -- -ra
- py39-test-opentelemetry-exporter-opencensus_ubuntu-latest:
- name: opentelemetry-exporter-opencensus 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-opencensus -- -ra
-
py310-test-opentelemetry-exporter-opencensus_ubuntu-latest:
name: opentelemetry-exporter-opencensus 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -1743,25 +1509,6 @@ jobs:
- name: Run tests
run: tox -e py314-test-opentelemetry-exporter-opencensus -- -ra
- py39-test-opentelemetry-exporter-otlp-proto-common_ubuntu-latest:
- name: opentelemetry-exporter-otlp-proto-common 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-otlp-proto-common -- -ra
-
py310-test-opentelemetry-exporter-otlp-proto-common_ubuntu-latest:
name: opentelemetry-exporter-otlp-proto-common 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -1877,17 +1624,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-exporter-otlp-proto-common -- -ra
pypy3-test-opentelemetry-exporter-otlp-proto-common_ubuntu-latest:
- name: opentelemetry-exporter-otlp-proto-common pypy-3.9 Ubuntu
+ name: opentelemetry-exporter-otlp-proto-common pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -1895,157 +1642,233 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-exporter-otlp-proto-common -- -ra
- py39-test-opentelemetry-exporter-otlp-combined_ubuntu-latest:
- name: opentelemetry-exporter-otlp-combined 3.9 Ubuntu
+ py310-test-opentelemetry-exporter-otlp-json-common_ubuntu-latest:
+ name: opentelemetry-exporter-otlp-json-common 3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.9
+ - name: Set up Python 3.10
uses: actions/setup-python@v5
with:
- python-version: "3.9"
+ python-version: "3.10"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-otlp-combined -- -ra
+ run: tox -e py310-test-opentelemetry-exporter-otlp-json-common -- -ra
- py310-test-opentelemetry-exporter-otlp-combined_ubuntu-latest:
- name: opentelemetry-exporter-otlp-combined 3.10 Ubuntu
+ py311-test-opentelemetry-exporter-otlp-json-common_ubuntu-latest:
+ name: opentelemetry-exporter-otlp-json-common 3.11 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.10
+ - name: Set up Python 3.11
uses: actions/setup-python@v5
with:
- python-version: "3.10"
+ python-version: "3.11"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py310-test-opentelemetry-exporter-otlp-combined -- -ra
+ run: tox -e py311-test-opentelemetry-exporter-otlp-json-common -- -ra
- py311-test-opentelemetry-exporter-otlp-combined_ubuntu-latest:
- name: opentelemetry-exporter-otlp-combined 3.11 Ubuntu
+ py312-test-opentelemetry-exporter-otlp-json-common_ubuntu-latest:
+ name: opentelemetry-exporter-otlp-json-common 3.12 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.11
+ - name: Set up Python 3.12
uses: actions/setup-python@v5
with:
- python-version: "3.11"
+ python-version: "3.12"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py311-test-opentelemetry-exporter-otlp-combined -- -ra
+ run: tox -e py312-test-opentelemetry-exporter-otlp-json-common -- -ra
- py312-test-opentelemetry-exporter-otlp-combined_ubuntu-latest:
- name: opentelemetry-exporter-otlp-combined 3.12 Ubuntu
+ py313-test-opentelemetry-exporter-otlp-json-common_ubuntu-latest:
+ name: opentelemetry-exporter-otlp-json-common 3.13 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.12
+ - name: Set up Python 3.13
uses: actions/setup-python@v5
with:
- python-version: "3.12"
+ python-version: "3.13"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py312-test-opentelemetry-exporter-otlp-combined -- -ra
+ run: tox -e py313-test-opentelemetry-exporter-otlp-json-common -- -ra
- py313-test-opentelemetry-exporter-otlp-combined_ubuntu-latest:
- name: opentelemetry-exporter-otlp-combined 3.13 Ubuntu
+ py314-test-opentelemetry-exporter-otlp-json-common_ubuntu-latest:
+ name: opentelemetry-exporter-otlp-json-common 3.14 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.13
+ - name: Set up Python 3.14
uses: actions/setup-python@v5
with:
- python-version: "3.13"
+ python-version: "3.14"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py313-test-opentelemetry-exporter-otlp-combined -- -ra
+ run: tox -e py314-test-opentelemetry-exporter-otlp-json-common -- -ra
- py314-test-opentelemetry-exporter-otlp-combined_ubuntu-latest:
- name: opentelemetry-exporter-otlp-combined 3.14 Ubuntu
+ py314t-test-opentelemetry-exporter-otlp-json-common_ubuntu-latest:
+ name: opentelemetry-exporter-otlp-json-common 3.14t Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.14
+ - name: Set up Python 3.14t
uses: actions/setup-python@v5
with:
- python-version: "3.14"
+ python-version: "3.14t"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py314-test-opentelemetry-exporter-otlp-combined -- -ra
+ run: tox -e py314t-test-opentelemetry-exporter-otlp-json-common -- -ra
+
+ pypy3-test-opentelemetry-exporter-otlp-json-common_ubuntu-latest:
+ name: opentelemetry-exporter-otlp-json-common pypy-3.10 Ubuntu
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ steps:
+ - name: Checkout repo @ SHA - ${{ github.sha }}
+ uses: actions/checkout@v4
- py39-test-opentelemetry-exporter-otlp-proto-grpc-oldest_ubuntu-latest:
- name: opentelemetry-exporter-otlp-proto-grpc-oldest 3.9 Ubuntu
+ - name: Set up Python pypy-3.10
+ uses: actions/setup-python@v5
+ with:
+ python-version: "pypy-3.10"
+
+ - name: Install tox
+ run: pip install tox-uv
+
+ - name: Run tests
+ run: tox -e pypy3-test-opentelemetry-exporter-otlp-json-common -- -ra
+
+ py310-test-opentelemetry-exporter-otlp-combined_ubuntu-latest:
+ name: opentelemetry-exporter-otlp-combined 3.10 Ubuntu
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ steps:
+ - name: Checkout repo @ SHA - ${{ github.sha }}
+ uses: actions/checkout@v4
+
+ - name: Set up Python 3.10
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+
+ - name: Install tox
+ run: pip install tox-uv
+
+ - name: Run tests
+ run: tox -e py310-test-opentelemetry-exporter-otlp-combined -- -ra
+
+ py311-test-opentelemetry-exporter-otlp-combined_ubuntu-latest:
+ name: opentelemetry-exporter-otlp-combined 3.11 Ubuntu
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ steps:
+ - name: Checkout repo @ SHA - ${{ github.sha }}
+ uses: actions/checkout@v4
+
+ - name: Set up Python 3.11
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.11"
+
+ - name: Install tox
+ run: pip install tox-uv
+
+ - name: Run tests
+ run: tox -e py311-test-opentelemetry-exporter-otlp-combined -- -ra
+
+ py312-test-opentelemetry-exporter-otlp-combined_ubuntu-latest:
+ name: opentelemetry-exporter-otlp-combined 3.12 Ubuntu
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ steps:
+ - name: Checkout repo @ SHA - ${{ github.sha }}
+ uses: actions/checkout@v4
+
+ - name: Set up Python 3.12
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Install tox
+ run: pip install tox-uv
+
+ - name: Run tests
+ run: tox -e py312-test-opentelemetry-exporter-otlp-combined -- -ra
+
+ py313-test-opentelemetry-exporter-otlp-combined_ubuntu-latest:
+ name: opentelemetry-exporter-otlp-combined 3.13 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.9
+ - name: Set up Python 3.13
uses: actions/setup-python@v5
with:
- python-version: "3.9"
+ python-version: "3.13"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-otlp-proto-grpc-oldest -- -ra
+ run: tox -e py313-test-opentelemetry-exporter-otlp-combined -- -ra
- py39-test-opentelemetry-exporter-otlp-proto-grpc-latest_ubuntu-latest:
- name: opentelemetry-exporter-otlp-proto-grpc-latest 3.9 Ubuntu
+ py314-test-opentelemetry-exporter-otlp-combined_ubuntu-latest:
+ name: opentelemetry-exporter-otlp-combined 3.14 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.9
+ - name: Set up Python 3.14
uses: actions/setup-python@v5
with:
- python-version: "3.9"
+ python-version: "3.14"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-otlp-proto-grpc-latest -- -ra
+ run: tox -e py314-test-opentelemetry-exporter-otlp-combined -- -ra
py310-test-opentelemetry-exporter-otlp-proto-grpc-oldest_ubuntu-latest:
name: opentelemetry-exporter-otlp-proto-grpc-oldest 3.10 Ubuntu
@@ -2237,25 +2060,6 @@ jobs:
- name: Run tests
run: tox -e py314-test-opentelemetry-exporter-otlp-proto-grpc-latest -- -ra
- py39-test-opentelemetry-exporter-otlp-proto-http_ubuntu-latest:
- name: opentelemetry-exporter-otlp-proto-http 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-otlp-proto-http -- -ra
-
py310-test-opentelemetry-exporter-otlp-proto-http_ubuntu-latest:
name: opentelemetry-exporter-otlp-proto-http 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -2371,17 +2175,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-exporter-otlp-proto-http -- -ra
pypy3-test-opentelemetry-exporter-otlp-proto-http_ubuntu-latest:
- name: opentelemetry-exporter-otlp-proto-http pypy-3.9 Ubuntu
+ name: opentelemetry-exporter-otlp-proto-http pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -2389,25 +2193,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-exporter-otlp-proto-http -- -ra
- py39-test-opentelemetry-exporter-prometheus_ubuntu-latest:
- name: opentelemetry-exporter-prometheus 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-prometheus -- -ra
-
py310-test-opentelemetry-exporter-prometheus_ubuntu-latest:
name: opentelemetry-exporter-prometheus 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -2523,17 +2308,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-exporter-prometheus -- -ra
pypy3-test-opentelemetry-exporter-prometheus_ubuntu-latest:
- name: opentelemetry-exporter-prometheus pypy-3.9 Ubuntu
+ name: opentelemetry-exporter-prometheus pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -2541,25 +2326,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-exporter-prometheus -- -ra
- py39-test-opentelemetry-exporter-zipkin-combined_ubuntu-latest:
- name: opentelemetry-exporter-zipkin-combined 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-zipkin-combined -- -ra
-
py310-test-opentelemetry-exporter-zipkin-combined_ubuntu-latest:
name: opentelemetry-exporter-zipkin-combined 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -2675,17 +2441,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-exporter-zipkin-combined -- -ra
pypy3-test-opentelemetry-exporter-zipkin-combined_ubuntu-latest:
- name: opentelemetry-exporter-zipkin-combined pypy-3.9 Ubuntu
+ name: opentelemetry-exporter-zipkin-combined pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -2693,25 +2459,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-exporter-zipkin-combined -- -ra
- py39-test-opentelemetry-exporter-zipkin-proto-http_ubuntu-latest:
- name: opentelemetry-exporter-zipkin-proto-http 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-zipkin-proto-http -- -ra
-
py310-test-opentelemetry-exporter-zipkin-proto-http_ubuntu-latest:
name: opentelemetry-exporter-zipkin-proto-http 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -2827,17 +2574,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-exporter-zipkin-proto-http -- -ra
pypy3-test-opentelemetry-exporter-zipkin-proto-http_ubuntu-latest:
- name: opentelemetry-exporter-zipkin-proto-http pypy-3.9 Ubuntu
+ name: opentelemetry-exporter-zipkin-proto-http pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -2845,25 +2592,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-exporter-zipkin-proto-http -- -ra
- py39-test-opentelemetry-exporter-zipkin-json_ubuntu-latest:
- name: opentelemetry-exporter-zipkin-json 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-zipkin-json -- -ra
-
py310-test-opentelemetry-exporter-zipkin-json_ubuntu-latest:
name: opentelemetry-exporter-zipkin-json 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -2979,17 +2707,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-exporter-zipkin-json -- -ra
pypy3-test-opentelemetry-exporter-zipkin-json_ubuntu-latest:
- name: opentelemetry-exporter-zipkin-json pypy-3.9 Ubuntu
+ name: opentelemetry-exporter-zipkin-json pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -2997,25 +2725,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-exporter-zipkin-json -- -ra
- py39-test-opentelemetry-propagator-b3_ubuntu-latest:
- name: opentelemetry-propagator-b3 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-propagator-b3 -- -ra
-
py310-test-opentelemetry-propagator-b3_ubuntu-latest:
name: opentelemetry-propagator-b3 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -3131,17 +2840,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-propagator-b3 -- -ra
pypy3-test-opentelemetry-propagator-b3_ubuntu-latest:
- name: opentelemetry-propagator-b3 pypy-3.9 Ubuntu
+ name: opentelemetry-propagator-b3 pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -3149,25 +2858,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-propagator-b3 -- -ra
- py39-test-opentelemetry-propagator-jaeger_ubuntu-latest:
- name: opentelemetry-propagator-jaeger 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-propagator-jaeger -- -ra
-
py310-test-opentelemetry-propagator-jaeger_ubuntu-latest:
name: opentelemetry-propagator-jaeger 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -3283,17 +2973,17 @@ jobs:
run: tox -e py314t-test-opentelemetry-propagator-jaeger -- -ra
pypy3-test-opentelemetry-propagator-jaeger_ubuntu-latest:
- name: opentelemetry-propagator-jaeger pypy-3.9 Ubuntu
+ name: opentelemetry-propagator-jaeger pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -3301,25 +2991,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-propagator-jaeger -- -ra
- py39-test-opentelemetry-test-utils_ubuntu-latest:
- name: opentelemetry-test-utils 3.9 Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-test-utils -- -ra
-
py310-test-opentelemetry-test-utils_ubuntu-latest:
name: opentelemetry-test-utils 3.10 Ubuntu
runs-on: ubuntu-latest
@@ -3328,6 +2999,12 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
+ - name: Install weaver
+ run: |
+ WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
+ curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
+ sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
+
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
@@ -3347,6 +3024,12 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
+ - name: Install weaver
+ run: |
+ WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
+ curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
+ sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
+
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
@@ -3366,6 +3049,12 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
+ - name: Install weaver
+ run: |
+ WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
+ curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
+ sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
+
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
@@ -3385,6 +3074,12 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
+ - name: Install weaver
+ run: |
+ WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
+ curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
+ sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
+
- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
@@ -3404,6 +3099,12 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
+ - name: Install weaver
+ run: |
+ WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
+ curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
+ sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
+
- name: Set up Python 3.14
uses: actions/setup-python@v5
with:
@@ -3415,64 +3116,30 @@ jobs:
- name: Run tests
run: tox -e py314-test-opentelemetry-test-utils -- -ra
- py314t-test-opentelemetry-test-utils_ubuntu-latest:
- name: opentelemetry-test-utils 3.14t Ubuntu
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.14t
- uses: actions/setup-python@v5
- with:
- python-version: "3.14t"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py314t-test-opentelemetry-test-utils -- -ra
-
pypy3-test-opentelemetry-test-utils_ubuntu-latest:
- name: opentelemetry-test-utils pypy-3.9 Ubuntu
+ name: opentelemetry-test-utils pypy-3.10 Ubuntu
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
- uses: actions/setup-python@v5
- with:
- python-version: "pypy-3.9"
+ - name: Install weaver
+ run: |
+ WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
+ curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
+ sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e pypy3-test-opentelemetry-test-utils -- -ra
-
- py39-test-opentelemetry-api_windows-latest:
- name: opentelemetry-api 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py39-test-opentelemetry-api -- -ra
+ run: tox -e pypy3-test-opentelemetry-test-utils -- -ra
py310-test-opentelemetry-api_windows-latest:
name: opentelemetry-api 3.10 Windows
@@ -3601,7 +3268,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-api -- -ra
pypy3-test-opentelemetry-api_windows-latest:
- name: opentelemetry-api pypy-3.9 Windows
+ name: opentelemetry-api pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -3610,10 +3277,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -3621,48 +3288,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-api -- -ra
- py39-test-opentelemetry-proto-gen-oldest_windows-latest:
- name: opentelemetry-proto-gen-oldest 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-proto-gen-oldest -- -ra
-
- py39-test-opentelemetry-proto-gen-latest_windows-latest:
- name: opentelemetry-proto-gen-latest 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-proto-gen-latest -- -ra
-
py310-test-opentelemetry-proto-gen-oldest_windows-latest:
name: opentelemetry-proto-gen-oldest 3.10 Windows
runs-on: windows-latest
@@ -3916,7 +3541,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-proto-gen-latest -- -ra
pypy3-test-opentelemetry-proto-gen-oldest_windows-latest:
- name: opentelemetry-proto-gen-oldest pypy-3.9 Windows
+ name: opentelemetry-proto-gen-oldest pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -3925,10 +3550,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -3937,7 +3562,7 @@ jobs:
run: tox -e pypy3-test-opentelemetry-proto-gen-oldest -- -ra
pypy3-test-opentelemetry-proto-gen-latest_windows-latest:
- name: opentelemetry-proto-gen-latest pypy-3.9 Windows
+ name: opentelemetry-proto-gen-latest pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -3946,10 +3571,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -3957,48 +3582,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-proto-gen-latest -- -ra
- py39-test-opentelemetry-protojson-gen-oldest_windows-latest:
- name: opentelemetry-protojson-gen-oldest 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-protojson-gen-oldest -- -ra
-
- py39-test-opentelemetry-protojson-gen-latest_windows-latest:
- name: opentelemetry-protojson-gen-latest 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-protojson-gen-latest -- -ra
-
py310-test-opentelemetry-protojson-gen-oldest_windows-latest:
name: opentelemetry-protojson-gen-oldest 3.10 Windows
runs-on: windows-latest
@@ -4252,7 +3835,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-protojson-gen-latest -- -ra
pypy3-test-opentelemetry-protojson-gen-oldest_windows-latest:
- name: opentelemetry-protojson-gen-oldest pypy-3.9 Windows
+ name: opentelemetry-protojson-gen-oldest pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -4261,10 +3844,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -4273,7 +3856,7 @@ jobs:
run: tox -e pypy3-test-opentelemetry-protojson-gen-oldest -- -ra
pypy3-test-opentelemetry-protojson-gen-latest_windows-latest:
- name: opentelemetry-protojson-gen-latest pypy-3.9 Windows
+ name: opentelemetry-protojson-gen-latest pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -4282,10 +3865,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -4293,27 +3876,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-protojson-gen-latest -- -ra
- py39-test-opentelemetry-codegen-json_windows-latest:
- name: opentelemetry-codegen-json 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-codegen-json -- -ra
-
py310-test-opentelemetry-codegen-json_windows-latest:
name: opentelemetry-codegen-json 3.10 Windows
runs-on: windows-latest
@@ -4419,27 +3981,6 @@ jobs:
- name: Run tests
run: tox -e py314-test-opentelemetry-codegen-json -- -ra
- py39-test-opentelemetry-sdk_windows-latest:
- name: opentelemetry-sdk 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-sdk -- -ra
-
py310-test-opentelemetry-sdk_windows-latest:
name: opentelemetry-sdk 3.10 Windows
runs-on: windows-latest
@@ -4567,7 +4108,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-sdk -- -ra
pypy3-test-opentelemetry-sdk_windows-latest:
- name: opentelemetry-sdk pypy-3.9 Windows
+ name: opentelemetry-sdk pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -4576,10 +4117,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -4587,27 +4128,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-sdk -- -ra
- py39-test-opentelemetry-semantic-conventions_windows-latest:
- name: opentelemetry-semantic-conventions 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-semantic-conventions -- -ra
-
py310-test-opentelemetry-semantic-conventions_windows-latest:
name: opentelemetry-semantic-conventions 3.10 Windows
runs-on: windows-latest
@@ -4735,7 +4255,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-semantic-conventions -- -ra
pypy3-test-opentelemetry-semantic-conventions_windows-latest:
- name: opentelemetry-semantic-conventions pypy-3.9 Windows
+ name: opentelemetry-semantic-conventions pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -4744,10 +4264,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -4755,34 +4275,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-semantic-conventions -- -ra
- py39-test-opentelemetry-getting-started_windows-latest:
- name: opentelemetry-getting-started 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Checkout contrib repo @ ${{ env.CONTRIB_REPO_SHA }}
- uses: actions/checkout@v4
- with:
- repository: open-telemetry/opentelemetry-python-contrib
- ref: ${{ env.CONTRIB_REPO_SHA }}
- path: opentelemetry-python-contrib
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-getting-started -- -ra
-
py310-test-opentelemetry-getting-started_windows-latest:
name: opentelemetry-getting-started 3.10 Windows
runs-on: windows-latest
@@ -4923,27 +4415,6 @@ jobs:
- name: Run tests
run: tox -e py314-test-opentelemetry-getting-started -- -ra
- py39-test-opentelemetry-opentracing-shim_windows-latest:
- name: opentelemetry-opentracing-shim 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-opentracing-shim -- -ra
-
py310-test-opentelemetry-opentracing-shim_windows-latest:
name: opentelemetry-opentracing-shim 3.10 Windows
runs-on: windows-latest
@@ -5071,7 +4542,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-opentracing-shim -- -ra
pypy3-test-opentelemetry-opentracing-shim_windows-latest:
- name: opentelemetry-opentracing-shim pypy-3.9 Windows
+ name: opentelemetry-opentracing-shim pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -5080,10 +4551,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -5091,27 +4562,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-opentracing-shim -- -ra
- py39-test-opentelemetry-opencensus-shim_windows-latest:
- name: opentelemetry-opencensus-shim 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-opencensus-shim -- -ra
-
py310-test-opentelemetry-opencensus-shim_windows-latest:
name: opentelemetry-opencensus-shim 3.10 Windows
runs-on: windows-latest
@@ -5217,27 +4667,6 @@ jobs:
- name: Run tests
run: tox -e py314-test-opentelemetry-opencensus-shim -- -ra
- py39-test-opentelemetry-exporter-opencensus_windows-latest:
- name: opentelemetry-exporter-opencensus 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-opencensus -- -ra
-
py310-test-opentelemetry-exporter-opencensus_windows-latest:
name: opentelemetry-exporter-opencensus 3.10 Windows
runs-on: windows-latest
@@ -5343,27 +4772,6 @@ jobs:
- name: Run tests
run: tox -e py314-test-opentelemetry-exporter-opencensus -- -ra
- py39-test-opentelemetry-exporter-otlp-proto-common_windows-latest:
- name: opentelemetry-exporter-otlp-proto-common 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-otlp-proto-common -- -ra
-
py310-test-opentelemetry-exporter-otlp-proto-common_windows-latest:
name: opentelemetry-exporter-otlp-proto-common 3.10 Windows
runs-on: windows-latest
@@ -5491,7 +4899,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-exporter-otlp-proto-common -- -ra
pypy3-test-opentelemetry-exporter-otlp-proto-common_windows-latest:
- name: opentelemetry-exporter-otlp-proto-common pypy-3.9 Windows
+ name: opentelemetry-exporter-otlp-proto-common pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -5500,10 +4908,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -5511,8 +4919,8 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-exporter-otlp-proto-common -- -ra
- py39-test-opentelemetry-exporter-otlp-combined_windows-latest:
- name: opentelemetry-exporter-otlp-combined 3.9 Windows
+ py310-test-opentelemetry-exporter-otlp-json-common_windows-latest:
+ name: opentelemetry-exporter-otlp-json-common 3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -5521,19 +4929,19 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.9
+ - name: Set up Python 3.10
uses: actions/setup-python@v5
with:
- python-version: "3.9"
+ python-version: "3.10"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-otlp-combined -- -ra
+ run: tox -e py310-test-opentelemetry-exporter-otlp-json-common -- -ra
- py310-test-opentelemetry-exporter-otlp-combined_windows-latest:
- name: opentelemetry-exporter-otlp-combined 3.10 Windows
+ py311-test-opentelemetry-exporter-otlp-json-common_windows-latest:
+ name: opentelemetry-exporter-otlp-json-common 3.11 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -5542,19 +4950,19 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.10
+ - name: Set up Python 3.11
uses: actions/setup-python@v5
with:
- python-version: "3.10"
+ python-version: "3.11"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py310-test-opentelemetry-exporter-otlp-combined -- -ra
+ run: tox -e py311-test-opentelemetry-exporter-otlp-json-common -- -ra
- py311-test-opentelemetry-exporter-otlp-combined_windows-latest:
- name: opentelemetry-exporter-otlp-combined 3.11 Windows
+ py312-test-opentelemetry-exporter-otlp-json-common_windows-latest:
+ name: opentelemetry-exporter-otlp-json-common 3.12 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -5563,19 +4971,19 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.11
+ - name: Set up Python 3.12
uses: actions/setup-python@v5
with:
- python-version: "3.11"
+ python-version: "3.12"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py311-test-opentelemetry-exporter-otlp-combined -- -ra
+ run: tox -e py312-test-opentelemetry-exporter-otlp-json-common -- -ra
- py312-test-opentelemetry-exporter-otlp-combined_windows-latest:
- name: opentelemetry-exporter-otlp-combined 3.12 Windows
+ py313-test-opentelemetry-exporter-otlp-json-common_windows-latest:
+ name: opentelemetry-exporter-otlp-json-common 3.13 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -5584,19 +4992,19 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.12
+ - name: Set up Python 3.13
uses: actions/setup-python@v5
with:
- python-version: "3.12"
+ python-version: "3.13"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py312-test-opentelemetry-exporter-otlp-combined -- -ra
+ run: tox -e py313-test-opentelemetry-exporter-otlp-json-common -- -ra
- py313-test-opentelemetry-exporter-otlp-combined_windows-latest:
- name: opentelemetry-exporter-otlp-combined 3.13 Windows
+ py314-test-opentelemetry-exporter-otlp-json-common_windows-latest:
+ name: opentelemetry-exporter-otlp-json-common 3.14 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -5605,19 +5013,19 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.13
+ - name: Set up Python 3.14
uses: actions/setup-python@v5
with:
- python-version: "3.13"
+ python-version: "3.14"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py313-test-opentelemetry-exporter-otlp-combined -- -ra
+ run: tox -e py314-test-opentelemetry-exporter-otlp-json-common -- -ra
- py314-test-opentelemetry-exporter-otlp-combined_windows-latest:
- name: opentelemetry-exporter-otlp-combined 3.14 Windows
+ py314t-test-opentelemetry-exporter-otlp-json-common_windows-latest:
+ name: opentelemetry-exporter-otlp-json-common 3.14t Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -5626,19 +5034,103 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.14
+ - name: Set up Python 3.14t
uses: actions/setup-python@v5
with:
- python-version: "3.14"
+ python-version: "3.14t"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py314-test-opentelemetry-exporter-otlp-combined -- -ra
+ run: tox -e py314t-test-opentelemetry-exporter-otlp-json-common -- -ra
+
+ pypy3-test-opentelemetry-exporter-otlp-json-common_windows-latest:
+ name: opentelemetry-exporter-otlp-json-common pypy-3.10 Windows
+ runs-on: windows-latest
+ timeout-minutes: 30
+ steps:
+ - name: Configure git to support long filenames
+ run: git config --system core.longpaths true
+ - name: Checkout repo @ SHA - ${{ github.sha }}
+ uses: actions/checkout@v4
+
+ - name: Set up Python pypy-3.10
+ uses: actions/setup-python@v5
+ with:
+ python-version: "pypy-3.10"
+
+ - name: Install tox
+ run: pip install tox-uv
+
+ - name: Run tests
+ run: tox -e pypy3-test-opentelemetry-exporter-otlp-json-common -- -ra
+
+ py310-test-opentelemetry-exporter-otlp-combined_windows-latest:
+ name: opentelemetry-exporter-otlp-combined 3.10 Windows
+ runs-on: windows-latest
+ timeout-minutes: 30
+ steps:
+ - name: Configure git to support long filenames
+ run: git config --system core.longpaths true
+ - name: Checkout repo @ SHA - ${{ github.sha }}
+ uses: actions/checkout@v4
+
+ - name: Set up Python 3.10
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+
+ - name: Install tox
+ run: pip install tox-uv
+
+ - name: Run tests
+ run: tox -e py310-test-opentelemetry-exporter-otlp-combined -- -ra
+
+ py311-test-opentelemetry-exporter-otlp-combined_windows-latest:
+ name: opentelemetry-exporter-otlp-combined 3.11 Windows
+ runs-on: windows-latest
+ timeout-minutes: 30
+ steps:
+ - name: Configure git to support long filenames
+ run: git config --system core.longpaths true
+ - name: Checkout repo @ SHA - ${{ github.sha }}
+ uses: actions/checkout@v4
+
+ - name: Set up Python 3.11
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.11"
+
+ - name: Install tox
+ run: pip install tox-uv
+
+ - name: Run tests
+ run: tox -e py311-test-opentelemetry-exporter-otlp-combined -- -ra
+
+ py312-test-opentelemetry-exporter-otlp-combined_windows-latest:
+ name: opentelemetry-exporter-otlp-combined 3.12 Windows
+ runs-on: windows-latest
+ timeout-minutes: 30
+ steps:
+ - name: Configure git to support long filenames
+ run: git config --system core.longpaths true
+ - name: Checkout repo @ SHA - ${{ github.sha }}
+ uses: actions/checkout@v4
+
+ - name: Set up Python 3.12
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Install tox
+ run: pip install tox-uv
- py39-test-opentelemetry-exporter-otlp-proto-grpc-oldest_windows-latest:
- name: opentelemetry-exporter-otlp-proto-grpc-oldest 3.9 Windows
+ - name: Run tests
+ run: tox -e py312-test-opentelemetry-exporter-otlp-combined -- -ra
+
+ py313-test-opentelemetry-exporter-otlp-combined_windows-latest:
+ name: opentelemetry-exporter-otlp-combined 3.13 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -5647,19 +5139,19 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.9
+ - name: Set up Python 3.13
uses: actions/setup-python@v5
with:
- python-version: "3.9"
+ python-version: "3.13"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-otlp-proto-grpc-oldest -- -ra
+ run: tox -e py313-test-opentelemetry-exporter-otlp-combined -- -ra
- py39-test-opentelemetry-exporter-otlp-proto-grpc-latest_windows-latest:
- name: opentelemetry-exporter-otlp-proto-grpc-latest 3.9 Windows
+ py314-test-opentelemetry-exporter-otlp-combined_windows-latest:
+ name: opentelemetry-exporter-otlp-combined 3.14 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -5668,16 +5160,16 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python 3.9
+ - name: Set up Python 3.14
uses: actions/setup-python@v5
with:
- python-version: "3.9"
+ python-version: "3.14"
- name: Install tox
run: pip install tox-uv
- name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-otlp-proto-grpc-latest -- -ra
+ run: tox -e py314-test-opentelemetry-exporter-otlp-combined -- -ra
py310-test-opentelemetry-exporter-otlp-proto-grpc-oldest_windows-latest:
name: opentelemetry-exporter-otlp-proto-grpc-oldest 3.10 Windows
@@ -5889,27 +5381,6 @@ jobs:
- name: Run tests
run: tox -e py314-test-opentelemetry-exporter-otlp-proto-grpc-latest -- -ra
- py39-test-opentelemetry-exporter-otlp-proto-http_windows-latest:
- name: opentelemetry-exporter-otlp-proto-http 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-otlp-proto-http -- -ra
-
py310-test-opentelemetry-exporter-otlp-proto-http_windows-latest:
name: opentelemetry-exporter-otlp-proto-http 3.10 Windows
runs-on: windows-latest
@@ -6037,7 +5508,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-exporter-otlp-proto-http -- -ra
pypy3-test-opentelemetry-exporter-otlp-proto-http_windows-latest:
- name: opentelemetry-exporter-otlp-proto-http pypy-3.9 Windows
+ name: opentelemetry-exporter-otlp-proto-http pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -6046,10 +5517,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -6057,27 +5528,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-exporter-otlp-proto-http -- -ra
- py39-test-opentelemetry-exporter-prometheus_windows-latest:
- name: opentelemetry-exporter-prometheus 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-prometheus -- -ra
-
py310-test-opentelemetry-exporter-prometheus_windows-latest:
name: opentelemetry-exporter-prometheus 3.10 Windows
runs-on: windows-latest
@@ -6205,7 +5655,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-exporter-prometheus -- -ra
pypy3-test-opentelemetry-exporter-prometheus_windows-latest:
- name: opentelemetry-exporter-prometheus pypy-3.9 Windows
+ name: opentelemetry-exporter-prometheus pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -6214,10 +5664,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -6225,27 +5675,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-exporter-prometheus -- -ra
- py39-test-opentelemetry-exporter-zipkin-combined_windows-latest:
- name: opentelemetry-exporter-zipkin-combined 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-zipkin-combined -- -ra
-
py310-test-opentelemetry-exporter-zipkin-combined_windows-latest:
name: opentelemetry-exporter-zipkin-combined 3.10 Windows
runs-on: windows-latest
@@ -6373,7 +5802,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-exporter-zipkin-combined -- -ra
pypy3-test-opentelemetry-exporter-zipkin-combined_windows-latest:
- name: opentelemetry-exporter-zipkin-combined pypy-3.9 Windows
+ name: opentelemetry-exporter-zipkin-combined pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -6382,10 +5811,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -6393,27 +5822,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-exporter-zipkin-combined -- -ra
- py39-test-opentelemetry-exporter-zipkin-proto-http_windows-latest:
- name: opentelemetry-exporter-zipkin-proto-http 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-zipkin-proto-http -- -ra
-
py310-test-opentelemetry-exporter-zipkin-proto-http_windows-latest:
name: opentelemetry-exporter-zipkin-proto-http 3.10 Windows
runs-on: windows-latest
@@ -6541,7 +5949,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-exporter-zipkin-proto-http -- -ra
pypy3-test-opentelemetry-exporter-zipkin-proto-http_windows-latest:
- name: opentelemetry-exporter-zipkin-proto-http pypy-3.9 Windows
+ name: opentelemetry-exporter-zipkin-proto-http pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -6550,10 +5958,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -6561,27 +5969,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-exporter-zipkin-proto-http -- -ra
- py39-test-opentelemetry-exporter-zipkin-json_windows-latest:
- name: opentelemetry-exporter-zipkin-json 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-exporter-zipkin-json -- -ra
-
py310-test-opentelemetry-exporter-zipkin-json_windows-latest:
name: opentelemetry-exporter-zipkin-json 3.10 Windows
runs-on: windows-latest
@@ -6709,7 +6096,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-exporter-zipkin-json -- -ra
pypy3-test-opentelemetry-exporter-zipkin-json_windows-latest:
- name: opentelemetry-exporter-zipkin-json pypy-3.9 Windows
+ name: opentelemetry-exporter-zipkin-json pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -6718,10 +6105,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -6729,27 +6116,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-exporter-zipkin-json -- -ra
- py39-test-opentelemetry-propagator-b3_windows-latest:
- name: opentelemetry-propagator-b3 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-propagator-b3 -- -ra
-
py310-test-opentelemetry-propagator-b3_windows-latest:
name: opentelemetry-propagator-b3 3.10 Windows
runs-on: windows-latest
@@ -6877,7 +6243,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-propagator-b3 -- -ra
pypy3-test-opentelemetry-propagator-b3_windows-latest:
- name: opentelemetry-propagator-b3 pypy-3.9 Windows
+ name: opentelemetry-propagator-b3 pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -6886,10 +6252,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -6897,27 +6263,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-propagator-b3 -- -ra
- py39-test-opentelemetry-propagator-jaeger_windows-latest:
- name: opentelemetry-propagator-jaeger 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-propagator-jaeger -- -ra
-
py310-test-opentelemetry-propagator-jaeger_windows-latest:
name: opentelemetry-propagator-jaeger 3.10 Windows
runs-on: windows-latest
@@ -7045,7 +6390,7 @@ jobs:
run: tox -e py314t-test-opentelemetry-propagator-jaeger -- -ra
pypy3-test-opentelemetry-propagator-jaeger_windows-latest:
- name: opentelemetry-propagator-jaeger pypy-3.9 Windows
+ name: opentelemetry-propagator-jaeger pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -7054,10 +6399,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
@@ -7065,27 +6410,6 @@ jobs:
- name: Run tests
run: tox -e pypy3-test-opentelemetry-propagator-jaeger -- -ra
- py39-test-opentelemetry-test-utils_windows-latest:
- name: opentelemetry-test-utils 3.9 Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: "3.9"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py39-test-opentelemetry-test-utils -- -ra
-
py310-test-opentelemetry-test-utils_windows-latest:
name: opentelemetry-test-utils 3.10 Windows
runs-on: windows-latest
@@ -7191,29 +6515,8 @@ jobs:
- name: Run tests
run: tox -e py314-test-opentelemetry-test-utils -- -ra
- py314t-test-opentelemetry-test-utils_windows-latest:
- name: opentelemetry-test-utils 3.14t Windows
- runs-on: windows-latest
- timeout-minutes: 30
- steps:
- - name: Configure git to support long filenames
- run: git config --system core.longpaths true
- - name: Checkout repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
-
- - name: Set up Python 3.14t
- uses: actions/setup-python@v5
- with:
- python-version: "3.14t"
-
- - name: Install tox
- run: pip install tox-uv
-
- - name: Run tests
- run: tox -e py314t-test-opentelemetry-test-utils -- -ra
-
pypy3-test-opentelemetry-test-utils_windows-latest:
- name: opentelemetry-test-utils pypy-3.9 Windows
+ name: opentelemetry-test-utils pypy-3.10 Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
@@ -7222,10 +6525,10 @@ jobs:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- - name: Set up Python pypy-3.9
+ - name: Set up Python pypy-3.10
uses: actions/setup-python@v5
with:
- python-version: "pypy-3.9"
+ python-version: "pypy-3.10"
- name: Install tox
run: pip install tox-uv
diff --git a/.pylintrc b/.pylintrc
index df65d4d..a6980c4 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -45,7 +45,7 @@ suggestion-mode=yes
unsafe-load-any-extension=no
# Run python dependant checks considering the baseline version
-py-version=3.9
+py-version=3.10
[MESSAGES CONTROL]
diff --git a/.readthedocs.yml b/.readthedocs.yml
index 9ee8233..b391fa8 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -5,8 +5,8 @@ version: 2
build:
os: "ubuntu-22.04"
tools:
- python: "3.9"
-
+ python: "3.10"
+
sphinx:
configuration: docs/conf.py
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..6161684
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,76 @@
+# OpenTelemetry Python
+
+This file is here to steer AI assisted PRs towards being high quality and valuable contributions
+that do not create excessive maintainer burden.
+
+Monorepo with the core OpenTelemetry Python API, SDK, and related packages.
+
+## General Rules and Guidelines
+
+The OpenTelemetry community has broader guidance on GenAI contributions at
+https://github.com/open-telemetry/community/blob/main/policies/genai.md — please read it before
+contributing.
+
+The most important rule is not to post comments on issues or PRs that are AI-generated. Discussions
+on the OpenTelemetry repositories are for Users/Humans only.
+
+Follow the PR scoping guidance in [CONTRIBUTING.md](CONTRIBUTING.md). Keep AI-assisted PRs tightly
+isolated to the requested change and never include unrelated cleanup or opportunistic improvements
+unless they are strictly necessary for correctness.
+
+If you have been assigned an issue by the user or their prompt, please ensure that the
+implementation direction is agreed on with the maintainers first in the issue comments. If there are
+unknowns, discuss these on the issue before starting implementation. Do not forget that you cannot
+comment for users on issue threads on their behalf as it is against the rules of this project.
+
+## Structure
+
+- `opentelemetry-api/` - the OpenTelemetry API package
+- `opentelemetry-sdk/` - the OpenTelemetry SDK package
+- `opentelemetry-semantic-conventions/` - semantic conventions
+- `opentelemetry-proto/` / `opentelemetry-proto-json/` - protobuf definitions and generated code
+- `exporter/` - exporters (OTLP, Prometheus, Zipkin, etc.)
+- `propagator/` - context propagators (B3, Jaeger)
+- `shim/` - compatibility shims (OpenTracing, OpenCensus)
+
+Each package lives under its own directory with a `pyproject.toml` and `tests/`.
+
+## Commands
+
+```sh
+# Install all packages and dev tools
+uv sync --frozen --all-packages
+
+# Lint (runs ruff via pre-commit)
+uv run tox -e precommit
+
+# Test a specific package
+uv run tox -e py312-test-opentelemetry-sdk
+
+# Lint (pylint) a specific package
+uv run tox -e lint-opentelemetry-sdk
+
+# Type check
+uv run tox -e typecheck
+```
+
+## Guidelines
+
+- Each package has its own `pyproject.toml` with version, dependencies, and entry points.
+- The monorepo uses `uv` workspaces.
+- `tox.ini` defines the test matrix - check it for available test environments.
+- Do not add `type: ignore` comments. If a type error arises, solve it properly or write a follow-up plan to address it in another PR.
+- Whenever applicable, all code changes should have tests that actually validate the changes.
+
+## Commit formatting
+
+We appreciate it if users disclose the use of AI tools when the significant part of a commit is
+taken from a tool without changes. When making a commit this should be disclosed through an
+`Assisted-by:` commit message trailer.
+
+Examples:
+
+```
+Assisted-by: ChatGPT 5.2
+Assisted-by: Claude Opus 4.6
+```
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac9aab0..045e251 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+<!--
+Do *NOT* add changelog entries here!
+
+This changelog is managed by towncrier and is compiled at release time.
+
+See https://github.com/open-telemetry/opentelemetry-python/blob/main/CONTRIBUTING.md#changelog for details.
+-->
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
@@ -10,8 +18,151 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
> [!IMPORTANT]
> We are working on stabilizing the Log signal that would require making deprecations and breaking changes. We will try to reduce the releases that may require an update to your code, especially for instrumentations or for sdk developers.
+<!-- changelog start -->
+
+## Version 1.42.1/0.63b1 (2026-05-21)
+
+### Fixed
+
+- Preserve the random trace ID flag when creating child spans instead of always
+ setting the random trace id bit depending on the available trace id
+ generator.
+ ([#5241](https://github.com/open-telemetry/opentelemetry-python/pull/5241))
+
+## Version 1.42.0/0.63b0 (2026-05-19)
+
+### Added
+
+- `opentelemetry-api`, `opentelemetry-sdk`: add support for 'random-trace-id'
+ flags in W3C traceparent header trace flags. Implementations of `IdGenerator`
+ that do randomly generate the 56 least significant bits, should also
+ implement a `is_trace_id_random` methods that returns `True`.
+ ([#4854](https://github.com/open-telemetry/opentelemetry-python/pull/4854))
+- logs: add exception support to Logger emit and LogRecord attributes
+ ([#4908](https://github.com/open-telemetry/opentelemetry-python/pull/4908))
+- `opentelemetry-exporter-otlp-proto-grpc`: make retryable gRPC error codes
+ configurable for gRPC exporters
+ ([#4917](https://github.com/open-telemetry/opentelemetry-python/pull/4917))
+- `opentelemetry-sdk`: Add `create_logger_provider`/`configure_logger_provider`
+ to declarative file configuration, enabling LoggerProvider instantiation from
+ config files without reading env vars
+ ([#4990](https://github.com/open-telemetry/opentelemetry-python/pull/4990))
+- `opentelemetry-exporter-otlp-json-common`: add
+ 'opentelemetry-exporter-otlp-json-common' package for OTLP JSON exporters
+ ([#4996](https://github.com/open-telemetry/opentelemetry-python/pull/4996))
+- `opentelemetry-sdk`: Add `service` resource detector support to declarative
+ file configuration via `detection_development.detectors[].service`
+ ([#5003](https://github.com/open-telemetry/opentelemetry-python/pull/5003))
+- `opentelemetry-docker-tests`: add docker-tests coverage of
+ `opentelemetry-exporter-otlp-proto-grpc` and
+ `opentelemetry-exporter-otlp-proto-http` metrics export
+ ([#5030](https://github.com/open-telemetry/opentelemetry-python/pull/5030))
+- Add `registry` keyword argument to `PrometheusMetricReader` to allow passing
+ a custom Prometheus registry
+ ([#5055](https://github.com/open-telemetry/opentelemetry-python/pull/5055))
+- Add WeaverLiveCheck test util
+ ([#5088](https://github.com/open-telemetry/opentelemetry-python/pull/5088))
+- `opentelemetry-sdk`: add `load_entry_point` shared utility to declarative
+ file configuration for loading plugins via entry points; refactor propagator
+ loading to use it
+ ([#5093](https://github.com/open-telemetry/opentelemetry-python/pull/5093))
+- `opentelemetry-sdk`: add sampler plugin loading to declarative file
+ configuration via the `opentelemetry_sampler` entry point group, matching the
+ spec's PluginComponentProvider mechanism
+ ([#5095](https://github.com/open-telemetry/opentelemetry-python/pull/5095))
+- `opentelemetry-sdk`: add propagator plugin loading to declarative file
+ configuration via the `opentelemetry_propagator` entry point group, matching
+ the spec's PluginComponentProvider mechanism
+ ([#5098](https://github.com/open-telemetry/opentelemetry-python/pull/5098))
+- `opentelemetry-sdk`: add exporter plugin loading to declarative file
+ configuration for all three signals (traces, metrics, logs) via the
+ `opentelemetry_*_exporter` entry point groups, matching the spec's
+ PluginComponentProvider mechanism
+ ([#5128](https://github.com/open-telemetry/opentelemetry-python/pull/5128))
+- `opentelemetry-sdk`: add generic resource detector plugin loading to
+ declarative file configuration via the `opentelemetry_resource_detector`
+ entry point group, matching the spec's PluginComponentProvider mechanism
+ ([#5129](https://github.com/open-telemetry/opentelemetry-python/pull/5129))
+- `opentelemetry-sdk`: add `additional_properties` support to generated config
+ models via custom `datamodel-codegen` template, enabling plugin/custom
+ component names to flow through typed dataclasses
+ ([#5131](https://github.com/open-telemetry/opentelemetry-python/pull/5131))
+- Add ability to selectively enable exporting of SDK internal metrics with the
+ `OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED` environment variable.
+ ([#5151](https://github.com/open-telemetry/opentelemetry-python/pull/5151))
+
+### Changed
+
+- `opentelemetry-semantic-conventions`: use `X | Y` union annotation
+ ([#5096](https://github.com/open-telemetry/opentelemetry-python/pull/5096))
+- `opentelemetry-api`: update `EnvironmentGetter` and `EnvironmentSetter` to
+ use normalized environment variable names
+ ([#5119](https://github.com/open-telemetry/opentelemetry-python/pull/5119))
+- Apply fixes for `UP` ruff rule
+ ([#5133](https://github.com/open-telemetry/opentelemetry-python/pull/5133))
+- `opentelemetry-sdk`: only load entrypoints for resource detectors if they are
+ configured via `OTEL_EXPERIMENTAL_RESOURCE_DETECTORS`
+ ([#5145](https://github.com/open-telemetry/opentelemetry-python/pull/5145))
+- ci: wait for tracecontext server readiness instead of a fixed sleep in
+ `scripts/tracecontext-integration-test.sh`
+ ([#5149](https://github.com/open-telemetry/opentelemetry-python/pull/5149))
+- Switch to SPDX license headers and add CI enforcement
+ ([#5177](https://github.com/open-telemetry/opentelemetry-python/pull/5177))
+- `opentelemetry-semantic-conventions`: Bump semantic conventions to 1.41.1,
+ this changes the metrics name of `K8S_CONTAINER_CPU_LIMIT_UTILIZATION` and
+ `K8S_CONTAINER_CPU_REQUEST_UTILIZATION`.
+ ([#5200](https://github.com/open-telemetry/opentelemetry-python/pull/5200))
+
+### Removed
+
+- `opentelemetry-api`: remove third-party importlib-metadata in favor of
+ standard library since Python >= 3.10 is now required
+ ([#3234](https://github.com/open-telemetry/opentelemetry-python/pull/3234))
+- Drop Python 3.9 support
+ ([#5076](https://github.com/open-telemetry/opentelemetry-python/pull/5076))
+
+### Fixed
+
+- `opentelemetry-sdk`: Allow declarative OTLP HTTP exporters to map
+ `compression: deflate` instead of rejecting it as unsupported.
+ ([#5075](https://github.com/open-telemetry/opentelemetry-python/pull/5075))
+- Fix incorrect code example in `create_tracer()` docstring
+ ([#5077](https://github.com/open-telemetry/opentelemetry-python/pull/5077))
+- `opentelemetry-sdk`: Fix `ProcessResourceDetector` to use `sys.orig_argv` so
+ that `process.command`, `process.command_line`, and `process.command_args`
+ reflect the original invocation for `python -m <module>` runs (where
+ `sys.argv[0]` is rewritten to the module path)
+ ([#5083](https://github.com/open-telemetry/opentelemetry-python/pull/5083))
+- `opentelemetry-sdk`: fix YAML structure injection via environment variable
+ substitution in declarative file configuration; values containing newlines
+ are now emitted as quoted YAML scalars per spec requirement
+ ([#5091](https://github.com/open-telemetry/opentelemetry-python/pull/5091))
+- `opentelemetry-sdk`: Fix mutable attributes reference in metrics, attributes
+ passed to instrument `add`/`record` are now copied so that subsequent
+ mutations do not affect recorded data points
+ ([#5106](https://github.com/open-telemetry/opentelemetry-python/pull/5106))
+- `opentelemetry-sdk`: make resource detector ordering deterministic
+ ([#5120](https://github.com/open-telemetry/opentelemetry-python/pull/5120))
+- Fix incorrect type annotation on `detectors` parameter of
+ `get_aggregated_resources`
+ ([#5135](https://github.com/open-telemetry/opentelemetry-python/pull/5135))
+- `opentelemetry-api`: Enforce W3C Baggage size limits on outbound propagation
+ in `W3CBaggagePropagator.inject()`. Previously only inbound extraction
+ enforced limits; now inject also caps entries at 180, individual pairs at
+ 4096 bytes, and total header at 8192 bytes per the W3C Baggage spec. The
+ extract path max_pairs limit now counts all size-valid entries rather than
+ only successfully parsed ones.
+ ([#5163](https://github.com/open-telemetry/opentelemetry-python/pull/5163))
+- `opentelemetry-sdk`: fix multi-processor `force_flush` skipping remaining
+ processors when one returns `None`
+ ([#5179](https://github.com/open-telemetry/opentelemetry-python/pull/5179))
+- `opentelemetry-test-utils`: fix weaver live check hanging when weaver log
+ output fills the pipe buffer
+ ([#5208](https://github.com/open-telemetry/opentelemetry-python/pull/5208))
+
## Version 1.41.0/0.62b0 (2026-04-09)
+
- `opentelemetry-sdk`: Add `host` resource detector support to declarative file configuration via `detection_development.detectors[].host`
([#5002](https://github.com/open-telemetry/opentelemetry-python/pull/5002))
- `opentelemetry-sdk`: Add `container` resource detector support to declarative file configuration via `detection_development.detectors[].container`, using entry point loading of the `opentelemetry-resource-detector-containerid` contrib package
@@ -49,6 +200,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#5012](https://github.com/open-telemetry/opentelemetry-python/pull/5012))
- `opentelemetry-sdk`: upgrade vendored OTel configuration schema from v1.0.0-rc.3 to v1.0.0
([#4965](https://github.com/open-telemetry/opentelemetry-python/pull/4965))
+- `opentelemetry-sdk`: implement exporter metrics
+ ([#4976](https://github.com/open-telemetry/opentelemetry-python/pull/4976))
- improve check-links ci job
([#4978](https://github.com/open-telemetry/opentelemetry-python/pull/4978))
- Resolve some Pyright type errors in Span/ReadableSpan and utility stubs
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..8b7cbf4
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1 @@
+See [AGENTS.md](AGENTS.md).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 27703db..f44e180 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -13,6 +13,8 @@ Before you can contribute, you will need to sign the [Contributor License Agreem
Please also read the [OpenTelemetry Contributor Guide](https://github.com/open-telemetry/community/blob/main/guides/contributor/README.md).
+If you are using AI agents to assist with contributions, please read [AGENTS.md](AGENTS.md) for guidance on how to use them responsibly in this project.
+
# Find your right repo
This is the main repo for OpenTelemetry Python. Nevertheless, there are other repos that are related to this project.
@@ -71,6 +73,37 @@ You can run `tox` with the following arguments:
- `tox -e tracecontext` to run integration tests for tracecontext.
- `tox -e precommit` to run all `pre-commit` actions
+### Changelog
+
+This project uses [towncrier](https://towncrier.readthedocs.io/) to manage the changelog. Instead of editing `CHANGELOG.md` directly, each PR should include a changelog fragment file in the `.changelog/` directory.
+
+**Creating a changelog fragment:**
+
+Create a file named `.changelog/<PR_NUMBER>.<TYPE>` where `TYPE` is one of: `added`, `changed`, `deprecated`, `removed`, `fixed`.
+
+The file should contain a one-line description of the change. For example, `.changelog/1234.added`:
+
+```
+`opentelemetry-sdk`: add support for new feature
+```
+
+**Writing a good changelog entry:**
+
+- Write in imperative tone, as if completing the phrase "This change will..."
+- Keep entries concise — ideally under 80 characters
+- Prefix with the affected package name when applicable (e.g. `` `opentelemetry-sdk`: ... ``)
+- Don't include the PR number — towncrier adds it automatically
+
+**Preview the changelog:**
+
+```console
+towncrier build --draft --version Unreleased
+```
+
+The CI will verify that a changelog fragment exists and that `CHANGELOG.md` is not directly modified.
+
+If your change does not need a changelog entry, add the "Skip Changelog" label to the PR.
+
`ruff check` and `ruff format` are executed when `tox -e ruff` is run. We strongly recommend you to configure [pre-commit](https://pre-commit.com/) locally to run `ruff` and `rstcheck` automatically before each commit by installing it as git hooks. You just need to [install pre-commit](https://pre-commit.com/#install) in your environment:
```console
@@ -144,7 +177,7 @@ CONTRIB_REPO_SHA=dde62cebffe519c35875af6d06fae053b3be65ec tox
```
The continuation integration overrides that environment variable with as per the configuration
-[here](https://github.com/open-telemetry/opentelemetry-python/blob/main/.github/workflows/test_0.yml#L14).
+[here](https://github.com/open-telemetry/opentelemetry-python/blob/main/.github/workflows/test.yml#L14).
### Benchmarks
@@ -232,7 +265,14 @@ git commit
git push fork feature
```
-Open a pull request against the main `opentelemetry-python` repo.
+Open a pull request (PR) against the main `opentelemetry-python` repo.
+
+A descriptive PR title will help the community better triage and review your changes. Make sure to prefix with the name(s) of the package/subdirectory/domain that your PR updates. Following any of these examples will help:
+
+* "opentelemetry-sdk: make test_force_flush_late_by_timeout less flaky on pypy/windows"
+* "opentelemetry-exporter-otlp-proto-http: enable typechecking"
+* "docs: replace TODO placeholders with API and SDK overview descriptions"
+* "feat(config): Add TracerProvider support for declarative config"
Pull requests are also tested for their compatibility with packages distributed
by OpenTelemetry in the [OpenTelemetry Python Contrib Repository](https://github.com/open-telemetry/opentelemetry-python.git).
@@ -329,6 +369,16 @@ automatically load as options for the `opentelemetry-instrument` command.
## Style Guide
+* All Python files must include the following SPDX license header as the first
+ two lines (or immediately after a shebang line):
+
+ ```python
+ # Copyright The OpenTelemetry Authors
+ # SPDX-License-Identifier: Apache-2.0
+ ```
+
+ This is enforced by CI via `tox -e lint-license-header-check`.
+
* docstrings should adhere to the [Google Python Style
Guide](http://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings)
as specified with the [napoleon
diff --git a/README.md b/README.md
index a42ccb5..6708e7a 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# OpenTelemetry Python
[![Slack](https://img.shields.io/badge/slack-@cncf/otel/python-brightgreen.svg?logo=slack)](https://cloud-native.slack.com/archives/C01PD4HUVBL)
[![Build Status](https://github.com/open-telemetry/opentelemetry-python/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/open-telemetry/opentelemetry-python/actions/workflows/ci.yml)
-[![Minimum Python Version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
+[![Minimum Python Version](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![Release](https://img.shields.io/github/v/release/open-telemetry/opentelemetry-python?include_prereleases&style=)](https://github.com/open-telemetry/opentelemetry-python/releases/)
[![Read the Docs](https://readthedocs.org/projects/opentelemetry-python/badge/?version=latest)](https://opentelemetry-python.readthedocs.io/en/latest/)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/11060/badge)](https://www.bestpractices.dev/projects/11060)
@@ -25,11 +25,11 @@ Project versioning information and stability guarantees can be found [here](./ra
## Getting started
-You can find the getting started guide for OpenTelemetry Python [here](https://opentelemetry.io/docs/instrumentation/python/getting-started/).
+You can find the getting started guide for OpenTelemetry Python [here](https://opentelemetry.io/docs/languages/python/getting-started/).
If you are looking for **examples** on how to use the OpenTelemetry API to
instrument your code manually, or how to set up the OpenTelemetry
-Python SDK, see https://opentelemetry.io/docs/instrumentation/python/manual/.
+Python SDK, see the [documentation](https://opentelemetry.io/docs/languages/python/instrumentation/).
## Python Version Support
@@ -100,6 +100,8 @@ We meet weekly on Thursdays at 9AM PST. The meeting is subject to change dependi
Meeting notes are available as a public [Google doc](https://docs.google.com/document/d/18w8zOBm_mbety0OqlPwxc7dvnfu641EgmrO4AdJef0U/edit?tab=t.0).
+The meeting is open for all to join. We invite everyone to join our meeting, regardless of your experience level. Whether you're a seasoned OpenTelemetry developer, just starting your journey, or simply curious about the work we do, you're more than welcome to participate!
+
### Maintainers
- [Aaron Abbott](https://github.com/aabmass), Google
@@ -113,13 +115,10 @@ For more information about the maintainer role, see the [community repository](h
- [Dylan Russell](https://github.com/dylanrussell), Google
- [Emídio Neto](https://github.com/emdneto), Independent
- [Héctor Hernández](https://github.com/hectorhdzg), Microsoft
-- [Jeremy Voss](https://github.com/jeremydvoss), Microsoft
- [Liudmila Molkova](https://github.com/lmolkova), Grafana Labs
- [Lukas Hering](https://github.com/herin049), Oracle
- [Mike Goldsmith](https://github.com/MikeGoldsmith), Honeycomb
-- [Owais Lone](https://github.com/owais), Splunk
- [Pablo Collins](https://github.com/pmcollins), Splunk
-- [Shalev Roda](https://github.com/shalevr), Cisco
- [Srikanth Chekuri](https://github.com/srikanthccv), signoz.io
- [Tammy Baylis](https://github.com/tammy-baylis-swi), SolarWinds
@@ -133,12 +132,15 @@ For more information about the approver role, see the [community repository](htt
- [Chris Kleinknecht](https://github.com/c24t), Maintainer
- [Christian Neumüller](https://github.com/Oberon00), Approver
- [Diego Hurtado](https://github.com/ocelotl), Maintainer
+- [Jeremy Voss](https://github.com/jeremydvoss), Approver
- [Mauricio Vásquez](https://github.com/mauriciovasquezbernal), Approver
- [Nathaniel Ruiz Nowell](https://github.com/NathanielRN), Approver
- [Nikolay Sokolik](https://github.com/oxeye-nikolay), Approver
- [Owais Lone](https://github.com/owais), Maintainer
+- [Owais Lone](https://github.com/owais), Approver
- [Reiley Yang](https://github.com/reyang), Maintainer
- [Sanket Mehta](https://github.com/sanketmehta28), Approver
+- [Shalev Roda](https://github.com/shalevr), Approver
- [Srikanth Chekuri](https://github.com/srikanthccv), Maintainer
- [Tahir H. Butt](https://github.com/majorgreys), Approver
- [Yusuke Tsutsumi](https://github.com/toumorokoshi), Maintainer
diff --git a/RELEASING.md b/RELEASING.md
index dc0b73b..dc5cb33 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -17,7 +17,11 @@
## Preparing a new patch release
-* Backport pull request(s) to the release branch.
+### Backporting
+
+Creating manual backports of pull request(s) requires the `backport` label to be added in order to have a green CI.
+
+To use the workflow to backport pull request(s) to the release branch:
* Run the [Backport workflow](https://github.com/open-telemetry/opentelemetry-python/actions/workflows/backport.yml).
* Press the "Run workflow" button, then select the release branch from the dropdown list,
e.g. `release/v1.9.x`, then enter the pull request number that you want to backport,
@@ -25,8 +29,9 @@
* Add the label `backport` to the generated pull request.
* In case label automation doesn't work, just close and reopen the PR so that the workflow will take into account the label automation we have in place.
* Review and merge the backport pull request that it generates.
-* Merge a pull request to the release branch updating the `CHANGELOG.md`.
- * The heading for the unreleased entries should be `## Unreleased`.
+
+### Preparing a patch release
+
* Run the [Prepare patch release workflow](https://github.com/open-telemetry/opentelemetry-python/actions/workflows/prepare-patch-release.yml).
* Press the "Run workflow" button, then select the release branch from the dropdown list,
e.g. `release/v1.9.x`, and click the "Run workflow" button below that.
diff --git a/codegen/opentelemetry-codegen-json/pyproject.toml b/codegen/opentelemetry-codegen-json/pyproject.toml
index 627583e..7b11ba4 100644
--- a/codegen/opentelemetry-codegen-json/pyproject.toml
+++ b/codegen/opentelemetry-codegen-json/pyproject.toml
@@ -4,11 +4,11 @@ build-backend = "hatchling.build"
[project]
name = "opentelemetry-codegen-json"
-dynamic = ["version"]
+version = "0.0.0" # Not published to PyPI
description = "Protobuf plugin to generate JSON serializers and deserializers for OpenTelemetry protobuf messages"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -18,7 +18,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
diff --git a/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/generator.py b/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/generator.py
index 5893dbd..f026b89 100644
--- a/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/generator.py
+++ b/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/generator.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=no-member,invalid-name,too-many-lines
@@ -18,9 +7,9 @@ from __future__ import annotations
import logging
from collections import defaultdict
-from collections.abc import Iterable
+from collections.abc import Callable, Iterable
from pathlib import Path
-from typing import Callable, Final, Optional, Set
+from typing import Final
from google.protobuf import descriptor_pb2 as descriptor
from google.protobuf.compiler import plugin_pb2 as plugin
@@ -34,7 +23,6 @@ from opentelemetry.codegen.json.types import (
is_int64_type,
to_json_field_name,
)
-from opentelemetry.codegen.json.version import __version__ as GENERATOR_VERSION
from opentelemetry.codegen.json.writer import CodeWriter
_logger = logging.getLogger(__name__)
@@ -136,7 +124,7 @@ class OtlpJsonGenerator:
msg_desc: descriptor.DescriptorProto,
package: str,
file_name: str,
- parent_path: Optional[str],
+ parent_path: str | None,
) -> None:
"""
Recursively index a message and its nested types.
@@ -349,7 +337,6 @@ class OtlpJsonGenerator:
"builtins",
"dataclasses",
"functools",
- "sys",
"typing",
]
if include_enum:
@@ -360,14 +347,10 @@ class OtlpJsonGenerator:
writer.blank_line()
- # TODO: Remove after dropping support for Python 3.9
- with writer.if_("sys.version_info >= (3, 10)"):
- writer.assignment(
- "_dataclass",
- "functools.partial(dataclasses.dataclass, slots=True)",
- )
- with writer.else_():
- writer.assignment("_dataclass", "dataclasses.dataclass")
+ writer.assignment(
+ "_dataclass",
+ "functools.partial(dataclasses.dataclass, slots=True)",
+ )
writer.blank_line()
# Collect all imports needed
@@ -381,7 +364,7 @@ class OtlpJsonGenerator:
writer.blank_line()
writer.blank_line()
- def _collect_imports(self, proto_file: str) -> Set[str]:
+ def _collect_imports(self, proto_file: str) -> set[str]:
"""
Collect all import statements needed for cross file references.
@@ -437,7 +420,7 @@ class OtlpJsonGenerator:
writer: CodeWriter,
proto_file: str,
msg_desc: descriptor.DescriptorProto,
- parent_path: Optional[str] = None,
+ parent_path: str | None = None,
) -> None:
"""
Generate a complete dataclass for a protobuf message.
@@ -967,7 +950,7 @@ class OtlpJsonGenerator:
@classmethod
def _get_field_default(
cls, field_desc: descriptor.FieldDescriptorProto
- ) -> Optional[str]:
+ ) -> str | None:
"""
Get the default value for a field.
@@ -1062,9 +1045,7 @@ def generate_code(
Returns:
Dictionary mapping output file paths to generated code
"""
- generator = OtlpJsonGenerator(
- request, package_transform, version=GENERATOR_VERSION
- )
+ generator = OtlpJsonGenerator(request, package_transform, version="0.0.0")
return generator.generate_all()
diff --git a/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/plugin.py b/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/plugin.py
index ce9e8b8..1662500 100644
--- a/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/plugin.py
+++ b/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/plugin.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=no-member
@@ -22,7 +11,6 @@ from contextlib import contextmanager
from google.protobuf.compiler import plugin_pb2 as plugin
from opentelemetry.codegen.json.generator import generate_plugin_response
-from opentelemetry.codegen.json.version import __version__
_logger = logging.getLogger(__name__)
@@ -35,7 +23,7 @@ def code_generation() -> Iterator[
Context manager for handling the code generation process.
"""
if len(sys.argv) > 1 and sys.argv[1] in ("-V", "--version"):
- print("opentelemetry-codegen-json " + __version__)
+ print("opentelemetry-codegen-json 0.0.0")
sys.exit(0)
data = sys.stdin.buffer.read()
diff --git a/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/runtime/json_codec.py b/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/runtime/json_codec.py
index da1f40b..0a6c7c7 100644
--- a/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/runtime/json_codec.py
+++ b/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/runtime/json_codec.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
@@ -49,14 +38,14 @@ class JsonMessage(abc.ABC):
return json.dumps(self.to_dict())
@classmethod
- def from_json(cls: type[M], data: typing.Union[str, bytes]) -> M:
+ def from_json(cls: type[M], data: str | bytes) -> M:
"""
Deserialize from a JSON string or bytes.
"""
return cls.from_dict(json.loads(data))
-def encode_hex(value: typing.Optional[bytes]) -> str:
+def encode_hex(value: bytes | None) -> str:
"""
Encode bytes as hex string.
@@ -68,7 +57,7 @@ def encode_hex(value: typing.Optional[bytes]) -> str:
return value.hex() if value else ""
-def encode_base64(value: typing.Optional[bytes]) -> str:
+def encode_base64(value: bytes | None) -> str:
"""
Encode bytes as base64 string.
Standard Proto3 JSON mapping for bytes.
@@ -94,7 +83,7 @@ def encode_int64(value: int) -> str:
return str(value)
-def encode_float(value: float) -> typing.Union[float, str]:
+def encode_float(value: float) -> float | str:
"""
Encode float/double values.
@@ -111,7 +100,7 @@ def encode_float(value: float) -> typing.Union[float, str]:
def encode_repeated(
- values: typing.Optional[list[T]],
+ values: list[T] | None,
map_fn: typing.Callable[[T], typing.Any],
) -> list[typing.Any]:
"""
@@ -126,7 +115,7 @@ def encode_repeated(
return [map_fn(v) for v in values] if values else []
-def decode_hex(value: typing.Optional[str], field_name: str) -> bytes:
+def decode_hex(value: str | None, field_name: str) -> bytes:
"""
Decode hex string to bytes.
@@ -147,7 +136,7 @@ def decode_hex(value: typing.Optional[str], field_name: str) -> bytes:
) from None
-def decode_base64(value: typing.Optional[str], field_name: str) -> bytes:
+def decode_base64(value: str | None, field_name: str) -> bytes:
"""
Decode base64 string to bytes.
@@ -168,9 +157,7 @@ def decode_base64(value: typing.Optional[str], field_name: str) -> bytes:
) from None
-def decode_int64(
- value: typing.Optional[typing.Union[int, str]], field_name: str
-) -> int:
+def decode_int64(value: int | str | None, field_name: str) -> int:
"""
Parse int64 from number or string.
@@ -191,9 +178,7 @@ def decode_int64(
) from None
-def decode_float(
- value: typing.Optional[typing.Union[float, int, str]], field_name: str
-) -> float:
+def decode_float(value: float | int | str | None, field_name: str) -> float:
"""
Parse float/double from number or string, handling special values.
@@ -221,7 +206,7 @@ def decode_float(
def decode_repeated(
- values: typing.Optional[list[typing.Any]],
+ values: list[typing.Any] | None,
item_parser: typing.Callable[[typing.Any], T],
field_name: str,
) -> list[T]:
@@ -243,7 +228,7 @@ def decode_repeated(
def validate_type(
value: typing.Any,
- expected_types: typing.Union[type, tuple[type, ...]],
+ expected_types: type | tuple[type, ...],
field_name: str,
) -> None:
"""
diff --git a/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/types.py b/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/types.py
index 5424d27..bc2da8d 100644
--- a/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/types.py
+++ b/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/types.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=no-member
diff --git a/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/version/__init__.py b/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/version/__init__.py
deleted file mode 100644
index ed89ddd..0000000
--- a/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/version/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright The OpenTelemetry Authors
-#
-# 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.
-
-__version__ = "0.62b0.dev"
diff --git a/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/writer.py b/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/writer.py
index 687de17..310aa6c 100644
--- a/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/writer.py
+++ b/codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/writer.py
@@ -1,22 +1,11 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
-from collections.abc import Iterable
+from collections.abc import Generator, Iterable
from contextlib import contextmanager
-from typing import Any, Generator, Optional, Union
+from typing import Any
# pylint: disable-next=too-many-public-methods
@@ -72,7 +61,7 @@ class CodeWriter:
self.writeln(line)
return self
- def comment(self, content: Union[str, Iterable[str]]) -> CodeWriter:
+ def comment(self, content: str | Iterable[str]) -> CodeWriter:
"""
Writes a comment line or block. If content is a string, it writes a single comment line.
@@ -88,7 +77,7 @@ class CodeWriter:
self.writeln(f"# {line}" if line else "#")
return self
- def docstring(self, content: Union[str, Iterable[str]]) -> CodeWriter:
+ def docstring(self, content: str | Iterable[str]) -> CodeWriter:
"""
Writes a docstring. If content is a string, it writes a single-line docstring. If content is an iterable of strings, it writes a multi-line docstring.
@@ -139,8 +128,8 @@ class CodeWriter:
def class_(
self,
name: str,
- bases: Optional[Iterable[str]] = None,
- decorators: Optional[Iterable[str]] = None,
+ bases: Iterable[str] | None = None,
+ decorators: Iterable[str] | None = None,
) -> Generator[CodeWriter, None, None]:
"""
Generate a class definition with optional base classes and decorators.
@@ -164,8 +153,8 @@ class CodeWriter:
def dataclass(
self,
name: str,
- bases: Optional[Iterable[str]] = None,
- decorators: Optional[Iterable[str]] = None,
+ bases: Iterable[str] | None = None,
+ decorators: Iterable[str] | None = None,
frozen: bool = False,
slots: bool = False,
decorator_name: str = "dataclasses.dataclass",
@@ -212,8 +201,8 @@ class CodeWriter:
self,
name: str,
enum_type: str = "enum.Enum",
- bases: Optional[Iterable[str]] = None,
- decorators: Optional[Iterable[str]] = None,
+ bases: Iterable[str] | None = None,
+ decorators: Iterable[str] | None = None,
) -> Generator[CodeWriter, None, None]:
"""
Generate an enum definition with optional base classes and decorators.
@@ -243,7 +232,7 @@ class CodeWriter:
name: str,
type_hint: str,
default: Any = None,
- default_factory: Optional[str] = None,
+ default_factory: str | None = None,
) -> CodeWriter:
"""
Write a dataclass field with optional default value or default factory.
@@ -279,9 +268,9 @@ class CodeWriter:
def function(
self,
name: str,
- params: Union[Iterable[str], str],
- decorators: Optional[Iterable[str]] = None,
- return_type: Optional[str] = None,
+ params: Iterable[str] | str,
+ decorators: Iterable[str] | None = None,
+ return_type: str | None = None,
) -> Generator[CodeWriter, None, None]:
"""
Create a function definition with optional decorators and return type.
@@ -307,9 +296,9 @@ class CodeWriter:
def method(
self,
name: str,
- params: Union[Iterable[str], str],
- decorators: Optional[Iterable[str]] = None,
- return_type: Optional[str] = None,
+ params: Iterable[str] | str,
+ decorators: Iterable[str] | None = None,
+ return_type: str | None = None,
) -> Generator[CodeWriter, None, None]:
"""
Create a method definition within a class with optional decorators and return type.
@@ -386,7 +375,7 @@ class CodeWriter:
yield self
def assignment(
- self, var: str, value: str, type_hint: Optional[str] = None
+ self, var: str, value: str, type_hint: str | None = None
) -> CodeWriter:
"""
Write a variable assignment with optional type hint
@@ -404,7 +393,7 @@ class CodeWriter:
self.writeln(f"{var} = {value}")
return self
- def return_(self, value: Optional[str] = None) -> CodeWriter:
+ def return_(self, value: str | None = None) -> CodeWriter:
"""
Write a return statement with an optional return value
diff --git a/codegen/opentelemetry-codegen-json/test-requirements.txt b/codegen/opentelemetry-codegen-json/test-requirements.txt
index 4353bc5..54a16dc 100644
--- a/codegen/opentelemetry-codegen-json/test-requirements.txt
+++ b/codegen/opentelemetry-codegen-json/test-requirements.txt
@@ -1,4 +1,3 @@
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.5.0
@@ -6,7 +5,6 @@ protobuf==6.31.1
pytest==7.4.4
tomli==2.0.1
typing_extensions==4.12.0
-zipp==3.19.2
grpcio==1.78.0
grpcio-tools==1.78.0
-e codegen/opentelemetry-codegen-json
diff --git a/codegen/opentelemetry-codegen-json/tests/conftest.py b/codegen/opentelemetry-codegen-json/tests/conftest.py
index d349025..888ff11 100644
--- a/codegen/opentelemetry-codegen-json/tests/conftest.py
+++ b/codegen/opentelemetry-codegen-json/tests/conftest.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=redefined-outer-name
diff --git a/codegen/opentelemetry-codegen-json/tests/test_json_codec.py b/codegen/opentelemetry-codegen-json/tests/test_json_codec.py
index 3cfa07b..aa24f83 100644
--- a/codegen/opentelemetry-codegen-json/tests/test_json_codec.py
+++ b/codegen/opentelemetry-codegen-json/tests/test_json_codec.py
@@ -1,19 +1,7 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import math
-from typing import Optional, Union
import pytest # type: ignore
@@ -40,7 +28,7 @@ from opentelemetry.codegen.json.runtime.json_codec import (
(None, ""),
],
)
-def test_encode_hex(value: Optional[bytes], expected: str) -> None:
+def test_encode_hex(value: bytes | None, expected: str) -> None:
assert encode_hex(value) == expected
@@ -52,7 +40,7 @@ def test_encode_hex(value: Optional[bytes], expected: str) -> None:
(None, b""),
],
)
-def test_decode_hex(value: Optional[str], expected: bytes) -> None:
+def test_decode_hex(value: str | None, expected: bytes) -> None:
assert decode_hex(value, "field") == expected
@@ -71,7 +59,7 @@ def test_decode_hex_errors() -> None:
(None, ""),
],
)
-def test_encode_base64(value: Optional[bytes], expected: str) -> None:
+def test_encode_base64(value: bytes | None, expected: str) -> None:
assert encode_base64(value) == expected
@@ -83,7 +71,7 @@ def test_encode_base64(value: Optional[bytes], expected: str) -> None:
(None, b""),
],
)
-def test_decode_base64(value: Optional[str], expected: bytes) -> None:
+def test_decode_base64(value: str | None, expected: bytes) -> None:
assert decode_base64(value, "field") == expected
@@ -112,7 +100,7 @@ def test_encode_int64(value: int, expected: str) -> None:
(None, 0),
],
)
-def test_decode_int64(value: Optional[Union[int, str]], expected: int) -> None:
+def test_decode_int64(value: int | str | None, expected: int) -> None:
assert decode_int64(value, "field") == expected
@@ -132,7 +120,7 @@ def test_decode_int64_errors() -> None:
(float("-inf"), "-Infinity"),
],
)
-def test_encode_float(value: float, expected: Union[float, str]) -> None:
+def test_encode_float(value: float, expected: float | str) -> None:
result = encode_float(value)
if isinstance(expected, float) and math.isnan(expected):
assert math.isnan(result) # type: ignore
@@ -153,7 +141,7 @@ def test_encode_float(value: float, expected: Union[float, str]) -> None:
],
)
def test_decode_float(
- value: Optional[Union[float, int, str]], expected: float
+ value: float | int | str | None, expected: float
) -> None:
result = decode_float(value, "field")
if math.isnan(expected):
diff --git a/codegen/opentelemetry-codegen-json/tests/test_protobuf_compatibility.py b/codegen/opentelemetry-codegen-json/tests/test_protobuf_compatibility.py
index 19f30be..16c1852 100644
--- a/codegen/opentelemetry-codegen-json/tests/test_protobuf_compatibility.py
+++ b/codegen/opentelemetry-codegen-json/tests/test_protobuf_compatibility.py
@@ -1,22 +1,11 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: skip-file
# ruff: noqa: PLC0415
import base64
-from typing import Any, Type
+from typing import Any
import pytest # type: ignore
from google.protobuf import json_format
@@ -39,7 +28,7 @@ def normalize_otlp_json(data: Any) -> Any:
@pytest.fixture
-def test_msg_classes() -> tuple[Type[Any], Type[Any], Type[Any]]:
+def test_msg_classes() -> tuple[type[Any], type[Any], type[Any]]:
from otel_test_json.test.v1.test import ( # type: ignore
SubMessage as JSONSubMessage, # type: ignore
)
@@ -54,7 +43,7 @@ def test_msg_classes() -> tuple[Type[Any], Type[Any], Type[Any]]:
@pytest.fixture
-def numeric_msg_classes() -> tuple[Type[Any], Type[Any]]:
+def numeric_msg_classes() -> tuple[type[Any], type[Any]]:
from otel_test_json.test.v1.complex import ( # type: ignore
NumericTest as JSONNumericTest, # type: ignore
)
@@ -66,7 +55,7 @@ def numeric_msg_classes() -> tuple[Type[Any], Type[Any]]:
@pytest.fixture
-def oneof_msg_classes() -> tuple[Type[Any], Type[Any], Type[Any]]:
+def oneof_msg_classes() -> tuple[type[Any], type[Any], type[Any]]:
from otel_test_json.common.v1.common import ( # type: ignore
InstrumentationScope as JSONScope,
)
@@ -81,7 +70,7 @@ def oneof_msg_classes() -> tuple[Type[Any], Type[Any], Type[Any]]:
@pytest.fixture
-def optional_msg_classes() -> tuple[Type[Any], Type[Any]]:
+def optional_msg_classes() -> tuple[type[Any], type[Any]]:
from otel_test_json.test.v1.complex import ( # type: ignore
OptionalScalar as JSONOptionalScalar,
)
@@ -93,7 +82,7 @@ def optional_msg_classes() -> tuple[Type[Any], Type[Any]]:
def test_parity_test_message(
- test_msg_classes: tuple[Type[Any], Type[Any], Type[Any]],
+ test_msg_classes: tuple[type[Any], type[Any], type[Any]],
) -> None:
JSONTestMessage, JSONSubMessage, ProtoTestMessage = test_msg_classes
@@ -147,7 +136,7 @@ def test_parity_test_message(
],
)
def test_parity_numeric_test(
- numeric_msg_classes: tuple[Type[Any], Type[Any]], values: dict[str, Any]
+ numeric_msg_classes: tuple[type[Any], type[Any]], values: dict[str, Any]
) -> None:
JSONNumericTest, ProtoNumericTest = numeric_msg_classes
@@ -171,7 +160,7 @@ def test_parity_numeric_test(
],
)
def test_parity_oneof_suite(
- oneof_msg_classes: tuple[Type[Any], Type[Any], Type[Any]],
+ oneof_msg_classes: tuple[type[Any], type[Any], type[Any]],
branch_data: dict[str, Any],
) -> None:
JSONOneofSuite, ProtoOneofSuite, JSONScope = oneof_msg_classes
@@ -215,7 +204,7 @@ def test_parity_oneof_suite(
],
)
def test_parity_optional_scalars(
- optional_msg_classes: tuple[Type[Any], Type[Any]], kwargs: dict[str, Any]
+ optional_msg_classes: tuple[type[Any], type[Any]], kwargs: dict[str, Any]
) -> None:
JSONOptionalScalar, ProtoOptionalScalar = optional_msg_classes
diff --git a/codegen/opentelemetry-codegen-json/tests/test_serde.py b/codegen/opentelemetry-codegen-json/tests/test_serde.py
index 67db536..e89453e 100644
--- a/codegen/opentelemetry-codegen-json/tests/test_serde.py
+++ b/codegen/opentelemetry-codegen-json/tests/test_serde.py
@@ -1,29 +1,18 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: skip-file
# ruff: noqa: PLC0415
import json
import math
-from typing import Any, Type
+from typing import Any
import pytest # type: ignore
@pytest.fixture
-def test_v1_types() -> tuple[Type[Any], Type[Any]]:
+def test_v1_types() -> tuple[type[Any], type[Any]]:
from otel_test_json.test.v1.test import ( # type: ignore
SubMessage,
TestMessage,
@@ -33,7 +22,7 @@ def test_v1_types() -> tuple[Type[Any], Type[Any]]:
@pytest.fixture
-def common_v1_types() -> Type[Any]:
+def common_v1_types() -> type[Any]:
from otel_test_json.common.v1.common import ( # type: ignore
InstrumentationScope, # type: ignore
)
@@ -42,7 +31,7 @@ def common_v1_types() -> Type[Any]:
@pytest.fixture
-def trace_v1_types() -> Type[Any]:
+def trace_v1_types() -> type[Any]:
from otel_test_json.trace.v1.trace import Span # type: ignore
return Span
@@ -50,7 +39,7 @@ def trace_v1_types() -> Type[Any]:
@pytest.fixture
def complex_v1_types() -> tuple[
- Type[Any], Type[Any], Type[Any], Type[Any], Type[Any]
+ type[Any], type[Any], type[Any], type[Any], type[Any]
]:
from otel_test_json.test.v1.complex import ( # type: ignore
DeeplyNested,
@@ -70,7 +59,7 @@ def complex_v1_types() -> tuple[
def test_generated_message_roundtrip(
- test_v1_types: tuple[Type[Any], Type[Any]],
+ test_v1_types: tuple[type[Any], type[Any]],
) -> None:
TestMessage, SubMessage = test_v1_types
diff --git a/codegen/opentelemetry-codegen-json/tests/test_types.py b/codegen/opentelemetry-codegen-json/tests/test_types.py
index 6524bd3..178fcae 100644
--- a/codegen/opentelemetry-codegen-json/tests/test_types.py
+++ b/codegen/opentelemetry-codegen-json/tests/test_types.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=no-member
diff --git a/codegen/opentelemetry-codegen-json/tests/test_writer.py b/codegen/opentelemetry-codegen-json/tests/test_writer.py
index e9565e5..f931c2b 100644
--- a/codegen/opentelemetry-codegen-json/tests/test_writer.py
+++ b/codegen/opentelemetry-codegen-json/tests/test_writer.py
@@ -1,17 +1,6 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 Any, Optional
+# SPDX-License-Identifier: Apache-2.0
+from typing import Any
import pytest # type: ignore
@@ -106,8 +95,8 @@ def test_suite() -> None:
)
def test_class(
name: str,
- bases: Optional[str],
- decorators: Optional[list[str]],
+ bases: str | None,
+ decorators: list[str] | None,
expected: list[str],
) -> None:
writer = CodeWriter()
@@ -175,8 +164,8 @@ def test_enum() -> None:
def test_field(
name: str,
type_hint: str,
- default: Optional[Any],
- default_factory: Optional[Any],
+ default: Any | None,
+ default_factory: Any | None,
expected: list[str],
) -> None:
writer = CodeWriter()
diff --git a/debian/changelog b/debian/changelog
index f371308..bd3b7bc 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+python-opentelemetry (1.42.1-1) unstable; urgency=medium
+
+ * [a6c1263] New upstream version 1.42.1
+ * [dfb1a0f] Rebuild patch queue from patch-queue branch
+ Dropped patch (fixed upstream):
+ Don-t-try-using-importlib_metadata-library.patch
+
+ -- Carsten Schoenert <c.schoenert@t-online.de> Sat, 30 May 2026 09:46:16 +0200
+
python-opentelemetry (1.41.0-2) unstable; urgency=medium
* [0fa4b08] d/control: Add missing deps in -sdk package
diff --git a/debian/patches/Don-t-try-using-importlib_metadata-library.patch b/debian/patches/Don-t-try-using-importlib_metadata-library.patch
deleted file mode 100644
index 918a88c..0000000
--- a/debian/patches/Don-t-try-using-importlib_metadata-library.patch
+++ /dev/null
@@ -1,51 +0,0 @@
-From: Debian Python Team <team+python@tracker.debian.org>
-Date: Sat, 7 Mar 2026 07:27:03 +0200
-Subject: Don't try using importlib_metadata library
-
-Since Python 3.8 there is the module importlib available in the standard
-library module in Python. And since Python 3.12 the metadata
-implementation is stable enough to be reliable for opentelemetry.
----
- opentelemetry-api/pyproject.toml | 3 ---
- opentelemetry-api/src/opentelemetry/util/_importlib_metadata.py | 7 ++-----
- 2 files changed, 2 insertions(+), 8 deletions(-)
-
-diff --git a/opentelemetry-api/pyproject.toml b/opentelemetry-api/pyproject.toml
-index a75e158..0fd112b 100644
---- a/opentelemetry-api/pyproject.toml
-+++ b/opentelemetry-api/pyproject.toml
-@@ -27,9 +27,6 @@ classifiers = [
- ]
- dependencies = [
- "typing-extensions >= 4.5.0",
-- # FIXME This should be able to be removed after 3.12 is released if there is a reliable API
-- # in importlib.metadata.
-- "importlib-metadata >= 6.0, < 8.8.0",
- ]
- dynamic = [
- "version",
-diff --git a/opentelemetry-api/src/opentelemetry/util/_importlib_metadata.py b/opentelemetry-api/src/opentelemetry/util/_importlib_metadata.py
-index a527bd7..75d1979 100644
---- a/opentelemetry-api/src/opentelemetry/util/_importlib_metadata.py
-+++ b/opentelemetry-api/src/opentelemetry/util/_importlib_metadata.py
-@@ -14,10 +14,7 @@
-
- from functools import cache
-
--# FIXME: Use importlib.metadata (not importlib_metadata)
--# when support for 3.11 is dropped if the rest of
--# the supported versions at that time have the same API.
--from importlib_metadata import ( # type: ignore
-+from importlib.metadata import ( # type: ignore
- Distribution,
- EntryPoint,
- EntryPoints,
-@@ -26,7 +23,7 @@ from importlib_metadata import ( # type: ignore
- requires,
- version,
- )
--from importlib_metadata import (
-+from importlib.metadata import (
- entry_points as original_entry_points,
- )
-
diff --git a/debian/patches/Use-local-intersphinx-resources-where-possible.patch b/debian/patches/Use-local-intersphinx-resources-where-possible.patch
index fbe2561..20f5bb8 100644
--- a/debian/patches/Use-local-intersphinx-resources-where-possible.patch
+++ b/debian/patches/Use-local-intersphinx-resources-where-possible.patch
@@ -15,10 +15,10 @@ Forwarded: not-needed
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/docs/conf.py b/docs/conf.py
-index 261fd0f..06ca6db 100644
+index 45fc14d..bd7dca4 100644
--- a/docs/conf.py
+++ b/docs/conf.py
-@@ -78,15 +78,15 @@ extensions = [
+@@ -81,15 +81,15 @@ extensions = [
]
intersphinx_mapping = {
diff --git a/debian/patches/series b/debian/patches/series
index b4fbeb4..019cda3 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,3 +1,2 @@
Use-local-intersphinx-resources-where-possible.patch
docs-Don-t-load-SVG-graphic-for-Slack-Chat-link.patch
-Don-t-try-using-importlib_metadata-library.patch
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 36a78f1..9b9727d 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -14,7 +14,6 @@ requests==2.32.3
ruamel.yaml==0.17.21
asgiref==3.7.2
psutil==7.2.2
-GitPython==3.1.41
-pre-commit==3.7.0; python_version >= '3.9'
-pre-commit==3.5.0; python_version < '3.9'
+GitPython==3.1.50
+pre-commit==3.7.0
ruff==0.14.1
diff --git a/docs/api/context.context.rst b/docs/api/context.context.rst
deleted file mode 100644
index 331557d..0000000
--- a/docs/api/context.context.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-opentelemetry.context.base\_context module
-==========================================
-
-.. automodule:: opentelemetry.context.context
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/api/context.rst b/docs/api/context.rst
index 7aef5ff..e73d8f6 100644
--- a/docs/api/context.rst
+++ b/docs/api/context.rst
@@ -1,14 +1,10 @@
opentelemetry.context package
=============================
-Submodules
-----------
-
-.. toctree::
-
- context.context
-
Module contents
---------------
.. automodule:: opentelemetry.context
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/trace.rst b/docs/api/trace.rst
index 65d9b4d..b59d9dc 100644
--- a/docs/api/trace.rst
+++ b/docs/api/trace.rst
@@ -1,15 +1,10 @@
opentelemetry.trace package
===========================
-Submodules
-----------
-
-.. toctree::
-
- trace.status
- trace.span
-
Module contents
---------------
-.. automodule:: opentelemetry.trace \ No newline at end of file
+.. automodule:: opentelemetry.trace
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/trace.span.rst b/docs/api/trace.span.rst
deleted file mode 100644
index 94b3693..0000000
--- a/docs/api/trace.span.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-opentelemetry.trace.span
-========================
-
-.. automodule:: opentelemetry.trace.span
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/api/trace.status.rst b/docs/api/trace.status.rst
deleted file mode 100644
index 0205446..0000000
--- a/docs/api/trace.status.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-opentelemetry.trace.status
-==========================
-
-.. automodule:: opentelemetry.trace.status
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/conf.py b/docs/conf.py
index 8ca6a83..45fc14d 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
@@ -174,6 +177,10 @@ nitpick_ignore = [
"py:class",
"AnyValue",
),
+ (
+ "py:class",
+ "_ExtendedAttributes",
+ ),
]
# Add any paths that contain templates here, relative to this directory.
diff --git a/docs/examples/auto-instrumentation/client.py b/docs/examples/auto-instrumentation/client.py
index 508a9b1..337a0d6 100644
--- a/docs/examples/auto-instrumentation/client.py
+++ b/docs/examples/auto-instrumentation/client.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import sys
diff --git a/docs/examples/auto-instrumentation/server_automatic.py b/docs/examples/auto-instrumentation/server_automatic.py
index 9c247a0..90ad100 100644
--- a/docs/examples/auto-instrumentation/server_automatic.py
+++ b/docs/examples/auto-instrumentation/server_automatic.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from flask import Flask, request
diff --git a/docs/examples/auto-instrumentation/server_manual.py b/docs/examples/auto-instrumentation/server_manual.py
index 38abc02..8e17c75 100644
--- a/docs/examples/auto-instrumentation/server_manual.py
+++ b/docs/examples/auto-instrumentation/server_manual.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from flask import Flask, request
diff --git a/docs/examples/auto-instrumentation/server_programmatic.py b/docs/examples/auto-instrumentation/server_programmatic.py
index 759613e..23c03bc 100644
--- a/docs/examples/auto-instrumentation/server_programmatic.py
+++ b/docs/examples/auto-instrumentation/server_programmatic.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from flask import Flask, request
diff --git a/docs/examples/basic_context/async_context.py b/docs/examples/basic_context/async_context.py
index d80ccb3..0b59ec0 100644
--- a/docs/examples/basic_context/async_context.py
+++ b/docs/examples/basic_context/async_context.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
diff --git a/docs/examples/basic_context/child_context.py b/docs/examples/basic_context/child_context.py
index d2a6d50..757763f 100644
--- a/docs/examples/basic_context/child_context.py
+++ b/docs/examples/basic_context/child_context.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry import baggage, trace
diff --git a/docs/examples/basic_context/implicit_context.py b/docs/examples/basic_context/implicit_context.py
index 0d89448..90fbe1b 100644
--- a/docs/examples/basic_context/implicit_context.py
+++ b/docs/examples/basic_context/implicit_context.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry import baggage, trace
from opentelemetry.sdk.trace import TracerProvider
diff --git a/docs/examples/basic_tracer/basic_trace.py b/docs/examples/basic_tracer/basic_trace.py
index bb1e341..b5cdab6 100644
--- a/docs/examples/basic_tracer/basic_trace.py
+++ b/docs/examples/basic_tracer/basic_trace.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
diff --git a/docs/examples/basic_tracer/resources.py b/docs/examples/basic_tracer/resources.py
index 87853a8..305cced 100644
--- a/docs/examples/basic_tracer/resources.py
+++ b/docs/examples/basic_tracer/resources.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
diff --git a/docs/examples/django/client.py b/docs/examples/django/client.py
index d8d4769..707004c 100644
--- a/docs/examples/django/client.py
+++ b/docs/examples/django/client.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from sys import argv
diff --git a/docs/examples/django/instrumentation_example/asgi.py b/docs/examples/django/instrumentation_example/asgi.py
index dd8fb56..5f6487b 100644
--- a/docs/examples/django/instrumentation_example/asgi.py
+++ b/docs/examples/django/instrumentation_example/asgi.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
ASGI config for instrumentation_example project.
diff --git a/docs/examples/django/instrumentation_example/settings.py b/docs/examples/django/instrumentation_example/settings.py
index b5b8897..33e631e 100644
--- a/docs/examples/django/instrumentation_example/settings.py
+++ b/docs/examples/django/instrumentation_example/settings.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
Django settings for instrumentation_example project.
diff --git a/docs/examples/django/instrumentation_example/urls.py b/docs/examples/django/instrumentation_example/urls.py
index fcdb2e0..d408a15 100644
--- a/docs/examples/django/instrumentation_example/urls.py
+++ b/docs/examples/django/instrumentation_example/urls.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""instrumentation_example URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
diff --git a/docs/examples/django/instrumentation_example/wsgi.py b/docs/examples/django/instrumentation_example/wsgi.py
index 70ea9e0..46c0c26 100644
--- a/docs/examples/django/instrumentation_example/wsgi.py
+++ b/docs/examples/django/instrumentation_example/wsgi.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
WSGI config for instrumentation_example project.
diff --git a/docs/examples/django/manage.py b/docs/examples/django/manage.py
index 75d9bc2..2ddd5e8 100755
--- a/docs/examples/django/manage.py
+++ b/docs/examples/django/manage.py
@@ -1,17 +1,9 @@
#!/usr/bin/env python
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
+
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
"""Django"s command-line utility for administrative tasks."""
diff --git a/docs/examples/django/pages/__init__.py b/docs/examples/django/pages/__init__.py
index 5855e41..de1bfea 100644
--- a/docs/examples/django/pages/__init__.py
+++ b/docs/examples/django/pages/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
default_app_config = "pages.apps.PagesConfig"
diff --git a/docs/examples/django/pages/apps.py b/docs/examples/django/pages/apps.py
index 0f12b7b..ed8e1ef 100644
--- a/docs/examples/django/pages/apps.py
+++ b/docs/examples/django/pages/apps.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from django.apps import AppConfig
diff --git a/docs/examples/django/pages/urls.py b/docs/examples/django/pages/urls.py
index 99c9576..cf77a51 100644
--- a/docs/examples/django/pages/urls.py
+++ b/docs/examples/django/pages/urls.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from django.urls import path
from .views import home_page_view
diff --git a/docs/examples/django/pages/views.py b/docs/examples/django/pages/views.py
index e805f43..83d532d 100644
--- a/docs/examples/django/pages/views.py
+++ b/docs/examples/django/pages/views.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from django.http import HttpResponse
from opentelemetry import trace
diff --git a/docs/examples/error_handler/error_handler_0/pyproject.toml b/docs/examples/error_handler/error_handler_0/pyproject.toml
index 31c1b41..7e73a2b 100644
--- a/docs/examples/error_handler/error_handler_0/pyproject.toml
+++ b/docs/examples/error_handler/error_handler_0/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "This is just an error handler example package"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -17,7 +17,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
diff --git a/docs/examples/error_handler/error_handler_0/src/error_handler_0/__init__.py b/docs/examples/error_handler/error_handler_0/src/error_handler_0/__init__.py
index ef3034b..f0ad517 100644
--- a/docs/examples/error_handler/error_handler_0/src/error_handler_0/__init__.py
+++ b/docs/examples/error_handler/error_handler_0/src/error_handler_0/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from logging import getLogger
diff --git a/docs/examples/error_handler/error_handler_0/src/error_handler_0/version/__init__.py b/docs/examples/error_handler/error_handler_0/src/error_handler_0/version/__init__.py
index c829b95..8aea899 100644
--- a/docs/examples/error_handler/error_handler_0/src/error_handler_0/version/__init__.py
+++ b/docs/examples/error_handler/error_handler_0/src/error_handler_0/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
__version__ = "0.23.dev0"
diff --git a/docs/examples/error_handler/error_handler_1/pyproject.toml b/docs/examples/error_handler/error_handler_1/pyproject.toml
index 5196844..ea8f2d2 100644
--- a/docs/examples/error_handler/error_handler_1/pyproject.toml
+++ b/docs/examples/error_handler/error_handler_1/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "This is just an error handler example package"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -17,7 +17,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
diff --git a/docs/examples/error_handler/error_handler_1/src/error_handler_1/__init__.py b/docs/examples/error_handler/error_handler_1/src/error_handler_1/__init__.py
index 1f210a3..393de26 100644
--- a/docs/examples/error_handler/error_handler_1/src/error_handler_1/__init__.py
+++ b/docs/examples/error_handler/error_handler_1/src/error_handler_1/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from logging import getLogger
diff --git a/docs/examples/error_handler/error_handler_1/src/error_handler_1/version/__init__.py b/docs/examples/error_handler/error_handler_1/src/error_handler_1/version/__init__.py
index c829b95..8aea899 100644
--- a/docs/examples/error_handler/error_handler_1/src/error_handler_1/version/__init__.py
+++ b/docs/examples/error_handler/error_handler_1/src/error_handler_1/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
__version__ = "0.23.dev0"
diff --git a/docs/examples/error_handler/example.py b/docs/examples/error_handler/example.py
index 372c39c..f251f25 100644
--- a/docs/examples/error_handler/example.py
+++ b/docs/examples/error_handler/example.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
from opentelemetry.sdk.error_handler import GlobalErrorHandler
# ZeroDivisionError to be handled by ErrorHandler0
diff --git a/docs/examples/fork-process-model/flask-gunicorn/app.py b/docs/examples/fork-process-model/flask-gunicorn/app.py
index 008e1f0..9213408 100644
--- a/docs/examples/fork-process-model/flask-gunicorn/app.py
+++ b/docs/examples/fork-process-model/flask-gunicorn/app.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import flask
from flask import request
diff --git a/docs/examples/fork-process-model/flask-gunicorn/gunicorn.conf.py b/docs/examples/fork-process-model/flask-gunicorn/gunicorn.conf.py
index 34b4591..51f852d 100644
--- a/docs/examples/fork-process-model/flask-gunicorn/gunicorn.conf.py
+++ b/docs/examples/fork-process-model/flask-gunicorn/gunicorn.conf.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry import metrics, trace
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
diff --git a/docs/examples/fork-process-model/flask-uwsgi/app.py b/docs/examples/fork-process-model/flask-uwsgi/app.py
index 1191bcc..d822bc7 100644
--- a/docs/examples/fork-process-model/flask-uwsgi/app.py
+++ b/docs/examples/fork-process-model/flask-uwsgi/app.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import flask
from flask import request
diff --git a/docs/examples/logs/example.py b/docs/examples/logs/example.py
index 51d12ce..08d04e9 100644
--- a/docs/examples/logs/example.py
+++ b/docs/examples/logs/example.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
import logging
from opentelemetry import trace
diff --git a/docs/examples/metrics/instruments/example.py b/docs/examples/metrics/instruments/example.py
index 90a9f7f..299c43f 100644
--- a/docs/examples/metrics/instruments/example.py
+++ b/docs/examples/metrics/instruments/example.py
@@ -1,4 +1,7 @@
-from typing import Iterable
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+from collections.abc import Iterable
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
OTLPMetricExporter,
diff --git a/docs/examples/metrics/prometheus-grafana/prometheus-monitor.py b/docs/examples/metrics/prometheus-grafana/prometheus-monitor.py
index 941be3b..c217b4f 100644
--- a/docs/examples/metrics/prometheus-grafana/prometheus-monitor.py
+++ b/docs/examples/metrics/prometheus-grafana/prometheus-monitor.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
import random
import time
diff --git a/docs/examples/metrics/reader/preferred_aggregation.py b/docs/examples/metrics/reader/preferred_aggregation.py
index a332840..b25b203 100644
--- a/docs/examples/metrics/reader/preferred_aggregation.py
+++ b/docs/examples/metrics/reader/preferred_aggregation.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import time
diff --git a/docs/examples/metrics/reader/preferred_exemplarfilter.py b/docs/examples/metrics/reader/preferred_exemplarfilter.py
index fd1e1cc..e519bc1 100644
--- a/docs/examples/metrics/reader/preferred_exemplarfilter.py
+++ b/docs/examples/metrics/reader/preferred_exemplarfilter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import time
from opentelemetry import trace
diff --git a/docs/examples/metrics/reader/preferred_temporality.py b/docs/examples/metrics/reader/preferred_temporality.py
index 910c3fc..070d9c7 100644
--- a/docs/examples/metrics/reader/preferred_temporality.py
+++ b/docs/examples/metrics/reader/preferred_temporality.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import time
diff --git a/docs/examples/metrics/reader/synchronous_gauge_read.py b/docs/examples/metrics/reader/synchronous_gauge_read.py
index d45f7ff..41499c1 100644
--- a/docs/examples/metrics/reader/synchronous_gauge_read.py
+++ b/docs/examples/metrics/reader/synchronous_gauge_read.py
@@ -1,19 +1,8 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import math
-from typing import Iterable
+from collections.abc import Iterable
from opentelemetry.metrics import (
CallbackOptions,
diff --git a/docs/examples/metrics/views/change_aggregation.py b/docs/examples/metrics/views/change_aggregation.py
index 5dad07e..3bdc33a 100644
--- a/docs/examples/metrics/views/change_aggregation.py
+++ b/docs/examples/metrics/views/change_aggregation.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import random
import time
diff --git a/docs/examples/metrics/views/change_name.py b/docs/examples/metrics/views/change_name.py
index c70f785..7d79457 100644
--- a/docs/examples/metrics/views/change_name.py
+++ b/docs/examples/metrics/views/change_name.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import random
import time
diff --git a/docs/examples/metrics/views/change_reservoir_factory.py b/docs/examples/metrics/views/change_reservoir_factory.py
index 8f8c676..7b4d2ab 100644
--- a/docs/examples/metrics/views/change_reservoir_factory.py
+++ b/docs/examples/metrics/views/change_reservoir_factory.py
@@ -1,20 +1,8 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import random
import time
-from typing import Type
from opentelemetry import trace
from opentelemetry.metrics import get_meter_provider, set_meter_provider
@@ -39,7 +27,7 @@ from opentelemetry.sdk.trace import TracerProvider
# Create a custom reservoir factory with specified parameters
def custom_reservoir_factory(
- aggregationType: Type[_Aggregation],
+ aggregationType: type[_Aggregation],
) -> ExemplarReservoirBuilder:
if issubclass(aggregationType, _ExplicitBucketHistogramAggregation):
return AlignedHistogramBucketExemplarReservoir
diff --git a/docs/examples/metrics/views/disable_default_aggregation.py b/docs/examples/metrics/views/disable_default_aggregation.py
index 387bfc4..e0e5bad 100644
--- a/docs/examples/metrics/views/disable_default_aggregation.py
+++ b/docs/examples/metrics/views/disable_default_aggregation.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import random
import time
diff --git a/docs/examples/metrics/views/drop_metrics_from_instrument.py b/docs/examples/metrics/views/drop_metrics_from_instrument.py
index c8ca100..fbefbc3 100644
--- a/docs/examples/metrics/views/drop_metrics_from_instrument.py
+++ b/docs/examples/metrics/views/drop_metrics_from_instrument.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import random
import time
diff --git a/docs/examples/metrics/views/limit_num_of_attrs.py b/docs/examples/metrics/views/limit_num_of_attrs.py
index d9f0e94..eedf389 100644
--- a/docs/examples/metrics/views/limit_num_of_attrs.py
+++ b/docs/examples/metrics/views/limit_num_of_attrs.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import random
import time
-from typing import Iterable
+from collections.abc import Iterable
from opentelemetry.metrics import (
CallbackOptions,
diff --git a/docs/examples/multi-destination-exporting/README.rst b/docs/examples/multi-destination-exporting/README.rst
new file mode 100644
index 0000000..e5b0720
--- /dev/null
+++ b/docs/examples/multi-destination-exporting/README.rst
@@ -0,0 +1,67 @@
+Multi-Destination Exporting
+===========================
+
+This example shows how to export telemetry data to multiple destinations
+simultaneously. As described in the `OTLP specification
+<https://opentelemetry.io/docs/specs/otlp/#multi-destination-exporting>`_,
+each destination should have implemented its own queuing, acknowledgement
+handling, and retry logic to prevent one slow or unavailable destination
+from blocking the others.
+
+The OpenTelemetry Python SDK achieves this by using a separate processor
+or reader per destination:
+
+* **Traces**: Use one ``BatchSpanProcessor`` per destination, each wrapping
+ its own ``SpanExporter``. Add each processor to the ``TracerProvider``
+ via ``add_span_processor()``.
+
+* **Metrics**: Pass multiple ``MetricReader`` instances (each wrapping its
+ own ``MetricExporter``) to the ``MeterProvider`` constructor via the
+ ``metric_readers`` parameter.
+
+* **Logs**: Use one ``BatchLogRecordProcessor`` per destination, each
+ wrapping its own ``LogExporter``. Add each processor to the
+ ``LoggerProvider`` via ``add_log_record_processor()``.
+
+.. note::
+
+ The **Profiles** signal is not yet supported in the Python SDK.
+ When it becomes available, the same pattern will apply.
+
+The source files of these examples are available :scm_web:`here <docs/examples/multi-destination-exporting/>`.
+
+Installation
+------------
+
+.. code-block:: sh
+
+ pip install opentelemetry-api
+ pip install opentelemetry-sdk
+ pip install opentelemetry-exporter-otlp-proto-grpc
+ pip install opentelemetry-exporter-otlp-proto-http
+ pip install opentelemetry-instrumentation-logging # For LoggingHandler
+
+Run the Example
+---------------
+
+.. code-block:: sh
+
+ python multi_destination_traces.py
+ python multi_destination_metrics.py
+ python multi_destination_logs.py
+
+The output will be shown in the console for the ``ConsoleSpanExporter``,
+``ConsoleMetricExporter``, and ``ConsoleLogRecordExporter`` destinations.
+The OTLP destinations require a running `collector
+<https://opentelemetry.io/docs/collector/>`_.
+
+Useful links
+------------
+
+- `OTLP multi-destination exporting specification <https://opentelemetry.io/docs/specs/otlp/#multi-destination-exporting>`_
+- OpenTelemetry_
+- :doc:`../../api/trace`
+- :doc:`../../api/metrics`
+- :doc:`../../api/_logs`
+
+.. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/
diff --git a/docs/examples/multi-destination-exporting/multi_destination_logs.py b/docs/examples/multi-destination-exporting/multi_destination_logs.py
new file mode 100644
index 0000000..4236aff
--- /dev/null
+++ b/docs/examples/multi-destination-exporting/multi_destination_logs.py
@@ -0,0 +1,59 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+"""
+This example shows how to export logs to multiple destinations.
+Each BatchLogRecordProcessor has its own queue and retry logic, so
+destinations do not block each other.
+"""
+
+import logging
+
+from opentelemetry._logs import set_logger_provider
+from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
+ OTLPLogExporter as GrpcLogExporter,
+)
+from opentelemetry.exporter.otlp.proto.http._log_exporter import (
+ OTLPLogExporter as HttpLogExporter,
+)
+
+# this is available in the opentelemetry-instrumentation-logging package
+from opentelemetry.instrumentation.logging.handler import LoggingHandler
+from opentelemetry.sdk._logs import LoggerProvider
+from opentelemetry.sdk._logs.export import (
+ BatchLogRecordProcessor,
+ ConsoleLogRecordExporter,
+)
+
+logger_provider = LoggerProvider()
+set_logger_provider(logger_provider)
+
+# Destination 1: OTLP over gRPC
+grpc_exporter = GrpcLogExporter(
+ endpoint="http://localhost:4317", insecure=True
+)
+logger_provider.add_log_record_processor(
+ BatchLogRecordProcessor(grpc_exporter)
+)
+
+# Destination 2: OTLP over HTTP
+http_exporter = HttpLogExporter(endpoint="http://localhost:4318/v1/logs")
+logger_provider.add_log_record_processor(
+ BatchLogRecordProcessor(http_exporter)
+)
+
+# Destination 3: Console (for debugging)
+logger_provider.add_log_record_processor(
+ BatchLogRecordProcessor(ConsoleLogRecordExporter())
+)
+
+# Bridge Python's logging to OpenTelemetry
+handler = LoggingHandler(level=logging.NOTSET, logger_provider=logger_provider)
+logging.getLogger().setLevel(logging.NOTSET)
+logging.getLogger().addHandler(handler)
+
+logger = logging.getLogger("myapp")
+logger.info("Logs are exported to all three destinations.")
+logger.warning("This warning also goes everywhere.")
+
+logger_provider.shutdown()
diff --git a/docs/examples/multi-destination-exporting/multi_destination_metrics.py b/docs/examples/multi-destination-exporting/multi_destination_metrics.py
new file mode 100644
index 0000000..8f6ca5e
--- /dev/null
+++ b/docs/examples/multi-destination-exporting/multi_destination_metrics.py
@@ -0,0 +1,52 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+"""
+This example shows how to export metrics to multiple destinations.
+Each PeriodicExportingMetricReader has its own collection interval
+and export queue, so destinations do not block each other.
+"""
+
+from opentelemetry import metrics
+from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
+ OTLPMetricExporter as GrpcMetricExporter,
+)
+from opentelemetry.exporter.otlp.proto.http.metric_exporter import (
+ OTLPMetricExporter as HttpMetricExporter,
+)
+from opentelemetry.sdk.metrics import MeterProvider
+from opentelemetry.sdk.metrics.export import (
+ ConsoleMetricExporter,
+ PeriodicExportingMetricReader,
+)
+
+# Destination 1: OTLP over gRPC
+grpc_reader = PeriodicExportingMetricReader(
+ GrpcMetricExporter(endpoint="http://localhost:4317", insecure=True)
+)
+
+# Destination 2: OTLP over HTTP
+http_reader = PeriodicExportingMetricReader(
+ HttpMetricExporter(endpoint="http://localhost:4318/v1/metrics")
+)
+
+# Destination 3: Console (for debugging)
+console_reader = PeriodicExportingMetricReader(ConsoleMetricExporter())
+
+# Pass all readers to the MeterProvider
+provider = MeterProvider(
+ metric_readers=[grpc_reader, http_reader, console_reader]
+)
+metrics.set_meter_provider(provider)
+
+meter = metrics.get_meter(__name__)
+counter = meter.create_counter(
+ "request.count", description="Number of requests"
+)
+
+counter.add(1, {"endpoint": "/api/users"})
+counter.add(1, {"endpoint": "/api/orders"})
+
+print("Metrics are exported to all three destinations.")
+
+provider.shutdown()
diff --git a/docs/examples/multi-destination-exporting/multi_destination_traces.py b/docs/examples/multi-destination-exporting/multi_destination_traces.py
new file mode 100644
index 0000000..ea4a737
--- /dev/null
+++ b/docs/examples/multi-destination-exporting/multi_destination_traces.py
@@ -0,0 +1,45 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+"""
+This example shows how to export traces to multiple destinations.
+Each BatchSpanProcessor has its own queue and retry logic, so
+destinations do not block each other.
+"""
+
+from opentelemetry import trace
+from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
+ OTLPSpanExporter as GrpcSpanExporter,
+)
+from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
+ OTLPSpanExporter as HttpSpanExporter,
+)
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import (
+ BatchSpanProcessor,
+ ConsoleSpanExporter,
+)
+
+provider = TracerProvider()
+trace.set_tracer_provider(provider)
+
+# Destination 1: OTLP over gRPC
+grpc_exporter = GrpcSpanExporter(
+ endpoint="http://localhost:4317", insecure=True
+)
+provider.add_span_processor(BatchSpanProcessor(grpc_exporter))
+
+# Destination 2: OTLP over HTTP
+http_exporter = HttpSpanExporter(endpoint="http://localhost:4318/v1/traces")
+provider.add_span_processor(BatchSpanProcessor(http_exporter))
+
+# Destination 3: Console (for debugging)
+provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
+
+tracer = trace.get_tracer(__name__)
+
+with tracer.start_as_current_span("example-request"):
+ with tracer.start_as_current_span("fetch-data"):
+ print("Spans are exported to all three destinations.")
+
+provider.shutdown()
diff --git a/docs/examples/opencensus-exporter-tracer/collector.py b/docs/examples/opencensus-exporter-tracer/collector.py
index cd33c89..f1d1025 100644
--- a/docs/examples/opencensus-exporter-tracer/collector.py
+++ b/docs/examples/opencensus-exporter-tracer/collector.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry import trace
from opentelemetry.exporter.opencensus.trace_exporter import (
diff --git a/docs/examples/opencensus-shim/app.py b/docs/examples/opencensus-shim/app.py
index d175ff8..9493215 100644
--- a/docs/examples/opencensus-shim/app.py
+++ b/docs/examples/opencensus-shim/app.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import sqlite3
diff --git a/docs/examples/opentracing/main.py b/docs/examples/opentracing/main.py
index f8a9c55..3bbbb1b 100755
--- a/docs/examples/opentracing/main.py
+++ b/docs/examples/opentracing/main.py
@@ -1,4 +1,6 @@
#!/usr/bin/env python
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
from rediscache import RedisCache
diff --git a/docs/examples/opentracing/rediscache.py b/docs/examples/opentracing/rediscache.py
index 61025ea..107bdeb 100644
--- a/docs/examples/opentracing/rediscache.py
+++ b/docs/examples/opentracing/rediscache.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
"""
This is an example of a library written to work with opentracing-python. It
provides a simple caching decorator backed by Redis, and uses the OpenTracing
diff --git a/docs/examples/sqlcommenter/instrumented_query.py b/docs/examples/sqlcommenter/instrumented_query.py
index 1844b46..6f7f06c 100644
--- a/docs/examples/sqlcommenter/instrumented_query.py
+++ b/docs/examples/sqlcommenter/instrumented_query.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from mysql.connector import connect
diff --git a/docs/exporter/otlp/otlp.rst b/docs/exporter/otlp/otlp.rst
index 18b8b15..3b9c1fa 100644
--- a/docs/exporter/otlp/otlp.rst
+++ b/docs/exporter/otlp/otlp.rst
@@ -14,10 +14,19 @@ opentelemetry.exporter.otlp.proto.http
:show-inheritance:
.. automodule:: opentelemetry.exporter.otlp.proto.http.trace_exporter
+ :members:
+ :undoc-members:
+ :show-inheritance:
.. automodule:: opentelemetry.exporter.otlp.proto.http.metric_exporter
+ :members:
+ :undoc-members:
+ :show-inheritance:
.. automodule:: opentelemetry.exporter.otlp.proto.http._log_exporter
+ :members:
+ :undoc-members:
+ :show-inheritance:
opentelemetry.exporter.otlp.proto.grpc
---------------------------------------
@@ -28,7 +37,16 @@ opentelemetry.exporter.otlp.proto.grpc
:show-inheritance:
.. automodule:: opentelemetry.exporter.otlp.proto.grpc.trace_exporter
+ :members:
+ :undoc-members:
+ :show-inheritance:
.. automodule:: opentelemetry.exporter.otlp.proto.grpc.metric_exporter
+ :members:
+ :undoc-members:
+ :show-inheritance:
.. automodule:: opentelemetry.exporter.otlp.proto.grpc._log_exporter
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/getting_started/flask_example.py b/docs/getting_started/flask_example.py
index 3ddf61d..62502fa 100644
--- a/docs/getting_started/flask_example.py
+++ b/docs/getting_started/flask_example.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# flask_example.py
import flask
diff --git a/docs/getting_started/metrics_example.py b/docs/getting_started/metrics_example.py
index 85df5cc..7d83d04 100644
--- a/docs/getting_started/metrics_example.py
+++ b/docs/getting_started/metrics_example.py
@@ -1,21 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# metrics.py
# This is still work in progress as the metrics SDK is being implemented
-from typing import Iterable
+from collections.abc import Iterable
from opentelemetry.metrics import (
CallbackOptions,
diff --git a/docs/getting_started/otlpcollector_example.py b/docs/getting_started/otlpcollector_example.py
index 11b3b12..624558f 100644
--- a/docs/getting_started/otlpcollector_example.py
+++ b/docs/getting_started/otlpcollector_example.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# otcollector.py
diff --git a/docs/getting_started/tests/__init__.py b/docs/getting_started/tests/__init__.py
index b0a6f42..e57cf4a 100644
--- a/docs/getting_started/tests/__init__.py
+++ b/docs/getting_started/tests/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/docs/getting_started/tests/requirements.txt b/docs/getting_started/tests/requirements.txt
index ede29c3..a9e878d 100644
--- a/docs/getting_started/tests/requirements.txt
+++ b/docs/getting_started/tests/requirements.txt
@@ -5,7 +5,6 @@ charset-normalizer==2.0.12
click==8.1.7
Flask==2.3.3
idna==3.7
-importlib-metadata==6.8.0
iniconfig==2.0.0
itsdangerous==2.1.2
Jinja2==3.1.5
@@ -20,7 +19,6 @@ typing_extensions==4.12.0
urllib3==1.26.19
Werkzeug==3.0.6
wrapt==1.15.0
-zipp==3.19.2
-e opentelemetry-semantic-conventions
-e opentelemetry-proto
-e exporter/opentelemetry-exporter-otlp-proto-common
diff --git a/docs/getting_started/tests/test_flask.py b/docs/getting_started/tests/test_flask.py
index ffaa7de..79b97a6 100644
--- a/docs/getting_started/tests/test_flask.py
+++ b/docs/getting_started/tests/test_flask.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import os
import subprocess
import sys
diff --git a/docs/getting_started/tests/test_metrics.py b/docs/getting_started/tests/test_metrics.py
index 8a1c933..7b15691 100644
--- a/docs/getting_started/tests/test_metrics.py
+++ b/docs/getting_started/tests/test_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import json
import os
diff --git a/docs/getting_started/tests/test_otlpcollector.py b/docs/getting_started/tests/test_otlpcollector.py
index c757d4a..1f2a4a5 100644
--- a/docs/getting_started/tests/test_otlpcollector.py
+++ b/docs/getting_started/tests/test_otlpcollector.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# oltpcollector_example.py
import os
diff --git a/docs/getting_started/tests/test_tracing.py b/docs/getting_started/tests/test_tracing.py
index 2ad5719..27f180a 100644
--- a/docs/getting_started/tests/test_tracing.py
+++ b/docs/getting_started/tests/test_tracing.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import os
import subprocess
import sys
diff --git a/docs/getting_started/tracing_example.py b/docs/getting_started/tracing_example.py
index 519e45f..574b8f3 100644
--- a/docs/getting_started/tracing_example.py
+++ b/docs/getting_started/tracing_example.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# tracing.py
from opentelemetry import trace
diff --git a/eachdist.ini b/eachdist.ini
index 8bd66d3..4560977 100644
--- a/eachdist.ini
+++ b/eachdist.ini
@@ -11,7 +11,7 @@ sortfirst=
exporter/*
[stable]
-version=1.41.0
+version=1.42.1
packages=
opentelemetry-sdk
@@ -27,13 +27,14 @@ packages=
opentelemetry-api
[prerelease]
-version=0.62b0
+version=0.63b1
packages=
opentelemetry-opentracing-shim
opentelemetry-opencensus-shim
opentelemetry-exporter-opencensus
opentelemetry-exporter-prometheus
+ opentelemetry-exporter-otlp-json-common
opentelemetry-distro
opentelemetry-proto-json
opentelemetry-semantic-conventions
diff --git a/exporter/opentelemetry-exporter-opencensus/pyproject.toml b/exporter/opentelemetry-exporter-opencensus/pyproject.toml
index 81630ef..8e5c495 100644
--- a/exporter/opentelemetry-exporter-opencensus/pyproject.toml
+++ b/exporter/opentelemetry-exporter-opencensus/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenCensus Exporter"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -19,7 +19,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -32,7 +31,7 @@ dependencies = [
"grpcio >= 1.66.2, < 2.0.0; python_version == '3.13'",
"grpcio >= 1.75.1, < 2.0.0; python_version >= '3.14'",
"opencensus-proto >= 0.1.0, < 1.0.0",
- "opentelemetry-api >= 1.41.0",
+ "opentelemetry-api >= 1.42.1",
"opentelemetry-sdk >= 1.15",
"protobuf ~= 3.13",
"setuptools >= 16.0",
diff --git a/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/__init__.py b/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/__init__.py
index ff8bb25..09643cf 100644
--- a/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/__init__.py
+++ b/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
The **OpenCensus Exporter** allows to export traces using OpenCensus.
diff --git a/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/trace_exporter/__init__.py b/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/trace_exporter/__init__.py
index 0b79bbb..831d4b8 100644
--- a/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/trace_exporter/__init__.py
+++ b/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/trace_exporter/__init__.py
@@ -1,21 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""OpenCensus Span Exporter."""
import logging
-from typing import Sequence
+from collections.abc import Sequence
import grpc
from opencensus.proto.agent.trace.v1 import (
diff --git a/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/util.py b/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/util.py
index 77eed6f..bf071f2 100644
--- a/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/util.py
+++ b/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/util.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from os import getpid
from socket import gethostname
diff --git a/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/version/__init__.py b/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/version/__init__.py
index 766799c..23888c2 100644
--- a/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/version/__init__.py
+++ b/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "0.62b0"
+__version__ = "0.63b1"
diff --git a/exporter/opentelemetry-exporter-opencensus/test-requirements.txt b/exporter/opentelemetry-exporter-opencensus/test-requirements.txt
index 3c92c67..72f4636 100644
--- a/exporter/opentelemetry-exporter-opencensus/test-requirements.txt
+++ b/exporter/opentelemetry-exporter-opencensus/test-requirements.txt
@@ -1,6 +1,5 @@
asgiref==3.7.2
grpcio==1.75.1
-importlib-metadata==6.11.0
iniconfig==2.0.0
opencensus-proto==0.1.0
packaging==24.0
@@ -11,7 +10,6 @@ pytest==7.4.4
tomli==2.0.1
typing_extensions==4.12.0
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e opentelemetry-sdk
-e tests/opentelemetry-test-utils
diff --git a/exporter/opentelemetry-exporter-opencensus/tests/test_otcollector_trace_exporter.py b/exporter/opentelemetry-exporter-opencensus/tests/test_otcollector_trace_exporter.py
index 75340da..077531e 100644
--- a/exporter/opentelemetry-exporter-opencensus/tests/test_otcollector_trace_exporter.py
+++ b/exporter/opentelemetry-exporter-opencensus/tests/test_otcollector_trace_exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
from unittest import mock
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/LICENSE b/exporter/opentelemetry-exporter-otlp-json-common/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/README.rst b/exporter/opentelemetry-exporter-otlp-json-common/README.rst
new file mode 100644
index 0000000..6d315c2
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/README.rst
@@ -0,0 +1,25 @@
+OpenTelemetry JSON Encoding
+===============================
+
+|pypi|
+
+.. |pypi| image:: https://badge.fury.io/py/opentelemetry-exporter-otlp-json-common.svg
+ :target: https://pypi.org/project/opentelemetry-exporter-otlp-json-common/
+
+This library is provided as a convenience to encode to OTLP JSON. Currently used by:
+
+-
+
+Installation
+------------
+
+::
+
+ pip install opentelemetry-exporter-otlp-json-common
+
+
+References
+----------
+
+* `OpenTelemetry <https://opentelemetry.io/>`_
+* `OpenTelemetry Protocol Specification <https://github.com/open-telemetry/oteps/blob/main/text/0035-opentelemetry-protocol.md>`_
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/benchmark-requirements.txt b/exporter/opentelemetry-exporter-otlp-json-common/benchmark-requirements.txt
new file mode 100644
index 0000000..7a37592
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/benchmark-requirements.txt
@@ -0,0 +1,2 @@
+pytest-benchmark==4.0.0
+-r test-requirements.txt
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/benchmarks/conftest.py b/exporter/opentelemetry-exporter-otlp-json-common/benchmarks/conftest.py
new file mode 100644
index 0000000..c9b7bf8
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/benchmarks/conftest.py
@@ -0,0 +1,13 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+import sys
+from pathlib import Path
+
+_pkg_root = str(Path(__file__).resolve().parent.parent)
+
+
+def pytest_configure(config):
+ """Add the package root to sys.path so 'from tests import ...' works."""
+ if _pkg_root not in sys.path:
+ sys.path.insert(0, _pkg_root)
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/benchmarks/test_benchmark_log_encoder.py b/exporter/opentelemetry-exporter-otlp-json-common/benchmarks/test_benchmark_log_encoder.py
new file mode 100644
index 0000000..911277c
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/benchmarks/test_benchmark_log_encoder.py
@@ -0,0 +1,26 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+import pytest
+
+from opentelemetry._logs import SeverityNumber
+from opentelemetry.exporter.otlp.json.common._log_encoder import encode_logs
+from tests import TIME, make_log, make_log_context
+
+
+@pytest.mark.parametrize("batch_size", [1, 10, 100])
+def test_benchmark_encode_logs(benchmark, batch_size):
+ ctx = make_log_context()
+ logs = [
+ make_log(
+ body=f"log message {i}",
+ context=ctx,
+ timestamp=TIME + i,
+ observed_timestamp=TIME + i + 1000,
+ severity_text="INFO",
+ severity_number=SeverityNumber.INFO,
+ )
+ for i in range(batch_size)
+ ]
+
+ benchmark(encode_logs, logs)
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/benchmarks/test_benchmark_metrics_encoder.py b/exporter/opentelemetry-exporter-otlp-json-common/benchmarks/test_benchmark_metrics_encoder.py
new file mode 100644
index 0000000..d6a3f84
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/benchmarks/test_benchmark_metrics_encoder.py
@@ -0,0 +1,66 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+from opentelemetry.exporter.otlp.json.common.metrics_encoder import (
+ encode_metrics,
+)
+from opentelemetry.sdk.metrics import Exemplar
+from tests import (
+ SPAN_ID,
+ TIME,
+ TRACE_ID,
+ make_exponential_histogram,
+ make_gauge,
+ make_histogram,
+ make_metrics_data,
+ make_sum,
+)
+
+
+def test_benchmark_encode_sum(benchmark):
+ data = make_metrics_data([make_sum()])
+ benchmark(encode_metrics, data)
+
+
+def test_benchmark_encode_gauge(benchmark):
+ data = make_metrics_data([make_gauge()])
+ benchmark(encode_metrics, data)
+
+
+def test_benchmark_encode_histogram(benchmark):
+ data = make_metrics_data(
+ [
+ make_histogram(
+ exemplars=[
+ Exemplar(
+ {"sampled": "true"}, 298.0, TIME, SPAN_ID, TRACE_ID
+ ),
+ ],
+ )
+ ]
+ )
+ benchmark(encode_metrics, data)
+
+
+def test_benchmark_encode_exponential_histogram(benchmark):
+ data = make_metrics_data([make_exponential_histogram()])
+ benchmark(encode_metrics, data)
+
+
+def test_benchmark_encode_mixed_metrics(benchmark):
+ data = make_metrics_data(
+ [
+ make_sum(name="counter"),
+ make_gauge(name="gauge"),
+ make_histogram(
+ name="histogram",
+ exemplars=[
+ Exemplar(
+ {"sampled": "true"}, 298.0, TIME, SPAN_ID, TRACE_ID
+ ),
+ ],
+ ),
+ make_exponential_histogram(name="exp_histogram"),
+ ]
+ )
+ benchmark(encode_metrics, data)
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/benchmarks/test_benchmark_trace_encoder.py b/exporter/opentelemetry-exporter-otlp-json-common/benchmarks/test_benchmark_trace_encoder.py
new file mode 100644
index 0000000..be51ba4
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/benchmarks/test_benchmark_trace_encoder.py
@@ -0,0 +1,47 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+# pylint: disable=protected-access
+
+import pytest
+
+from opentelemetry.exporter.otlp.json.common.trace_encoder import encode_spans
+from opentelemetry.sdk.resources import Resource
+from opentelemetry.sdk.trace import Event, SpanContext
+from opentelemetry.sdk.util.instrumentation import InstrumentationScope
+from opentelemetry.trace import Link
+from opentelemetry.trace.status import Status, StatusCode
+from tests import BASE_TIME, TRACE_ID, make_span
+
+
+def test_benchmark_encode_span_with_events_and_links(benchmark):
+ link_ctx = SpanContext(TRACE_ID, 0x2222222222222222, is_remote=False)
+ span = make_span(
+ events=tuple(
+ Event(
+ name=f"event_{i}",
+ timestamp=BASE_TIME + i * 10**6,
+ attributes={"event_key": f"val_{i}"},
+ )
+ for i in range(5)
+ ),
+ links=tuple(
+ Link(context=link_ctx, attributes={"link_key": True})
+ for _ in range(3)
+ ),
+ resource=Resource({"service.name": "bench-svc"}),
+ instrumentation_scope=InstrumentationScope("bench_lib", "1.0"),
+ )
+ span._status = Status(StatusCode.ERROR, "benchmark error")
+
+ benchmark(encode_spans, [span])
+
+
+@pytest.mark.parametrize("batch_size", [1, 10, 100])
+def test_benchmark_encode_spans(benchmark, batch_size):
+ spans = [
+ make_span(name=f"span-{i}", span_id=0x1000 + i)
+ for i in range(batch_size)
+ ]
+
+ benchmark(encode_spans, spans)
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/pyproject.toml b/exporter/opentelemetry-exporter-otlp-json-common/pyproject.toml
new file mode 100644
index 0000000..d376bf6
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/pyproject.toml
@@ -0,0 +1,47 @@
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[project]
+name = "opentelemetry-exporter-otlp-json-common"
+dynamic = ["version"]
+description = "OpenTelemetry JSON encoding"
+readme = "README.rst"
+license = "Apache-2.0"
+requires-python = ">=3.10"
+authors = [
+ { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
+]
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Framework :: OpenTelemetry",
+ "Framework :: OpenTelemetry :: Exporters",
+ "Intended Audience :: Developers",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
+]
+dependencies = [
+ "opentelemetry-proto-json == 0.63b1",
+ "opentelemetry-sdk ~= 1.42.1",
+]
+
+[project.urls]
+Homepage = "https://github.com/open-telemetry/opentelemetry-python/tree/main/exporter/opentelemetry-exporter-otlp-json-common"
+Repository = "https://github.com/open-telemetry/opentelemetry-python"
+
+[tool.hatch.version]
+path = "src/opentelemetry/exporter/otlp/json/common/version/__init__.py"
+
+[tool.hatch.build.targets.sdist]
+include = [
+ "/src",
+ "/tests",
+]
+
+[tool.hatch.build.targets.wheel]
+packages = ["src/opentelemetry"]
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/__init__.py b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/__init__.py
new file mode 100644
index 0000000..e57cf4a
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/__init__.py
@@ -0,0 +1,2 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/__init__.py b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/__init__.py
new file mode 100644
index 0000000..7670c23
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/__init__.py
@@ -0,0 +1,135 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+# pyright: reportCallIssue=false
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Collection, Mapping, Sequence
+from typing import (
+ Any,
+ cast,
+)
+
+from opentelemetry.proto_json.common.v1.common import AnyValue as JSONAnyValue
+from opentelemetry.proto_json.common.v1.common import (
+ ArrayValue as JSONArrayValue,
+)
+from opentelemetry.proto_json.common.v1.common import (
+ InstrumentationScope as JSONInstrumentationScope,
+)
+from opentelemetry.proto_json.common.v1.common import KeyValue as JSONKeyValue
+from opentelemetry.proto_json.common.v1.common import (
+ KeyValueList as JSONKeyValueList,
+)
+from opentelemetry.proto_json.resource.v1.resource import (
+ Resource as JSONResource,
+)
+from opentelemetry.sdk.resources import Resource
+from opentelemetry.sdk.util.instrumentation import InstrumentationScope
+from opentelemetry.util.types import Attributes
+
+_logger = logging.getLogger(__name__)
+
+
+def _encode_instrumentation_scope(
+ instrumentation_scope: InstrumentationScope | None,
+) -> JSONInstrumentationScope:
+ return (
+ JSONInstrumentationScope(
+ name=instrumentation_scope.name,
+ version=instrumentation_scope.version,
+ attributes=_encode_attributes(instrumentation_scope.attributes),
+ )
+ if instrumentation_scope is not None
+ else JSONInstrumentationScope()
+ )
+
+
+def _encode_resource(resource: Resource) -> JSONResource:
+ return JSONResource(attributes=_encode_attributes(resource.attributes))
+
+
+# pylint: disable-next=too-many-return-statements
+def _encode_value(value: Any, allow_null: bool = False) -> JSONAnyValue | None:
+ if allow_null and value is None:
+ return None
+ if isinstance(value, bool):
+ return JSONAnyValue(bool_value=value)
+ if isinstance(value, str):
+ return JSONAnyValue(string_value=value)
+ if isinstance(value, int):
+ return JSONAnyValue(int_value=value)
+ if isinstance(value, float):
+ return JSONAnyValue(double_value=value)
+ if isinstance(value, bytes):
+ return JSONAnyValue(bytes_value=value)
+ if isinstance(value, Sequence):
+ return JSONAnyValue(
+ array_value=JSONArrayValue(
+ values=cast(
+ list[JSONAnyValue],
+ _encode_array(value, allow_null=allow_null),
+ )
+ )
+ )
+ if isinstance(value, Mapping):
+ return JSONAnyValue(
+ kvlist_value=JSONKeyValueList(
+ values=[
+ _encode_key_value(str(k), v, allow_null=allow_null)
+ for k, v in value.items()
+ ]
+ )
+ )
+ raise TypeError(f"Invalid type {type(value)} of value {value}")
+
+
+def _encode_key_value(
+ key: str, value: Any, allow_null: bool = False
+) -> JSONKeyValue:
+ return JSONKeyValue(
+ key=key, value=_encode_value(value, allow_null=allow_null)
+ )
+
+
+def _encode_array(
+ array: Collection[Any], allow_null: bool = False
+) -> list[JSONAnyValue | None]:
+ if not allow_null:
+ # Let the exception get raised by _encode_value()
+ return [_encode_value(v, allow_null=allow_null) for v in array]
+
+ return [
+ _encode_value(v, allow_null=allow_null)
+ if v is not None
+ else JSONAnyValue()
+ for v in array
+ ]
+
+
+def _encode_span_id(span_id: int) -> bytes:
+ return span_id.to_bytes(length=8, byteorder="big", signed=False)
+
+
+def _encode_trace_id(trace_id: int) -> bytes:
+ return trace_id.to_bytes(length=16, byteorder="big", signed=False)
+
+
+def _encode_attributes(
+ attributes: Attributes | None,
+ allow_null: bool = False,
+) -> list[JSONKeyValue]:
+ if not attributes:
+ return []
+ json_attributes = []
+ for key, value in attributes.items():
+ # pylint: disable=broad-exception-caught
+ try:
+ json_attributes.append(
+ _encode_key_value(key, value, allow_null=allow_null)
+ )
+ except Exception as error:
+ _logger.exception("Failed to encode key %s: %s", key, error)
+ return json_attributes
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/_log_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/_log_encoder/__init__.py
new file mode 100644
index 0000000..2569892
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/_log_encoder/__init__.py
@@ -0,0 +1,102 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+# pyright: reportCallIssue=false
+
+from __future__ import annotations
+
+from collections import defaultdict
+from collections.abc import Collection
+from typing import cast
+
+from opentelemetry.exporter.otlp.json.common._internal import (
+ _encode_attributes,
+ _encode_instrumentation_scope,
+ _encode_resource,
+ _encode_span_id,
+ _encode_trace_id,
+ _encode_value,
+)
+from opentelemetry.proto_json.collector.logs.v1.logs_service import (
+ ExportLogsServiceRequest as JSONExportLogsServiceRequest,
+)
+from opentelemetry.proto_json.logs.v1.logs import LogRecord as JSONLogRecord
+from opentelemetry.proto_json.logs.v1.logs import (
+ ResourceLogs as JSONResourceLogs,
+)
+from opentelemetry.proto_json.logs.v1.logs import (
+ ScopeLogs as JSONScopeLogs,
+)
+from opentelemetry.sdk._logs import ReadableLogRecord
+from opentelemetry.util.types import Attributes
+
+
+def encode_logs(
+ batch: Collection[ReadableLogRecord],
+) -> JSONExportLogsServiceRequest:
+ return JSONExportLogsServiceRequest(
+ resource_logs=_encode_resource_logs(batch)
+ )
+
+
+def _encode_log(readable_log_record: ReadableLogRecord) -> JSONLogRecord:
+ return JSONLogRecord(
+ time_unix_nano=readable_log_record.log_record.timestamp,
+ observed_time_unix_nano=readable_log_record.log_record.observed_timestamp,
+ span_id=_encode_span_id(readable_log_record.log_record.span_id)
+ if readable_log_record.log_record.span_id
+ else None,
+ trace_id=_encode_trace_id(readable_log_record.log_record.trace_id)
+ if readable_log_record.log_record.trace_id
+ else None,
+ flags=int(readable_log_record.log_record.trace_flags),
+ body=_encode_value(
+ readable_log_record.log_record.body, allow_null=True
+ ),
+ severity_text=readable_log_record.log_record.severity_text,
+ attributes=_encode_attributes(
+ cast(Attributes, readable_log_record.log_record.attributes),
+ allow_null=True,
+ ),
+ dropped_attributes_count=readable_log_record.dropped_attributes,
+ severity_number=getattr(
+ readable_log_record.log_record.severity_number, "value", None
+ ),
+ event_name=readable_log_record.log_record.event_name,
+ )
+
+
+def _encode_resource_logs(
+ batch: Collection[ReadableLogRecord],
+) -> list[JSONResourceLogs]:
+ sdk_resource_logs = defaultdict(lambda: defaultdict(list))
+
+ for readable_log in batch:
+ sdk_resource = readable_log.resource
+ sdk_instrumentation = readable_log.instrumentation_scope or None
+ sdk_resource_logs[sdk_resource][sdk_instrumentation].append(
+ _encode_log(readable_log)
+ )
+
+ json_resource_logs = []
+ for sdk_resource, sdk_instrumentations in sdk_resource_logs.items():
+ scope_logs = []
+ for sdk_instrumentation, json_logs in sdk_instrumentations.items():
+ scope_logs.append(
+ JSONScopeLogs(
+ scope=(_encode_instrumentation_scope(sdk_instrumentation)),
+ log_records=json_logs,
+ schema_url=sdk_instrumentation.schema_url
+ if sdk_instrumentation
+ else None,
+ )
+ )
+ json_resource_logs.append(
+ JSONResourceLogs(
+ resource=_encode_resource(sdk_resource),
+ scope_logs=scope_logs,
+ schema_url=sdk_resource.schema_url,
+ )
+ )
+
+ return json_resource_logs
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/metrics_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/metrics_encoder/__init__.py
new file mode 100644
index 0000000..f7a572c
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/metrics_encoder/__init__.py
@@ -0,0 +1,270 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+# pyright: reportCallIssue=false
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Collection
+from typing import TYPE_CHECKING
+
+from opentelemetry.exporter.otlp.json.common._internal import (
+ _encode_attributes,
+ _encode_instrumentation_scope,
+ _encode_span_id,
+ _encode_trace_id,
+)
+from opentelemetry.proto_json.collector.metrics.v1.metrics_service import (
+ ExportMetricsServiceRequest as JSONExportMetricsServiceRequest,
+)
+from opentelemetry.proto_json.metrics.v1.metrics import (
+ Exemplar as JSONExemplar,
+)
+from opentelemetry.proto_json.metrics.v1.metrics import (
+ ExponentialHistogram as JSONExponentialHistogram,
+)
+from opentelemetry.proto_json.metrics.v1.metrics import (
+ ExponentialHistogramDataPoint as JSONExponentialHistogramDataPoint,
+)
+from opentelemetry.proto_json.metrics.v1.metrics import Gauge as JSONGauge
+from opentelemetry.proto_json.metrics.v1.metrics import (
+ Histogram as JSONHistogram,
+)
+from opentelemetry.proto_json.metrics.v1.metrics import (
+ HistogramDataPoint as JSONHistogramDataPoint,
+)
+from opentelemetry.proto_json.metrics.v1.metrics import Metric as JSONMetric
+from opentelemetry.proto_json.metrics.v1.metrics import (
+ NumberDataPoint as JSONNumberDataPoint,
+)
+from opentelemetry.proto_json.metrics.v1.metrics import (
+ ResourceMetrics as JSONResourceMetrics,
+)
+from opentelemetry.proto_json.metrics.v1.metrics import (
+ ScopeMetrics as JSONScopeMetrics,
+)
+from opentelemetry.proto_json.metrics.v1.metrics import Sum as JSONSum
+from opentelemetry.proto_json.resource.v1.resource import (
+ Resource as JSONResource,
+)
+from opentelemetry.sdk.metrics import (
+ Exemplar,
+)
+from opentelemetry.sdk.metrics._internal.point import (
+ ExponentialHistogramDataPoint,
+ HistogramDataPoint,
+ Metric,
+ NumberDataPoint,
+ ResourceMetrics,
+ ScopeMetrics,
+)
+from opentelemetry.sdk.metrics.export import (
+ ExponentialHistogram,
+ Gauge,
+ Histogram,
+ MetricsData,
+ Sum,
+)
+
+if TYPE_CHECKING:
+ from typing_extensions import assert_never
+
+_logger = logging.getLogger(__name__)
+
+
+def encode_metrics(data: MetricsData) -> JSONExportMetricsServiceRequest:
+ return JSONExportMetricsServiceRequest(
+ resource_metrics=[
+ _encode_resource_metrics(rm) for rm in data.resource_metrics
+ ]
+ )
+
+
+def _encode_resource_metrics(
+ rm: ResourceMetrics,
+) -> JSONResourceMetrics:
+ return JSONResourceMetrics(
+ resource=JSONResource(
+ attributes=_encode_attributes(rm.resource.attributes)
+ ),
+ scope_metrics=[_encode_scope_metrics(sm) for sm in rm.scope_metrics],
+ schema_url=rm.resource.schema_url,
+ )
+
+
+def _encode_scope_metrics(
+ sm: ScopeMetrics,
+) -> JSONScopeMetrics:
+ return JSONScopeMetrics(
+ scope=_encode_instrumentation_scope(sm.scope),
+ schema_url=sm.scope.schema_url,
+ metrics=[_encode_metric(m) for m in sm.metrics],
+ )
+
+
+def _encode_metric(metric: Metric) -> JSONMetric:
+ json_metric = JSONMetric(
+ name=metric.name,
+ description=metric.description,
+ unit=metric.unit,
+ )
+ if isinstance(metric.data, Gauge):
+ json_metric.gauge = JSONGauge(
+ data_points=[
+ _encode_gauge_data_point(pt) for pt in metric.data.data_points
+ ]
+ )
+ elif isinstance(metric.data, Histogram):
+ json_metric.histogram = JSONHistogram(
+ data_points=[
+ _encode_histogram_data_point(pt)
+ for pt in metric.data.data_points
+ ],
+ aggregation_temporality=metric.data.aggregation_temporality,
+ )
+ elif isinstance(metric.data, Sum):
+ json_metric.sum = JSONSum(
+ data_points=[
+ _encode_sum_data_point(pt) for pt in metric.data.data_points
+ ],
+ aggregation_temporality=metric.data.aggregation_temporality,
+ is_monotonic=metric.data.is_monotonic,
+ )
+ elif isinstance(metric.data, ExponentialHistogram):
+ json_metric.exponential_histogram = JSONExponentialHistogram(
+ data_points=[
+ _encode_exponential_histogram_data_point(pt)
+ for pt in metric.data.data_points
+ ],
+ aggregation_temporality=metric.data.aggregation_temporality,
+ )
+ else:
+ if TYPE_CHECKING:
+ assert_never(metric.data)
+ _logger.warning(
+ "unsupported data type %s",
+ metric.data.__class__.__name__,
+ )
+ return json_metric
+
+
+def _encode_gauge_data_point(
+ data_point: NumberDataPoint,
+) -> JSONNumberDataPoint:
+ pt = JSONNumberDataPoint(
+ attributes=_encode_attributes(data_point.attributes),
+ time_unix_nano=data_point.time_unix_nano,
+ exemplars=_encode_exemplars(data_point.exemplars),
+ )
+ if isinstance(data_point.value, int):
+ pt.as_int = data_point.value
+ else:
+ pt.as_double = data_point.value
+ return pt
+
+
+def _encode_sum_data_point(
+ data_point: NumberDataPoint,
+) -> JSONNumberDataPoint:
+ pt = JSONNumberDataPoint(
+ attributes=_encode_attributes(data_point.attributes),
+ start_time_unix_nano=data_point.start_time_unix_nano,
+ time_unix_nano=data_point.time_unix_nano,
+ exemplars=_encode_exemplars(data_point.exemplars),
+ )
+ if isinstance(data_point.value, int):
+ pt.as_int = data_point.value
+ else:
+ pt.as_double = data_point.value
+ return pt
+
+
+def _encode_histogram_data_point(
+ data_point: HistogramDataPoint,
+) -> JSONHistogramDataPoint:
+ return JSONHistogramDataPoint(
+ attributes=_encode_attributes(data_point.attributes),
+ time_unix_nano=data_point.time_unix_nano,
+ start_time_unix_nano=data_point.start_time_unix_nano,
+ exemplars=_encode_exemplars(data_point.exemplars),
+ count=data_point.count,
+ sum=data_point.sum,
+ bucket_counts=list(data_point.bucket_counts),
+ explicit_bounds=list(data_point.explicit_bounds),
+ max=data_point.max,
+ min=data_point.min,
+ )
+
+
+def _encode_exponential_histogram_data_point(
+ data_point: ExponentialHistogramDataPoint,
+) -> JSONExponentialHistogramDataPoint:
+ return JSONExponentialHistogramDataPoint(
+ attributes=_encode_attributes(data_point.attributes),
+ time_unix_nano=data_point.time_unix_nano,
+ start_time_unix_nano=data_point.start_time_unix_nano,
+ exemplars=_encode_exemplars(data_point.exemplars),
+ count=data_point.count,
+ sum=data_point.sum,
+ scale=data_point.scale,
+ zero_count=data_point.zero_count,
+ positive=(
+ JSONExponentialHistogramDataPoint.Buckets(
+ offset=data_point.positive.offset,
+ bucket_counts=list(data_point.positive.bucket_counts),
+ )
+ if data_point.positive.bucket_counts
+ else None
+ ),
+ negative=(
+ JSONExponentialHistogramDataPoint.Buckets(
+ offset=data_point.negative.offset,
+ bucket_counts=list(data_point.negative.bucket_counts),
+ )
+ if data_point.negative.bucket_counts
+ else None
+ ),
+ flags=data_point.flags,
+ max=data_point.max,
+ min=data_point.min,
+ )
+
+
+def _encode_exemplars(
+ sdk_exemplars: Collection[Exemplar],
+) -> list[JSONExemplar]:
+ json_exemplars = []
+ for sdk_exemplar in sdk_exemplars:
+ if (
+ sdk_exemplar.span_id is not None
+ and sdk_exemplar.trace_id is not None
+ ):
+ json_exemplar = JSONExemplar(
+ time_unix_nano=sdk_exemplar.time_unix_nano,
+ span_id=_encode_span_id(sdk_exemplar.span_id),
+ trace_id=_encode_trace_id(sdk_exemplar.trace_id),
+ filtered_attributes=_encode_attributes(
+ sdk_exemplar.filtered_attributes
+ ),
+ )
+ else:
+ json_exemplar = JSONExemplar(
+ time_unix_nano=sdk_exemplar.time_unix_nano,
+ filtered_attributes=_encode_attributes(
+ sdk_exemplar.filtered_attributes
+ ),
+ )
+
+ # Assign the value based on its type in the SDK exemplar
+ if isinstance(sdk_exemplar.value, float):
+ json_exemplar.as_double = sdk_exemplar.value
+ elif isinstance(sdk_exemplar.value, int):
+ json_exemplar.as_int = sdk_exemplar.value
+ else:
+ if TYPE_CHECKING:
+ assert_never(sdk_exemplar.value)
+ raise ValueError("Exemplar value must be an int or float")
+ json_exemplars.append(json_exemplar)
+
+ return json_exemplars
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/trace_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/trace_encoder/__init__.py
new file mode 100644
index 0000000..6542c56
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_internal/trace_encoder/__init__.py
@@ -0,0 +1,173 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+# pyright: reportCallIssue=false
+
+from __future__ import annotations
+
+import logging
+from collections import defaultdict
+from collections.abc import Collection
+
+from opentelemetry.exporter.otlp.json.common._internal import (
+ _encode_attributes,
+ _encode_instrumentation_scope,
+ _encode_resource,
+ _encode_span_id,
+ _encode_trace_id,
+)
+from opentelemetry.proto_json.collector.trace.v1.trace_service import (
+ ExportTraceServiceRequest as JSONExportTraceServiceRequest,
+)
+from opentelemetry.proto_json.trace.v1.trace import (
+ ResourceSpans as JSONResourceSpans,
+)
+from opentelemetry.proto_json.trace.v1.trace import (
+ ScopeSpans as JSONScopeSpans,
+)
+from opentelemetry.proto_json.trace.v1.trace import Span as JSONSpan
+from opentelemetry.proto_json.trace.v1.trace import (
+ SpanFlags as JSONSpanFlags,
+)
+from opentelemetry.proto_json.trace.v1.trace import Status as JSONStatus
+from opentelemetry.sdk.trace import Event, ReadableSpan
+from opentelemetry.trace import Link, SpanKind
+from opentelemetry.trace.span import SpanContext, Status, TraceState
+
+# pylint: disable=E1101
+_SPAN_KIND_MAP = {
+ SpanKind.INTERNAL: JSONSpan.SpanKind.SPAN_KIND_INTERNAL,
+ SpanKind.SERVER: JSONSpan.SpanKind.SPAN_KIND_SERVER,
+ SpanKind.CLIENT: JSONSpan.SpanKind.SPAN_KIND_CLIENT,
+ SpanKind.PRODUCER: JSONSpan.SpanKind.SPAN_KIND_PRODUCER,
+ SpanKind.CONSUMER: JSONSpan.SpanKind.SPAN_KIND_CONSUMER,
+}
+
+_logger = logging.getLogger(__name__)
+
+
+def encode_spans(
+ sdk_spans: Collection[ReadableSpan],
+) -> JSONExportTraceServiceRequest:
+ return JSONExportTraceServiceRequest(
+ resource_spans=_encode_resource_spans(sdk_spans)
+ )
+
+
+def _encode_resource_spans(
+ sdk_spans: Collection[ReadableSpan],
+) -> list[JSONResourceSpans]:
+ sdk_resource_spans = defaultdict(lambda: defaultdict(list))
+
+ for sdk_span in sdk_spans:
+ sdk_resource = sdk_span.resource
+ sdk_instrumentation = sdk_span.instrumentation_scope or None
+ sdk_resource_spans[sdk_resource][sdk_instrumentation].append(
+ _encode_span(sdk_span)
+ )
+
+ json_resource_spans = []
+ for sdk_resource, sdk_instrumentations in sdk_resource_spans.items():
+ scope_spans = []
+ for sdk_instrumentation, json_spans in sdk_instrumentations.items():
+ scope_spans.append(
+ JSONScopeSpans(
+ scope=(_encode_instrumentation_scope(sdk_instrumentation)),
+ spans=json_spans,
+ schema_url=sdk_instrumentation.schema_url
+ if sdk_instrumentation
+ else None,
+ )
+ )
+ json_resource_spans.append(
+ JSONResourceSpans(
+ resource=_encode_resource(sdk_resource),
+ scope_spans=scope_spans,
+ schema_url=sdk_resource.schema_url,
+ )
+ )
+
+ return json_resource_spans
+
+
+def _span_flags(parent_span_context: SpanContext | None) -> int:
+ flags = JSONSpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK
+ if parent_span_context and parent_span_context.is_remote:
+ flags |= JSONSpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK
+ return int(flags)
+
+
+def _encode_span(sdk_span: ReadableSpan) -> JSONSpan:
+ span_context = sdk_span.get_span_context()
+ return JSONSpan(
+ trace_id=_encode_trace_id(
+ span_context.trace_id if span_context else 0
+ ),
+ span_id=_encode_span_id(span_context.span_id if span_context else 0),
+ trace_state=_encode_trace_state(
+ span_context.trace_state if span_context else None
+ ),
+ parent_span_id=_encode_context_span_id(sdk_span.parent),
+ name=sdk_span.name,
+ kind=_SPAN_KIND_MAP[sdk_span.kind],
+ start_time_unix_nano=sdk_span.start_time,
+ end_time_unix_nano=sdk_span.end_time,
+ attributes=_encode_attributes(sdk_span.attributes),
+ events=_encode_events(sdk_span.events),
+ links=_encode_links(sdk_span.links),
+ status=_encode_status(sdk_span.status),
+ dropped_attributes_count=sdk_span.dropped_attributes,
+ dropped_events_count=sdk_span.dropped_events,
+ dropped_links_count=sdk_span.dropped_links,
+ flags=_span_flags(sdk_span.parent),
+ )
+
+
+def _encode_events(
+ events: Collection[Event],
+) -> list[JSONSpan.Event]:
+ return [
+ JSONSpan.Event(
+ name=event.name,
+ time_unix_nano=event.timestamp,
+ attributes=_encode_attributes(event.attributes),
+ dropped_attributes_count=event.dropped_attributes,
+ )
+ for event in events
+ ]
+
+
+def _encode_links(links: Collection[Link]) -> list[JSONSpan.Link]:
+ return [
+ JSONSpan.Link(
+ trace_id=_encode_trace_id(link.context.trace_id),
+ span_id=_encode_span_id(link.context.span_id),
+ attributes=_encode_attributes(link.attributes),
+ dropped_attributes_count=link.dropped_attributes,
+ flags=_span_flags(link.context),
+ )
+ for link in links
+ ]
+
+
+def _encode_status(status: Status | None) -> JSONStatus | None:
+ return (
+ JSONStatus(
+ code=JSONStatus.StatusCode(status.status_code.value),
+ message=status.description,
+ )
+ if status is not None
+ else None
+ )
+
+
+def _encode_trace_state(trace_state: TraceState | None) -> str | None:
+ return (
+ ",".join([f"{key}={value}" for key, value in (trace_state.items())])
+ if trace_state is not None
+ else None
+ )
+
+
+def _encode_context_span_id(context: SpanContext | None) -> bytes | None:
+ return _encode_span_id(context.span_id) if context else None
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_log_encoder.py b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_log_encoder.py
new file mode 100644
index 0000000..ebe6f66
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/_log_encoder.py
@@ -0,0 +1,8 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+from opentelemetry.exporter.otlp.json.common._internal._log_encoder import (
+ encode_logs,
+)
+
+__all__ = ["encode_logs"]
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/metrics_encoder.py b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/metrics_encoder.py
new file mode 100644
index 0000000..1247e71
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/metrics_encoder.py
@@ -0,0 +1,8 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+from opentelemetry.exporter.otlp.json.common._internal.metrics_encoder import (
+ encode_metrics,
+)
+
+__all__ = ["encode_metrics"]
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/py.typed b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/py.typed
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/py.typed
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/trace_encoder.py b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/trace_encoder.py
new file mode 100644
index 0000000..7f17e8c
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/trace_encoder.py
@@ -0,0 +1,8 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+from opentelemetry.exporter.otlp.json.common._internal.trace_encoder import (
+ encode_spans,
+)
+
+__all__ = ["encode_spans"]
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/version/__init__.py b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/version/__init__.py
new file mode 100644
index 0000000..23888c2
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/src/opentelemetry/exporter/otlp/json/common/version/__init__.py
@@ -0,0 +1,4 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+__version__ = "0.63b1"
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/test-requirements.txt b/exporter/opentelemetry-exporter-otlp-json-common/test-requirements.txt
new file mode 100644
index 0000000..45c1ed3
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/test-requirements.txt
@@ -0,0 +1,18 @@
+asgiref==3.7.2
+iniconfig==2.0.0
+packaging==24.0
+pluggy==1.6.0
+protobuf==6.31.1
+py-cpuinfo==9.0.0
+pytest==7.4.4
+tomli==2.0.1
+typing_extensions==4.12.0
+wrapt==1.16.0
+-e opentelemetry-api
+-e opentelemetry-sdk
+-e opentelemetry-semantic-conventions
+-e tests/opentelemetry-test-utils
+-e opentelemetry-proto-json
+-e opentelemetry-proto
+-e exporter/opentelemetry-exporter-otlp-json-common
+-e exporter/opentelemetry-exporter-otlp-proto-common
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/tests/__init__.py b/exporter/opentelemetry-exporter-otlp-json-common/tests/__init__.py
new file mode 100644
index 0000000..f11d0a5
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/tests/__init__.py
@@ -0,0 +1,359 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+import dataclasses
+import unittest
+
+from opentelemetry._logs import LogRecord, SeverityNumber
+from opentelemetry.sdk._logs import ReadableLogRecord
+from opentelemetry.sdk.metrics.export import (
+ AggregationTemporality,
+ Buckets,
+ ExponentialHistogram,
+ ExponentialHistogramDataPoint,
+ Gauge,
+ Histogram,
+ HistogramDataPoint,
+ Metric,
+ MetricsData,
+ NumberDataPoint,
+ ResourceMetrics,
+ ScopeMetrics,
+ Sum,
+)
+from opentelemetry.sdk.resources import Resource
+from opentelemetry.sdk.trace import SpanContext, _Span
+from opentelemetry.sdk.util.instrumentation import InstrumentationScope
+from opentelemetry.trace import (
+ NonRecordingSpan,
+ TraceFlags,
+ set_span_in_context,
+)
+
+TRACE_ID = 0x3E0C63257DE34C926F9EFCD03927272E
+SPAN_ID = 0x34BF92DEEFC58C92
+PARENT_SPAN_ID = 0x1111111111111111
+BASE_TIME = 683647322 * 10**9
+START_TIME = 1641946015139533244
+TIME = 1641946016139533244
+
+_UNSET = object()
+
+
+def _is_none_equivalent(val_a, val_b):
+ """Check if two values should be treated as equal because one is None
+ and the other is the empty/zero default for that type.
+
+ Only None and type-matching empty defaults are considered equivalent:
+ None ~= [] (empty list)
+ None ~= "" (empty string)
+ None ~= b"" (empty bytes)
+ None ~= 0 (zero int)
+ None ~= 0.0 (zero float)
+ """
+ if val_a is None and val_b is None:
+ return True
+ if val_a is None:
+ return val_b == type(val_b)()
+ if val_b is None:
+ return val_a == type(val_a)()
+ return False
+
+
+def assert_proto_json_equal(
+ test_case: unittest.TestCase, obj_a, obj_b, path: str = ""
+):
+ """Recursively compare two proto_json dataclass objects, treating
+ None as equivalent to the type's empty default ([], "", b"", 0, 0.0)."""
+ if dataclasses.is_dataclass(obj_a) and dataclasses.is_dataclass(obj_b):
+ for field in dataclasses.fields(obj_a):
+ field_path = f"{path}.{field.name}" if path else field.name
+ val_a = getattr(obj_a, field.name)
+ val_b = getattr(obj_b, field.name)
+ assert_proto_json_equal(test_case, val_a, val_b, field_path)
+ elif isinstance(obj_a, list) and isinstance(obj_b, list):
+ test_case.assertEqual(
+ len(obj_a),
+ len(obj_b),
+ f"List length mismatch at {path}: {len(obj_a)} != {len(obj_b)}",
+ )
+ for idx, (item_a, item_b) in enumerate(zip(obj_a, obj_b)):
+ assert_proto_json_equal(
+ test_case, item_a, item_b, f"{path}[{idx}]"
+ )
+ elif _is_none_equivalent(obj_a, obj_b):
+ pass
+ else:
+ test_case.assertEqual(
+ obj_a, obj_b, f"Mismatch at {path}: {obj_a!r} != {obj_b!r}"
+ )
+
+
+def make_span_unended(
+ name="test-span",
+ trace_id=TRACE_ID,
+ span_id=SPAN_ID,
+ parent=None,
+ resource=None,
+ instrumentation_scope=None,
+ events=(),
+ links=(),
+ start_time=BASE_TIME,
+):
+ if resource is None:
+ resource = Resource({})
+ span = _Span(
+ name=name,
+ context=SpanContext(
+ trace_id,
+ span_id,
+ is_remote=False,
+ trace_flags=TraceFlags(TraceFlags.SAMPLED),
+ ),
+ parent=parent,
+ resource=resource,
+ instrumentation_scope=instrumentation_scope,
+ events=events,
+ links=links,
+ )
+ span.start(start_time=start_time)
+ return span
+
+
+def make_span(
+ name="test-span",
+ trace_id=TRACE_ID,
+ span_id=SPAN_ID,
+ parent=None,
+ resource=None,
+ instrumentation_scope=None,
+ events=(),
+ links=(),
+ start_time=BASE_TIME,
+ end_time=BASE_TIME + 50 * 10**6,
+):
+ span = make_span_unended(
+ name=name,
+ trace_id=trace_id,
+ span_id=span_id,
+ parent=parent,
+ resource=resource,
+ instrumentation_scope=instrumentation_scope,
+ events=events,
+ links=links,
+ start_time=start_time,
+ )
+ span.end(end_time=end_time)
+ return span
+
+
+def make_log_context(trace_id=TRACE_ID, span_id=SPAN_ID):
+ return set_span_in_context(
+ NonRecordingSpan(
+ SpanContext(trace_id, span_id, False, TraceFlags(0x01))
+ )
+ )
+
+
+def make_log(
+ body="test log message",
+ severity_text="INFO",
+ severity_number=SeverityNumber.INFO,
+ attributes=None,
+ timestamp=TIME,
+ observed_timestamp=TIME + 1000,
+ resource=None,
+ instrumentation_scope=_UNSET,
+ event_name=None,
+ context=None,
+ limits=None,
+):
+ kwargs = {
+ "timestamp": timestamp,
+ "observed_timestamp": observed_timestamp,
+ "severity_text": severity_text,
+ "severity_number": severity_number,
+ "body": body,
+ "attributes": attributes or {},
+ "event_name": event_name,
+ }
+ if context is not None:
+ kwargs["context"] = context
+
+ rkwargs = {
+ "resource": resource or Resource({}),
+ "instrumentation_scope": InstrumentationScope("test_scope", "1.0")
+ if instrumentation_scope is _UNSET
+ else instrumentation_scope,
+ }
+ if limits is not None:
+ rkwargs["limits"] = limits
+
+ return ReadableLogRecord(LogRecord(**kwargs), **rkwargs)
+
+
+def make_metrics_data(
+ metrics,
+ resource_attrs=None,
+ resource_schema_url=None,
+ scope_name="test_scope",
+ scope_version="1.0",
+ scope_schema_url=None,
+):
+ return MetricsData(
+ resource_metrics=[
+ ResourceMetrics(
+ resource=Resource(
+ attributes=resource_attrs or {},
+ schema_url=resource_schema_url,
+ ),
+ scope_metrics=[
+ ScopeMetrics(
+ scope=InstrumentationScope(
+ name=scope_name,
+ version=scope_version,
+ schema_url=scope_schema_url,
+ ),
+ metrics=metrics,
+ schema_url=scope_schema_url,
+ )
+ ],
+ schema_url=resource_schema_url,
+ )
+ ]
+ )
+
+
+def make_sum(
+ name="test_sum",
+ value=33,
+ attributes=None,
+ temporality=AggregationTemporality.CUMULATIVE,
+ is_monotonic=True,
+ description="desc",
+ unit="s",
+):
+ return Metric(
+ name=name,
+ description=description,
+ unit=unit,
+ data=Sum(
+ data_points=[
+ NumberDataPoint(
+ attributes=attributes or {"a": 1},
+ start_time_unix_nano=START_TIME,
+ time_unix_nano=TIME,
+ value=value,
+ )
+ ],
+ aggregation_temporality=temporality,
+ is_monotonic=is_monotonic,
+ ),
+ )
+
+
+def make_gauge(
+ name="test_gauge",
+ value=9000,
+ attributes=None,
+ description="desc",
+ unit="1",
+):
+ return Metric(
+ name=name,
+ description=description,
+ unit=unit,
+ data=Gauge(
+ data_points=[
+ NumberDataPoint(
+ attributes=attributes or {"a": 1},
+ start_time_unix_nano=None,
+ time_unix_nano=TIME,
+ value=value,
+ )
+ ],
+ ),
+ )
+
+
+def make_histogram(
+ name="test_histogram",
+ attributes=None,
+ count=5,
+ sum_value=67,
+ bucket_counts=None,
+ explicit_bounds=None,
+ min_value=8,
+ max_value=18,
+ exemplars=None,
+ temporality=AggregationTemporality.DELTA,
+ description="desc",
+ unit="ms",
+):
+ return Metric(
+ name=name,
+ description=description,
+ unit=unit,
+ data=Histogram(
+ data_points=[
+ HistogramDataPoint(
+ attributes=attributes or {"a": 1},
+ start_time_unix_nano=START_TIME,
+ time_unix_nano=TIME,
+ count=count,
+ sum=sum_value,
+ bucket_counts=bucket_counts or [1, 4],
+ explicit_bounds=explicit_bounds or [10.0, 20.0],
+ min=min_value,
+ max=max_value,
+ exemplars=exemplars or [],
+ )
+ ],
+ aggregation_temporality=temporality,
+ ),
+ )
+
+
+def make_exponential_histogram(
+ name="test_exp_hist",
+ attributes=None,
+ count=10,
+ sum_value=100.5,
+ scale=1,
+ zero_count=2,
+ positive=None,
+ negative=None,
+ flags=0,
+ min_value=1.0,
+ max_value=50.0,
+ exemplars=None,
+ temporality=AggregationTemporality.CUMULATIVE,
+ description="desc",
+ unit="s",
+):
+ return Metric(
+ name=name,
+ description=description,
+ unit=unit,
+ data=ExponentialHistogram(
+ data_points=[
+ ExponentialHistogramDataPoint(
+ attributes=attributes or {"a": 1},
+ start_time_unix_nano=START_TIME,
+ time_unix_nano=TIME,
+ count=count,
+ sum=sum_value,
+ scale=scale,
+ zero_count=zero_count,
+ positive=positive
+ or Buckets(offset=0, bucket_counts=[1, 2, 3]),
+ negative=negative or Buckets(offset=1, bucket_counts=[1]),
+ flags=flags,
+ min=min_value,
+ max=max_value,
+ exemplars=exemplars or [],
+ )
+ ],
+ aggregation_temporality=temporality,
+ ),
+ )
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/tests/test_common_encoder.py b/exporter/opentelemetry-exporter-otlp-json-common/tests/test_common_encoder.py
new file mode 100644
index 0000000..70a9e74
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/tests/test_common_encoder.py
@@ -0,0 +1,328 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+# pylint: disable=protected-access
+
+import base64
+import unittest
+from logging import ERROR
+
+from opentelemetry.exporter.otlp.json.common._internal import (
+ _encode_array,
+ _encode_attributes,
+ _encode_instrumentation_scope,
+ _encode_key_value,
+ _encode_resource,
+ _encode_span_id,
+ _encode_trace_id,
+ _encode_value,
+)
+from opentelemetry.proto_json.common.v1.common import AnyValue as JSONAnyValue
+from opentelemetry.proto_json.common.v1.common import (
+ ArrayValue as JSONArrayValue,
+)
+from opentelemetry.proto_json.common.v1.common import (
+ InstrumentationScope as JSONInstrumentationScope,
+)
+from opentelemetry.proto_json.common.v1.common import KeyValue as JSONKeyValue
+from opentelemetry.proto_json.common.v1.common import (
+ KeyValueList as JSONKeyValueList,
+)
+from opentelemetry.proto_json.resource.v1.resource import (
+ Resource as JSONResource,
+)
+from opentelemetry.sdk.resources import Resource
+from opentelemetry.sdk.util.instrumentation import InstrumentationScope
+
+
+class TestCommonEncoder(unittest.TestCase):
+ def test_encode_value(self):
+ cases = [
+ (
+ "string",
+ "hello",
+ JSONAnyValue(string_value="hello"),
+ {"stringValue": "hello"},
+ ),
+ (
+ "bool_true",
+ True,
+ JSONAnyValue(bool_value=True),
+ {"boolValue": True},
+ ),
+ (
+ "bool_false",
+ False,
+ JSONAnyValue(bool_value=False),
+ {"boolValue": False},
+ ),
+ (
+ "int",
+ 42,
+ JSONAnyValue(int_value=42),
+ {"intValue": "42"},
+ ),
+ (
+ "int_zero",
+ 0,
+ JSONAnyValue(int_value=0),
+ {"intValue": "0"},
+ ),
+ (
+ "int_negative",
+ -7,
+ JSONAnyValue(int_value=-7),
+ {"intValue": "-7"},
+ ),
+ (
+ "float",
+ 3.14,
+ JSONAnyValue(double_value=3.14),
+ {"doubleValue": 3.14},
+ ),
+ (
+ "bytes",
+ b"\x01\x02\x03",
+ JSONAnyValue(bytes_value=b"\x01\x02\x03"),
+ {
+ "bytesValue": base64.b64encode(b"\x01\x02\x03").decode(
+ "utf-8"
+ )
+ },
+ ),
+ ]
+ for name, value, expected_obj, expected_dict in cases:
+ with self.subTest(name=name):
+ result = _encode_value(value)
+ self.assertEqual(result, expected_obj)
+ self.assertEqual(result.to_dict(), expected_dict)
+
+ def test_encode_value_sequence(self):
+ result = _encode_value([1, 2, 3])
+ expected = JSONAnyValue(
+ array_value=JSONArrayValue(
+ values=[
+ JSONAnyValue(int_value=1),
+ JSONAnyValue(int_value=2),
+ JSONAnyValue(int_value=3),
+ ]
+ )
+ )
+ self.assertEqual(result, expected)
+ result_dict = result.to_dict()
+ self.assertIn("arrayValue", result_dict)
+ self.assertEqual(len(result_dict["arrayValue"]["values"]), 3)
+
+ def test_encode_value_mapping(self):
+ result = _encode_value({"key": "val", "num": 1})
+ expected = JSONAnyValue(
+ kvlist_value=JSONKeyValueList(
+ values=[
+ JSONKeyValue(
+ key="key", value=JSONAnyValue(string_value="val")
+ ),
+ JSONKeyValue(key="num", value=JSONAnyValue(int_value=1)),
+ ]
+ )
+ )
+ self.assertEqual(result, expected)
+ result_dict = result.to_dict()
+ self.assertIn("kvlistValue", result_dict)
+
+ def test_encode_value_none_not_allowed(self):
+ with self.assertRaises(TypeError):
+ _encode_value(None)
+
+ def test_encode_value_none_allowed(self):
+ result = _encode_value(None, allow_null=True)
+ self.assertIsNone(result)
+
+ def test_encode_array_with_nulls(self):
+ result = _encode_array([1, None, 2], allow_null=True)
+ self.assertEqual(
+ result,
+ [
+ JSONAnyValue(int_value=1),
+ JSONAnyValue(),
+ JSONAnyValue(int_value=2),
+ ],
+ )
+ self.assertEqual(result[1].to_dict(), {})
+
+ def test_encode_array_none_raises(self):
+ with self.assertRaises(TypeError):
+ _encode_array([1, None, 2], allow_null=False)
+
+ def test_encode_key_value(self):
+ result = _encode_key_value("mykey", "myval")
+ expected = JSONKeyValue(
+ key="mykey", value=JSONAnyValue(string_value="myval")
+ )
+ self.assertEqual(result, expected)
+ self.assertEqual(
+ result.to_dict(),
+ {"key": "mykey", "value": {"stringValue": "myval"}},
+ )
+
+ def test_encode_attributes_all_types(self):
+ result = _encode_attributes(
+ {
+ "a": 1,
+ "b": 3.14,
+ "c": False,
+ "hello": "world",
+ "greet": ["hola", "bonjour"],
+ "data": [1, 2],
+ "data_granular": [1.4, 2.4],
+ "binary_data": b"x00\x01\x02",
+ }
+ )
+ self.assertEqual(
+ result,
+ [
+ JSONKeyValue(key="a", value=JSONAnyValue(int_value=1)),
+ JSONKeyValue(key="b", value=JSONAnyValue(double_value=3.14)),
+ JSONKeyValue(key="c", value=JSONAnyValue(bool_value=False)),
+ JSONKeyValue(
+ key="hello", value=JSONAnyValue(string_value="world")
+ ),
+ JSONKeyValue(
+ key="greet",
+ value=JSONAnyValue(
+ array_value=JSONArrayValue(
+ values=[
+ JSONAnyValue(string_value="hola"),
+ JSONAnyValue(string_value="bonjour"),
+ ]
+ )
+ ),
+ ),
+ JSONKeyValue(
+ key="data",
+ value=JSONAnyValue(
+ array_value=JSONArrayValue(
+ values=[
+ JSONAnyValue(int_value=1),
+ JSONAnyValue(int_value=2),
+ ]
+ )
+ ),
+ ),
+ JSONKeyValue(
+ key="data_granular",
+ value=JSONAnyValue(
+ array_value=JSONArrayValue(
+ values=[
+ JSONAnyValue(double_value=1.4),
+ JSONAnyValue(double_value=2.4),
+ ]
+ )
+ ),
+ ),
+ JSONKeyValue(
+ key="binary_data",
+ value=JSONAnyValue(bytes_value=b"x00\x01\x02"),
+ ),
+ ],
+ )
+
+ def test_encode_attributes_empty(self):
+ self.assertEqual(_encode_attributes({}), [])
+ self.assertEqual(_encode_attributes(None), [])
+
+ def test_encode_attributes_error_skips_bad_key(self):
+ with self.assertLogs(level=ERROR) as error:
+ result = _encode_attributes({"a": 1, "bad_key": None, "b": 2})
+
+ self.assertEqual(len(error.records), 1)
+ self.assertEqual(error.records[0].msg, "Failed to encode key %s: %s")
+ self.assertEqual(error.records[0].args[0], "bad_key")
+ self.assertIsInstance(error.records[0].args[1], Exception)
+ self.assertEqual(
+ result,
+ [
+ JSONKeyValue(key="a", value=JSONAnyValue(int_value=1)),
+ JSONKeyValue(key="b", value=JSONAnyValue(int_value=2)),
+ ],
+ )
+
+ def test_encode_attributes_error_list_none(self):
+ with self.assertLogs(level=ERROR) as error:
+ result = _encode_attributes(
+ {"a": 1, "bad_key": ["test", None, "test"], "b": 2}
+ )
+
+ self.assertEqual(len(error.records), 1)
+ self.assertEqual(error.records[0].msg, "Failed to encode key %s: %s")
+ self.assertEqual(error.records[0].args[0], "bad_key")
+ self.assertIsInstance(error.records[0].args[1], Exception)
+ self.assertEqual(
+ result,
+ [
+ JSONKeyValue(key="a", value=JSONAnyValue(int_value=1)),
+ JSONKeyValue(key="b", value=JSONAnyValue(int_value=2)),
+ ],
+ )
+
+ def test_encode_span_id(self):
+ span_id = 0x1234567890ABCDEF
+ result = _encode_span_id(span_id)
+ self.assertEqual(result, b"\x12\x34\x56\x78\x90\xab\xcd\xef")
+ self.assertEqual(len(result), 8)
+
+ def test_encode_trace_id(self):
+ trace_id = 0x3E0C63257DE34C926F9EFCD03927272E
+ result = _encode_trace_id(trace_id)
+ self.assertEqual(
+ result,
+ b"\x3e\x0c\x63\x25\x7d\xe3\x4c\x92\x6f\x9e\xfc\xd0\x39\x27\x27\x2e",
+ )
+ self.assertEqual(len(result), 16)
+
+ def test_encode_resource(self):
+ resource = Resource({"key": "val"})
+ result = _encode_resource(resource)
+ expected = JSONResource(
+ attributes=[
+ JSONKeyValue(key="key", value=JSONAnyValue(string_value="val"))
+ ]
+ )
+ self.assertEqual(result, expected)
+ result_dict = result.to_dict()
+ self.assertIn("attributes", result_dict)
+ self.assertEqual(len(result_dict["attributes"]), 1)
+ self.assertEqual(
+ result_dict["attributes"][0],
+ {"key": "key", "value": {"stringValue": "val"}},
+ )
+
+ def test_encode_resource_empty(self):
+ resource = Resource({})
+ result = _encode_resource(resource)
+ self.assertEqual(result, JSONResource())
+ self.assertEqual(result.to_dict(), {})
+
+ def test_encode_instrumentation_scope(self):
+ scope = InstrumentationScope(
+ name="my_lib",
+ version="1.0.0",
+ attributes={"k": 1},
+ )
+ result = _encode_instrumentation_scope(scope)
+ expected = JSONInstrumentationScope(
+ name="my_lib",
+ version="1.0.0",
+ attributes=[
+ JSONKeyValue(key="k", value=JSONAnyValue(int_value=1))
+ ],
+ )
+ self.assertEqual(result, expected)
+ result_dict = result.to_dict()
+ self.assertEqual(result_dict["name"], "my_lib")
+ self.assertEqual(result_dict["version"], "1.0.0")
+ self.assertIn("attributes", result_dict)
+
+ def test_encode_instrumentation_scope_none(self):
+ result = _encode_instrumentation_scope(None)
+ self.assertEqual(result, JSONInstrumentationScope())
+ self.assertEqual(result.to_dict(), {})
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/tests/test_log_encoder.py b/exporter/opentelemetry-exporter-otlp-json-common/tests/test_log_encoder.py
new file mode 100644
index 0000000..12e1dfd
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/tests/test_log_encoder.py
@@ -0,0 +1,361 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+# pylint: disable=unsubscriptable-object
+
+import json
+import unittest
+
+from opentelemetry._logs import LogRecord, SeverityNumber
+from opentelemetry.exporter.otlp.json.common._internal import (
+ _encode_span_id,
+ _encode_trace_id,
+)
+from opentelemetry.exporter.otlp.json.common._log_encoder import encode_logs
+from opentelemetry.proto_json.collector.logs.v1.logs_service import (
+ ExportLogsServiceRequest as JSONExportLogsServiceRequest,
+)
+from opentelemetry.proto_json.common.v1.common import AnyValue as JSONAnyValue
+from opentelemetry.proto_json.common.v1.common import (
+ ArrayValue as JSONArrayValue,
+)
+from opentelemetry.proto_json.common.v1.common import KeyValue as JSONKeyValue
+from opentelemetry.proto_json.common.v1.common import (
+ KeyValueList as JSONKeyValueList,
+)
+from opentelemetry.sdk._logs import (
+ LogRecordLimits,
+ ReadableLogRecord,
+ ReadWriteLogRecord,
+)
+from opentelemetry.sdk.resources import Resource
+from opentelemetry.sdk.util.instrumentation import InstrumentationScope
+from opentelemetry.trace import (
+ NonRecordingSpan,
+ SpanContext,
+ TraceFlags,
+ set_span_in_context,
+)
+from tests import (
+ SPAN_ID,
+ TIME,
+ TRACE_ID,
+ assert_proto_json_equal,
+ make_log,
+ make_log_context,
+)
+
+
+def _get_first_log_record(result):
+ return result.resource_logs[0].scope_logs[0].log_records[0]
+
+
+class TestOTLPLogEncoder(unittest.TestCase):
+ def test_encode_single_log(self):
+ log = make_log()
+ result = encode_logs([log])
+
+ self.assertEqual(len(result.resource_logs), 1)
+ self.assertEqual(len(result.resource_logs[0].scope_logs), 1)
+ self.assertEqual(
+ len(result.resource_logs[0].scope_logs[0].log_records), 1
+ )
+
+ lr = _get_first_log_record(result)
+ self.assertEqual(lr.time_unix_nano, TIME)
+ self.assertEqual(lr.observed_time_unix_nano, TIME + 1000)
+ self.assertEqual(lr.severity_text, "INFO")
+ self.assertEqual(lr.severity_number, SeverityNumber.INFO.value)
+ self.assertEqual(
+ lr.body, JSONAnyValue(string_value="test log message")
+ )
+
+ def test_encode_log_with_trace_context(self):
+ ctx = make_log_context()
+ log = make_log(context=ctx)
+ result = encode_logs([log])
+ lr = _get_first_log_record(result)
+
+ self.assertEqual(lr.trace_id, _encode_trace_id(TRACE_ID))
+ self.assertEqual(lr.span_id, _encode_span_id(SPAN_ID))
+ self.assertEqual(lr.flags, int(TraceFlags(0x01)))
+
+ def test_encode_log_zero_span_trace_id(self):
+ ctx = set_span_in_context(NonRecordingSpan(SpanContext(0, 0, False)))
+ log = make_log(context=ctx)
+ result = encode_logs([log])
+ lr = _get_first_log_record(result)
+
+ self.assertIsNone(lr.span_id)
+ self.assertIsNone(lr.trace_id)
+
+ lr_dict = result.to_dict()["resourceLogs"][0]["scopeLogs"][0][
+ "logRecords"
+ ][0]
+ self.assertNotIn("traceId", lr_dict)
+ self.assertNotIn("spanId", lr_dict)
+
+ def test_encode_log_severity_numbers(self):
+ cases = [
+ ("WARN", SeverityNumber.WARN),
+ ("DEBUG", SeverityNumber.DEBUG),
+ ("INFO", SeverityNumber.INFO),
+ ("ERROR", SeverityNumber.ERROR),
+ ("FATAL", SeverityNumber.FATAL),
+ ]
+ for text, number in cases:
+ with self.subTest(severity=text):
+ log = make_log(severity_text=text, severity_number=number)
+ result = encode_logs([log])
+ lr = _get_first_log_record(result)
+ self.assertEqual(lr.severity_text, text)
+ self.assertEqual(lr.severity_number, number.value)
+
+ def test_encode_log_string_body(self):
+ log = make_log(body="hello world")
+ result = encode_logs([log])
+ lr = _get_first_log_record(result)
+ self.assertEqual(lr.body, JSONAnyValue(string_value="hello world"))
+
+ def test_encode_log_dict_body_with_nulls(self):
+ log = make_log(body={"error": None, "array_with_nones": [1, None, 2]})
+ result = encode_logs([log])
+ lr = _get_first_log_record(result)
+
+ self.assertEqual(
+ lr.body,
+ JSONAnyValue(
+ kvlist_value=JSONKeyValueList(
+ values=[
+ JSONKeyValue(key="error"),
+ JSONKeyValue(
+ key="array_with_nones",
+ value=JSONAnyValue(
+ array_value=JSONArrayValue(
+ values=[
+ JSONAnyValue(int_value=1),
+ JSONAnyValue(),
+ JSONAnyValue(int_value=2),
+ ]
+ )
+ ),
+ ),
+ ]
+ )
+ ),
+ )
+
+ def test_encode_log_no_body(self):
+ log = make_log(body=None)
+ result = encode_logs([log])
+ lr = _get_first_log_record(result)
+ self.assertIsNone(lr.body)
+
+ lr_dict = result.to_dict()["resourceLogs"][0]["scopeLogs"][0][
+ "logRecords"
+ ][0]
+ self.assertNotIn("body", lr_dict)
+
+ def test_encode_log_extended_attributes(self):
+ log = make_log(
+ attributes={
+ "extended": {"sequence": [{"inner": "mapping", "none": None}]}
+ }
+ )
+ result = encode_logs([log])
+ lr = _get_first_log_record(result)
+
+ self.assertIsNotNone(lr.attributes)
+ self.assertEqual(len(lr.attributes), 1)
+ self.assertEqual(lr.attributes[0].key, "extended")
+ self.assertIsNotNone(lr.attributes[0].value.kvlist_value)
+
+ def test_encode_log_empty_record(self):
+ ctx = make_log_context()
+ log = ReadableLogRecord(
+ LogRecord(observed_timestamp=TIME + 1000, context=ctx),
+ resource=Resource({}),
+ instrumentation_scope=InstrumentationScope("test", "1.0"),
+ )
+ result = encode_logs([log])
+ lr = _get_first_log_record(result)
+
+ self.assertIsNone(lr.time_unix_nano)
+ self.assertEqual(lr.observed_time_unix_nano, TIME + 1000)
+ self.assertIsNone(lr.severity_text)
+ self.assertIsNone(lr.severity_number)
+ self.assertIsNone(lr.body)
+ self.assertEqual(lr.attributes, [])
+
+ def test_encode_log_event_name(self):
+ log = make_log(body="event happened", event_name="my.event")
+ result = encode_logs([log])
+ lr = _get_first_log_record(result)
+ self.assertEqual(lr.event_name, "my.event")
+
+ lr_dict = result.to_dict()["resourceLogs"][0]["scopeLogs"][0][
+ "logRecords"
+ ][0]
+ self.assertEqual(lr_dict["eventName"], "my.event")
+
+ def test_dropped_attributes_count(self):
+ ctx = make_log_context()
+ # ReadWriteLogRecord applies limits via __post_init__
+ log = ReadWriteLogRecord(
+ LogRecord(
+ timestamp=TIME,
+ context=ctx,
+ severity_text="WARN",
+ severity_number=SeverityNumber.WARN,
+ body="test",
+ attributes={"a": 1, "b": "c", "user_id": "B121092"},
+ ),
+ resource=Resource({}),
+ limits=LogRecordLimits(max_attributes=1),
+ instrumentation_scope=InstrumentationScope("test", "1.0"),
+ )
+ result = encode_logs([log])
+ lr = _get_first_log_record(result)
+ self.assertEqual(lr.dropped_attributes_count, 2)
+
+ def test_encode_log_grouping_by_resource(self):
+ r1 = Resource({"service": "svc1"})
+ r2 = Resource({"service": "svc2"})
+ log1 = make_log(body="r1", resource=r1)
+ log2 = make_log(body="r2", resource=r2)
+
+ result = encode_logs([log1, log2])
+ self.assertEqual(len(result.resource_logs), 2)
+
+ groups = {}
+ # pylint: disable-next=not-an-iterable
+ for rl in result.resource_logs:
+ svc_val = rl.resource.attributes[0].value.string_value
+ bodies = [
+ lr.body.string_value
+ for sl in rl.scope_logs
+ for lr in sl.log_records
+ ]
+ groups[svc_val] = bodies
+
+ self.assertEqual(groups["svc1"], ["r1"])
+ self.assertEqual(groups["svc2"], ["r2"])
+
+ def test_encode_log_grouping_by_scope(self):
+ resource = Resource({"svc": "test"})
+ scope1 = InstrumentationScope("lib1", "1.0")
+ scope2 = InstrumentationScope("lib2", "2.0")
+
+ logs = [
+ make_log(
+ body="s1a",
+ resource=resource,
+ instrumentation_scope=scope1,
+ ),
+ make_log(
+ body="s1b",
+ resource=resource,
+ instrumentation_scope=scope1,
+ ),
+ make_log(
+ body="s2",
+ resource=resource,
+ instrumentation_scope=scope2,
+ ),
+ ]
+ result = encode_logs(logs)
+ self.assertEqual(len(result.resource_logs), 1)
+ scope_logs = result.resource_logs[0].scope_logs
+ self.assertEqual(len(scope_logs), 2)
+
+ groups = {
+ sl.scope.name: [lr.body.string_value for lr in sl.log_records]
+ for sl in scope_logs
+ }
+ self.assertEqual(groups["lib1"], ["s1a", "s1b"])
+ self.assertEqual(groups["lib2"], ["s2"])
+ self.assertEqual(scope_logs[0].scope.version, "1.0")
+ self.assertEqual(scope_logs[1].scope.version, "2.0")
+
+ def test_encode_log_scope_schema_url(self):
+ scope = InstrumentationScope("my_scope", "1.0", "schema_url_value")
+ log = make_log(instrumentation_scope=scope)
+ result = encode_logs([log])
+ self.assertEqual(
+ result.resource_logs[0].scope_logs[0].schema_url,
+ "schema_url_value",
+ )
+
+ def test_encode_log_scope_attributes(self):
+ scope = InstrumentationScope(
+ "my_scope",
+ "1.0",
+ attributes={"scope_key": 42},
+ )
+ log = make_log(instrumentation_scope=scope)
+ result = encode_logs([log])
+ encoded_scope = result.resource_logs[0].scope_logs[0].scope
+ self.assertEqual(encoded_scope.name, "my_scope")
+ self.assertEqual(len(encoded_scope.attributes), 1)
+ self.assertEqual(encoded_scope.attributes[0].key, "scope_key")
+
+ def test_encode_log_none_scope(self):
+ log = make_log(instrumentation_scope=None)
+ result = encode_logs([log])
+ encoded_scope = result.resource_logs[0].scope_logs[0].scope
+ self.assertFalse(encoded_scope.name)
+ self.assertFalse(encoded_scope.version)
+ self.assertEqual(encoded_scope.to_dict(), {})
+
+ def test_encode_logs_to_dict(self):
+ ctx = make_log_context()
+ log = make_log(context=ctx, attributes={"key": "val"})
+ result = encode_logs([log])
+ result_dict = result.to_dict()
+
+ self.assertIn("resourceLogs", result_dict)
+ lr = result_dict["resourceLogs"][0]["scopeLogs"][0]["logRecords"][0]
+
+ self.assertIsInstance(lr["traceId"], str)
+ self.assertEqual(len(lr["traceId"]), 32)
+ self.assertIsInstance(lr["spanId"], str)
+ self.assertEqual(len(lr["spanId"]), 16)
+ self.assertIsInstance(lr["timeUnixNano"], str)
+ self.assertIsInstance(lr["observedTimeUnixNano"], str)
+ self.assertIn("severityText", lr)
+ self.assertIn("severityNumber", lr)
+
+ def test_encode_logs_json_roundtrip(self):
+ ctx1 = make_log_context()
+ ctx2 = make_log_context(trace_id=12345678, span_id=87654321)
+ logs = [
+ make_log(
+ body="log with context",
+ context=ctx1,
+ attributes={"a": 1},
+ resource=Resource({"r": "v"}, "resource_schema"),
+ instrumentation_scope=InstrumentationScope(
+ "lib",
+ "1.0",
+ "scope_schema",
+ {"sk": "sv"},
+ ),
+ ),
+ make_log(
+ body={"dict_body": [1, None, 2]},
+ context=ctx2,
+ resource=Resource({}),
+ ),
+ make_log(
+ body=None,
+ severity_text="WARN",
+ severity_number=SeverityNumber.WARN,
+ event_name="my.event",
+ ),
+ ]
+ result = encode_logs(logs)
+ json_str = result.to_json()
+ roundtripped = JSONExportLogsServiceRequest.from_dict(
+ json.loads(json_str)
+ )
+ assert_proto_json_equal(self, result, roundtripped)
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/tests/test_metrics_encoder.py b/exporter/opentelemetry-exporter-otlp-json-common/tests/test_metrics_encoder.py
new file mode 100644
index 0000000..9a7e398
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/tests/test_metrics_encoder.py
@@ -0,0 +1,272 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+# pylint: disable=protected-access,unsubscriptable-object
+
+import json
+import unittest
+from logging import WARNING
+
+from opentelemetry.exporter.otlp.json.common.metrics_encoder import (
+ encode_metrics,
+)
+from opentelemetry.proto_json.collector.metrics.v1.metrics_service import (
+ ExportMetricsServiceRequest as JSONExportMetricsServiceRequest,
+)
+from opentelemetry.sdk.metrics import Exemplar
+from opentelemetry.sdk.metrics.export import (
+ AggregationTemporality,
+ Buckets,
+ Metric,
+)
+from tests import (
+ SPAN_ID,
+ TIME,
+ TRACE_ID,
+ assert_proto_json_equal,
+ make_exponential_histogram,
+ make_gauge,
+ make_histogram,
+ make_metrics_data,
+ make_sum,
+)
+
+
+def _get_first_metric(result):
+ return result.resource_metrics[0].scope_metrics[0].metrics[0]
+
+
+class TestOTLPMetricsEncoder(unittest.TestCase):
+ def test_encode_sum(self):
+ cases = [
+ ("int_value", 33, True),
+ ("double_value", 2.98, False),
+ ]
+ for name, value, is_int in cases:
+ with self.subTest(name=name):
+ result = encode_metrics(
+ make_metrics_data([make_sum(value=value)])
+ )
+ encoded = _get_first_metric(result)
+
+ self.assertIsNotNone(encoded.sum)
+ self.assertIsNone(encoded.gauge)
+ self.assertEqual(
+ encoded.sum.aggregation_temporality,
+ AggregationTemporality.CUMULATIVE,
+ )
+ self.assertTrue(encoded.sum.is_monotonic)
+
+ dp = encoded.sum.data_points[0]
+ self.assertIsNotNone(dp.start_time_unix_nano)
+ self.assertIsNotNone(dp.time_unix_nano)
+ if is_int:
+ self.assertEqual(dp.as_int, value)
+ self.assertIsNone(dp.as_double)
+ else:
+ self.assertEqual(dp.as_double, value)
+ self.assertIsNone(dp.as_int)
+
+ def test_encode_gauge(self):
+ cases = [
+ ("int_value", 9000, True),
+ ("double_value", 52.028, False),
+ ]
+ for name, value, is_int in cases:
+ with self.subTest(name=name):
+ result = encode_metrics(
+ make_metrics_data([make_gauge(value=value)])
+ )
+ encoded = _get_first_metric(result)
+
+ self.assertIsNotNone(encoded.gauge)
+ self.assertIsNone(encoded.sum)
+
+ dp = encoded.gauge.data_points[0]
+ self.assertIsNotNone(dp.time_unix_nano)
+ if is_int:
+ self.assertEqual(dp.as_int, value)
+ else:
+ self.assertEqual(dp.as_double, value)
+
+ def test_encode_histogram(self):
+ metric = make_histogram(
+ exemplars=[
+ Exemplar(
+ {"filtered": "banana"}, 298.0, TIME, SPAN_ID, TRACE_ID
+ ),
+ ],
+ )
+ result = encode_metrics(make_metrics_data([metric]))
+ encoded = _get_first_metric(result)
+
+ self.assertIsNotNone(encoded.histogram)
+ self.assertEqual(
+ encoded.histogram.aggregation_temporality,
+ AggregationTemporality.DELTA,
+ )
+
+ dp = encoded.histogram.data_points[0]
+ self.assertEqual(dp.count, 5)
+ self.assertEqual(dp.sum, 67)
+ self.assertEqual(dp.bucket_counts, [1, 4])
+ self.assertEqual(dp.explicit_bounds, [10.0, 20.0])
+ self.assertEqual(dp.min, 8)
+ self.assertEqual(dp.max, 18)
+ self.assertEqual(len(dp.exemplars), 1)
+
+ def test_encode_exponential_histogram(self):
+ result = encode_metrics(
+ make_metrics_data([make_exponential_histogram()])
+ )
+ encoded = _get_first_metric(result)
+
+ self.assertIsNotNone(encoded.exponential_histogram)
+ dp = encoded.exponential_histogram.data_points[0]
+ self.assertEqual(dp.count, 10)
+ self.assertEqual(dp.sum, 100.5)
+ self.assertEqual(dp.scale, 1)
+ self.assertEqual(dp.zero_count, 2)
+ self.assertIsNotNone(dp.positive)
+ self.assertEqual(dp.positive.offset, 0)
+ self.assertEqual(dp.positive.bucket_counts, [1, 2, 3])
+ self.assertIsNotNone(dp.negative)
+ self.assertEqual(dp.negative.offset, 1)
+ self.assertEqual(dp.negative.bucket_counts, [1])
+
+ def test_encode_exponential_histogram_empty_buckets(self):
+ metric = make_exponential_histogram(
+ attributes={},
+ count=0,
+ sum_value=0.0,
+ zero_count=0,
+ positive=Buckets(offset=0, bucket_counts=[]),
+ negative=Buckets(offset=0, bucket_counts=[]),
+ min_value=0.0,
+ max_value=0.0,
+ )
+ result = encode_metrics(make_metrics_data([metric]))
+ dp = _get_first_metric(result).exponential_histogram.data_points[0]
+ self.assertIsNone(dp.positive)
+ self.assertIsNone(dp.negative)
+
+ def test_encode_exemplars(self):
+ cases = [
+ (
+ "float_with_ids",
+ Exemplar({"f": "banana"}, 298.0, TIME, SPAN_ID, TRACE_ID),
+ True,
+ True,
+ ),
+ (
+ "float_without_ids",
+ Exemplar({"f": "banana"}, 298.0, TIME, None, None),
+ False,
+ True,
+ ),
+ (
+ "int_with_ids",
+ Exemplar({"f": "banana"}, 42, TIME, SPAN_ID, TRACE_ID),
+ True,
+ False,
+ ),
+ ]
+ for name, exemplar, has_ids, is_float in cases:
+ with self.subTest(name=name):
+ metric = make_histogram(
+ attributes={},
+ count=1,
+ sum_value=exemplar.value,
+ bucket_counts=[1],
+ explicit_bounds=[],
+ min_value=exemplar.value,
+ max_value=exemplar.value,
+ exemplars=[exemplar],
+ )
+ result = encode_metrics(make_metrics_data([metric]))
+ enc_ex = (
+ _get_first_metric(result)
+ .histogram.data_points[0]
+ .exemplars[0]
+ )
+ if has_ids:
+ self.assertTrue(enc_ex.span_id)
+ self.assertTrue(enc_ex.trace_id)
+ else:
+ self.assertFalse(enc_ex.span_id)
+ self.assertFalse(enc_ex.trace_id)
+ if is_float:
+ self.assertEqual(enc_ex.as_double, exemplar.value)
+ else:
+ self.assertEqual(enc_ex.as_int, exemplar.value)
+
+ def test_encode_metrics_resource_and_scope(self):
+ result = encode_metrics(
+ make_metrics_data(
+ [make_gauge(value=1, attributes={})],
+ resource_attrs={"service.name": "test-svc"},
+ resource_schema_url="res_schema",
+ scope_name="my_lib",
+ scope_version="2.0",
+ scope_schema_url="scope_schema",
+ )
+ )
+ rm = result.resource_metrics[0]
+ self.assertEqual(rm.schema_url, "res_schema")
+ self.assertEqual(rm.resource.attributes[0].key, "service.name")
+
+ sm = rm.scope_metrics[0]
+ self.assertEqual(sm.schema_url, "scope_schema")
+ self.assertEqual(sm.scope.name, "my_lib")
+ self.assertEqual(sm.scope.version, "2.0")
+
+ def test_encode_metrics_to_dict(self):
+ result = encode_metrics(
+ make_metrics_data([make_sum(name="sum_int", value=33)])
+ )
+ result_dict = result.to_dict()
+
+ self.assertIn("resourceMetrics", result_dict)
+ rm = result_dict["resourceMetrics"][0]
+ self.assertIn("scopeMetrics", rm)
+
+ dp = rm["scopeMetrics"][0]["metrics"][0]["sum"]["dataPoints"][0]
+ self.assertIsInstance(dp["startTimeUnixNano"], str)
+ self.assertIsInstance(dp["timeUnixNano"], str)
+ self.assertIsInstance(dp["asInt"], str)
+ self.assertEqual(dp["asInt"], "33")
+
+ def test_encode_metrics_json_roundtrip(self):
+ metrics = [
+ make_sum(name="s", value=33),
+ make_gauge(name="g", value=52.028),
+ make_histogram(
+ name="h",
+ exemplars=[
+ Exemplar({"f": "b"}, 298.0, TIME, SPAN_ID, TRACE_ID),
+ ],
+ ),
+ make_exponential_histogram(name="eh"),
+ ]
+ result = encode_metrics(make_metrics_data(metrics))
+ json_str = result.to_json()
+ roundtripped = JSONExportMetricsServiceRequest.from_dict(
+ json.loads(json_str)
+ )
+ assert_proto_json_equal(self, result, roundtripped)
+
+ def test_unsupported_metric_type(self):
+ metric = Metric(
+ name="unsupported",
+ description="foo",
+ unit="s",
+ data=None,
+ )
+ with self.assertLogs(level=WARNING):
+ result = encode_metrics(make_metrics_data([metric]))
+ encoded = _get_first_metric(result)
+ self.assertEqual(encoded.name, "unsupported")
+ self.assertIsNone(encoded.gauge)
+ self.assertIsNone(encoded.sum)
+ self.assertIsNone(encoded.histogram)
+ self.assertIsNone(encoded.exponential_histogram)
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/tests/test_proto_json_compatibility.py b/exporter/opentelemetry-exporter-otlp-json-common/tests/test_proto_json_compatibility.py
new file mode 100644
index 0000000..fb10138
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/tests/test_proto_json_compatibility.py
@@ -0,0 +1,290 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+"""Tests verifying compatibility between opentelemetry-exporter-otlp-json-common
+and protobuf's built-in JSON serialization (MessageToDict / ParseDict).
+
+OTLP JSON deviations from standard proto3 JSON mapping:
+- traceId/spanId/parentSpanId: hex-encoded (not base64)
+- Enum fields: integer values only
+- Keys: lowerCamelCase
+- 64-bit integers: string-encoded
+"""
+
+import base64
+import unittest
+from typing import Any
+
+from google.protobuf.json_format import MessageToDict, ParseDict
+
+from opentelemetry._logs import SeverityNumber
+from opentelemetry.exporter.otlp.json.common._log_encoder import (
+ encode_logs as json_encode_logs,
+)
+from opentelemetry.exporter.otlp.json.common.metrics_encoder import (
+ encode_metrics as json_encode_metrics,
+)
+from opentelemetry.exporter.otlp.json.common.trace_encoder import (
+ encode_spans as json_encode_spans,
+)
+from opentelemetry.exporter.otlp.proto.common._log_encoder import (
+ encode_logs as proto_encode_logs,
+)
+from opentelemetry.exporter.otlp.proto.common.metrics_encoder import (
+ encode_metrics as proto_encode_metrics,
+)
+from opentelemetry.exporter.otlp.proto.common.trace_encoder import (
+ encode_spans as proto_encode_spans,
+)
+from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import (
+ ExportLogsServiceRequest as PB2ExportLogsServiceRequest,
+)
+from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import (
+ ExportMetricsServiceRequest as PB2ExportMetricsServiceRequest,
+)
+from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import (
+ ExportTraceServiceRequest as PB2ExportTraceServiceRequest,
+)
+from opentelemetry.sdk.metrics import Exemplar
+from opentelemetry.sdk.resources import Resource
+from opentelemetry.sdk.trace import Event, SpanContext
+from opentelemetry.sdk.util.instrumentation import InstrumentationScope
+from opentelemetry.trace import Link
+from tests import (
+ BASE_TIME,
+ PARENT_SPAN_ID,
+ SPAN_ID,
+ TIME,
+ TRACE_ID,
+ make_exponential_histogram,
+ make_gauge,
+ make_histogram,
+ make_log,
+ make_log_context,
+ make_metrics_data,
+ make_span,
+ make_sum,
+)
+
+_HEX_ID_KEYS = frozenset({"traceId", "spanId", "parentSpanId"})
+
+
+def _normalize_otlp_json(data: Any) -> Any:
+ """Convert proto3 standard JSON (base64 IDs) to OTLP JSON (hex IDs)."""
+ if isinstance(data, list):
+ return [_normalize_otlp_json(item) for item in data]
+ if isinstance(data, dict):
+ return {
+ key: (
+ base64.b64decode(value).hex()
+ if key in _HEX_ID_KEYS and isinstance(value, str)
+ else _normalize_otlp_json(value)
+ )
+ for key, value in data.items()
+ }
+ return data
+
+
+def _denormalize_otlp_json(data: Any) -> Any:
+ """Convert OTLP JSON (hex IDs) back to proto3 standard JSON (base64 IDs)."""
+ if isinstance(data, list):
+ return [_denormalize_otlp_json(item) for item in data]
+ if isinstance(data, dict):
+ return {
+ key: (
+ base64.b64encode(bytes.fromhex(value)).decode("utf-8")
+ if key in _HEX_ID_KEYS and isinstance(value, str)
+ else _denormalize_otlp_json(value)
+ )
+ for key, value in data.items()
+ }
+ return data
+
+
+def _make_spans():
+ parent_ctx = SpanContext(TRACE_ID, PARENT_SPAN_ID, is_remote=True)
+ link_ctx = SpanContext(TRACE_ID, 0x2222222222222222, is_remote=False)
+
+ span1 = make_span(
+ name="span-with-everything",
+ parent=parent_ctx,
+ events=(
+ Event(
+ name="event1",
+ timestamp=BASE_TIME + 10 * 10**6,
+ attributes={"event_key": "event_val", "event_int": 42},
+ ),
+ ),
+ links=(Link(context=link_ctx, attributes={"link_bool": True}),),
+ resource=Resource(
+ {"service.name": "test-service"},
+ schema_url="resource_schema",
+ ),
+ instrumentation_scope=InstrumentationScope(
+ name="test_lib",
+ version="1.0.0",
+ schema_url="scope_schema",
+ ),
+ )
+
+ span2 = make_span(
+ name="bare-span",
+ span_id=0xBBBBBBBBBBBBBBBB,
+ parent=None,
+ resource=Resource({"service.name": "test-service"}),
+ start_time=BASE_TIME + 100 * 10**6,
+ end_time=BASE_TIME + 200 * 10**6,
+ )
+
+ return [span1, span2]
+
+
+def _make_test_metrics_data():
+ return make_metrics_data(
+ [
+ make_sum(
+ name="counter",
+ value=100,
+ attributes={"method": "GET"},
+ description="a counter",
+ unit="1",
+ ),
+ make_gauge(
+ name="gauge_float",
+ value=52.5,
+ attributes={"host": "localhost"},
+ description="a gauge",
+ unit="ms",
+ ),
+ make_histogram(
+ name="histogram",
+ attributes={"path": "/api"},
+ count=10,
+ sum_value=500.0,
+ bucket_counts=[2, 5, 3],
+ explicit_bounds=[100.0, 250.0],
+ min_value=10.0,
+ max_value=450.0,
+ exemplars=[
+ Exemplar(
+ {"sampled": "true"},
+ 298.0,
+ TIME,
+ SPAN_ID,
+ TRACE_ID,
+ ),
+ ],
+ description="a histogram",
+ unit="ms",
+ ),
+ make_exponential_histogram(
+ name="exp_histogram",
+ attributes={},
+ count=8,
+ sum_value=80.0,
+ scale=2,
+ zero_count=1,
+ min_value=1.0,
+ max_value=30.0,
+ description="exponential",
+ ),
+ ],
+ resource_attrs={"service.name": "metrics-svc"},
+ resource_schema_url="resource_schema",
+ scope_name="metrics_lib",
+ scope_version="2.0",
+ scope_schema_url="scope_schema",
+ )
+
+
+def _make_logs():
+ ctx = make_log_context()
+ return [
+ make_log(
+ body="something went wrong",
+ severity_text="ERROR",
+ severity_number=SeverityNumber.ERROR,
+ attributes={"error.code": 500, "path": "/api"},
+ context=ctx,
+ resource=Resource({"service.name": "log-svc"}, "resource_schema"),
+ instrumentation_scope=InstrumentationScope(
+ "log_lib", "1.0", "scope_schema"
+ ),
+ ),
+ make_log(
+ body="healthy",
+ timestamp=TIME + 2000,
+ observed_timestamp=TIME + 3000,
+ resource=Resource({"service.name": "log-svc"}),
+ instrumentation_scope=InstrumentationScope("log_lib", "1.0"),
+ ),
+ ]
+
+
+class TestProtoJsonDictCompatibility(unittest.TestCase):
+ """Compare .to_dict() from JSON encoder with MessageToDict from proto
+ encoder (after normalizing IDs)."""
+
+ def test_trace_dict_compatibility(self):
+ spans = _make_spans()
+ json_dict = json_encode_spans(spans).to_dict()
+ proto_dict = MessageToDict(
+ proto_encode_spans(spans),
+ preserving_proto_field_name=False,
+ use_integers_for_enums=True,
+ )
+ self.assertEqual(json_dict, _normalize_otlp_json(proto_dict))
+
+ def test_metrics_dict_compatibility(self):
+ data = _make_test_metrics_data()
+ json_dict = json_encode_metrics(data).to_dict()
+ proto_dict = MessageToDict(
+ proto_encode_metrics(data),
+ preserving_proto_field_name=False,
+ use_integers_for_enums=True,
+ )
+ self.assertEqual(json_dict, _normalize_otlp_json(proto_dict))
+
+ def test_log_dict_compatibility(self):
+ logs = _make_logs()
+ json_dict = json_encode_logs(logs).to_dict()
+ proto_dict = MessageToDict(
+ proto_encode_logs(logs),
+ preserving_proto_field_name=False,
+ use_integers_for_enums=True,
+ )
+ self.assertEqual(json_dict, _normalize_otlp_json(proto_dict))
+
+
+class TestProtoJsonParseCompatibility(unittest.TestCase):
+ """Encode SDK objects to dict via JSON encoder, denormalize (hex→base64
+ IDs), parse into protobuf via ParseDict, compare with proto encoder."""
+
+ def test_trace_parse_compatibility(self):
+ spans = _make_spans()
+ json_dict = json_encode_spans(spans).to_dict()
+ proto_expected = proto_encode_spans(spans)
+
+ denormalized = _denormalize_otlp_json(json_dict)
+ proto_parsed = ParseDict(denormalized, PB2ExportTraceServiceRequest())
+ self.assertEqual(proto_parsed, proto_expected)
+
+ def test_metrics_parse_compatibility(self):
+ data = _make_test_metrics_data()
+ json_dict = json_encode_metrics(data).to_dict()
+ proto_expected = proto_encode_metrics(data)
+
+ denormalized = _denormalize_otlp_json(json_dict)
+ proto_parsed = ParseDict(
+ denormalized, PB2ExportMetricsServiceRequest()
+ )
+ self.assertEqual(proto_parsed, proto_expected)
+
+ def test_log_parse_compatibility(self):
+ logs = _make_logs()
+ json_dict = json_encode_logs(logs).to_dict()
+ proto_expected = proto_encode_logs(logs)
+
+ denormalized = _denormalize_otlp_json(json_dict)
+ proto_parsed = ParseDict(denormalized, PB2ExportLogsServiceRequest())
+ self.assertEqual(proto_parsed, proto_expected)
diff --git a/exporter/opentelemetry-exporter-otlp-json-common/tests/test_trace_encoder.py b/exporter/opentelemetry-exporter-otlp-json-common/tests/test_trace_encoder.py
new file mode 100644
index 0000000..f28e9b9
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-json-common/tests/test_trace_encoder.py
@@ -0,0 +1,359 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+# pylint: disable=protected-access,unsubscriptable-object
+
+import json
+import unittest
+
+from opentelemetry.exporter.otlp.json.common._internal import (
+ _encode_span_id,
+ _encode_trace_id,
+)
+from opentelemetry.exporter.otlp.json.common._internal.trace_encoder import (
+ _SPAN_KIND_MAP,
+ _encode_context_span_id,
+ _encode_events,
+ _encode_links,
+ _encode_status,
+ _encode_trace_state,
+ _span_flags,
+)
+from opentelemetry.exporter.otlp.json.common.trace_encoder import encode_spans
+from opentelemetry.proto_json.collector.trace.v1.trace_service import (
+ ExportTraceServiceRequest as JSONExportTraceServiceRequest,
+)
+from opentelemetry.proto_json.common.v1.common import AnyValue as JSONAnyValue
+from opentelemetry.proto_json.trace.v1.trace import Span as JSONSpan
+from opentelemetry.proto_json.trace.v1.trace import (
+ SpanFlags as JSONSpanFlags,
+)
+from opentelemetry.sdk.resources import Resource
+from opentelemetry.sdk.trace import Event, SpanContext
+from opentelemetry.sdk.util.instrumentation import InstrumentationScope
+from opentelemetry.trace import Link, SpanKind
+from opentelemetry.trace.span import TraceState
+from opentelemetry.trace.status import Status, StatusCode
+from tests import (
+ BASE_TIME,
+ PARENT_SPAN_ID,
+ SPAN_ID,
+ TRACE_ID,
+ assert_proto_json_equal,
+ make_span,
+ make_span_unended,
+)
+
+
+def _get_span(result, rs_idx=0, ss_idx=0, s_idx=0):
+ return result.resource_spans[rs_idx].scope_spans[ss_idx].spans[s_idx]
+
+
+# pylint: disable=too-many-public-methods
+class TestOTLPTraceEncoder(unittest.TestCase):
+ def test_encode_single_span(self):
+ span = make_span()
+ result = encode_spans([span])
+
+ self.assertEqual(len(result.resource_spans), 1)
+ self.assertEqual(len(result.resource_spans[0].scope_spans), 1)
+ self.assertEqual(len(result.resource_spans[0].scope_spans[0].spans), 1)
+
+ encoded = _get_span(result)
+ self.assertEqual(encoded.name, "test-span")
+ self.assertEqual(encoded.kind, JSONSpan.SpanKind.SPAN_KIND_INTERNAL)
+ self.assertEqual(encoded.start_time_unix_nano, BASE_TIME)
+ self.assertEqual(encoded.end_time_unix_nano, BASE_TIME + 50 * 10**6)
+ self.assertEqual(encoded.trace_id, _encode_trace_id(TRACE_ID))
+ self.assertEqual(encoded.span_id, _encode_span_id(SPAN_ID))
+
+ def test_encode_span_attributes(self):
+ span = make_span()
+ span._attributes = {"key_bool": False, "key_str": "hello"}
+ result = encode_spans([span])
+ encoded = _get_span(result)
+
+ attr_dict = {kv.key: kv.value for kv in encoded.attributes}
+ self.assertEqual(attr_dict["key_bool"], JSONAnyValue(bool_value=False))
+ self.assertEqual(
+ attr_dict["key_str"], JSONAnyValue(string_value="hello")
+ )
+
+ def test_encode_span_events(self):
+ event = Event(
+ name="my-event",
+ timestamp=BASE_TIME + 10 * 10**6,
+ attributes={"event_key": "event_val"},
+ )
+ span = make_span(events=(event,))
+ result = encode_spans([span])
+ encoded = _get_span(result)
+
+ self.assertEqual(len(encoded.events), 1)
+ self.assertEqual(encoded.events[0].name, "my-event")
+ self.assertEqual(
+ encoded.events[0].time_unix_nano, BASE_TIME + 10 * 10**6
+ )
+ self.assertEqual(encoded.events[0].attributes[0].key, "event_key")
+
+ def test_encode_span_links(self):
+ link_ctx = SpanContext(TRACE_ID, 0x2222222222222222, is_remote=False)
+ link = Link(context=link_ctx, attributes={"link_key": True})
+ span = make_span(links=(link,))
+ result = encode_spans([span])
+ encoded = _get_span(result)
+
+ self.assertEqual(len(encoded.links), 1)
+ self.assertEqual(encoded.links[0].trace_id, _encode_trace_id(TRACE_ID))
+ self.assertEqual(
+ encoded.links[0].span_id,
+ _encode_span_id(0x2222222222222222),
+ )
+ self.assertEqual(encoded.links[0].attributes[0].key, "link_key")
+ self.assertEqual(
+ encoded.links[0].flags,
+ int(JSONSpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK),
+ )
+
+ def test_encode_span_status(self):
+ cases = [
+ ("unset", StatusCode.UNSET, None, 0),
+ ("ok", StatusCode.OK, None, 1),
+ ("error", StatusCode.ERROR, "Something broke", 2),
+ ]
+ for name, code, description, expected_code in cases:
+ with self.subTest(name=name):
+ span = make_span_unended()
+ span.set_status(Status(code, description))
+ span.end(end_time=BASE_TIME + 50 * 10**6)
+
+ result = encode_spans([span])
+ encoded = _get_span(result)
+ self.assertEqual(int(encoded.status.code), expected_code)
+ if description:
+ self.assertEqual(encoded.status.message, description)
+
+ def test_encode_span_parent(self):
+ parent_ctx = SpanContext(TRACE_ID, PARENT_SPAN_ID, is_remote=True)
+ span = make_span(parent=parent_ctx)
+ result = encode_spans([span])
+ encoded = _get_span(result)
+
+ self.assertEqual(
+ encoded.parent_span_id,
+ _encode_span_id(PARENT_SPAN_ID),
+ )
+ self.assertEqual(
+ encoded.flags,
+ int(
+ JSONSpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK
+ | JSONSpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK
+ ),
+ )
+
+ def test_encode_span_no_parent(self):
+ span = make_span(parent=None)
+ result = encode_spans([span])
+ encoded = _get_span(result)
+
+ self.assertIsNone(encoded.parent_span_id)
+ self.assertEqual(
+ encoded.flags,
+ int(JSONSpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK),
+ )
+
+ def test_encode_span_grouping_by_resource(self):
+ r1 = Resource({"svc": "svc1"})
+ r2 = Resource({"svc": "svc2"})
+ span1 = make_span(name="s1", resource=r1, span_id=0xAAAA)
+ span2 = make_span(name="s2", resource=r2, span_id=0xBBBB)
+
+ result = encode_spans([span1, span2])
+ self.assertEqual(len(result.resource_spans), 2)
+
+ groups = {}
+ # pylint: disable-next=not-an-iterable
+ for rs in result.resource_spans:
+ svc_val = rs.resource.attributes[0].value.string_value
+ span_names = [s.name for ss in rs.scope_spans for s in ss.spans]
+ groups[svc_val] = span_names
+
+ self.assertEqual(groups["svc1"], ["s1"])
+ self.assertEqual(groups["svc2"], ["s2"])
+
+ def test_encode_span_grouping_by_scope(self):
+ resource = Resource({"svc": "test"})
+ scope1 = InstrumentationScope(name="lib1", version="1.0")
+ scope2 = InstrumentationScope(name="lib2", version="2.0")
+ span1 = make_span(
+ name="s1",
+ resource=resource,
+ instrumentation_scope=scope1,
+ span_id=0xAAAA,
+ )
+ span2 = make_span(
+ name="s2",
+ resource=resource,
+ instrumentation_scope=scope2,
+ span_id=0xBBBB,
+ )
+
+ result = encode_spans([span1, span2])
+ self.assertEqual(len(result.resource_spans), 1)
+ scope_spans = result.resource_spans[0].scope_spans
+ self.assertEqual(len(scope_spans), 2)
+
+ groups = {
+ ss.scope.name: [s.name for s in ss.spans] for ss in scope_spans
+ }
+ self.assertEqual(groups["lib1"], ["s1"])
+ self.assertEqual(groups["lib2"], ["s2"])
+ self.assertEqual(scope_spans[0].scope.version, "1.0")
+ self.assertEqual(scope_spans[1].scope.version, "2.0")
+
+ def test_encode_span_schema_urls(self):
+ resource = Resource({"key": "val"}, schema_url="resource_schema")
+ scope = InstrumentationScope(
+ name="lib",
+ version="1.0",
+ schema_url="scope_schema",
+ )
+ span = make_span(resource=resource, instrumentation_scope=scope)
+ result = encode_spans([span])
+
+ self.assertEqual(
+ result.resource_spans[0].schema_url, "resource_schema"
+ )
+ self.assertEqual(
+ result.resource_spans[0].scope_spans[0].schema_url,
+ "scope_schema",
+ )
+
+ def test_encode_span_scope_attributes(self):
+ scope = InstrumentationScope(
+ name="lib",
+ version="1.0",
+ attributes={"scope_key": "scope_val"},
+ )
+ span = make_span(instrumentation_scope=scope)
+ result = encode_spans([span])
+
+ encoded_scope = result.resource_spans[0].scope_spans[0].scope
+ self.assertEqual(encoded_scope.name, "lib")
+ self.assertEqual(encoded_scope.version, "1.0")
+ self.assertEqual(len(encoded_scope.attributes), 1)
+ self.assertEqual(encoded_scope.attributes[0].key, "scope_key")
+
+ def test_span_kind_map(self):
+ expected = [
+ (SpanKind.INTERNAL, JSONSpan.SpanKind.SPAN_KIND_INTERNAL),
+ (SpanKind.SERVER, JSONSpan.SpanKind.SPAN_KIND_SERVER),
+ (SpanKind.CLIENT, JSONSpan.SpanKind.SPAN_KIND_CLIENT),
+ (SpanKind.PRODUCER, JSONSpan.SpanKind.SPAN_KIND_PRODUCER),
+ (SpanKind.CONSUMER, JSONSpan.SpanKind.SPAN_KIND_CONSUMER),
+ ]
+ for sdk_kind, json_kind in expected:
+ with self.subTest(kind=sdk_kind.name):
+ self.assertEqual(_SPAN_KIND_MAP[sdk_kind], json_kind)
+
+ def test_span_flags(self):
+ cases = [
+ (
+ "remote_parent",
+ SpanContext(TRACE_ID, 0x1111, is_remote=True),
+ int(
+ JSONSpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK
+ | JSONSpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK
+ ),
+ ),
+ (
+ "local_parent",
+ SpanContext(TRACE_ID, 0x2222, is_remote=False),
+ int(JSONSpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK),
+ ),
+ (
+ "no_parent",
+ None,
+ int(JSONSpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK),
+ ),
+ ]
+ for name, context, expected_flags in cases:
+ with self.subTest(name=name):
+ self.assertEqual(_span_flags(context), expected_flags)
+
+ def test_encode_status_translations(self):
+ cases = [
+ ("unset", StatusCode.UNSET, None),
+ ("ok", StatusCode.OK, None),
+ ("error", StatusCode.ERROR, "desc"),
+ ]
+ for name, code, desc in cases:
+ with self.subTest(name=name):
+ result = _encode_status(Status(code, desc))
+ self.assertEqual(
+ int(result.code),
+ code.value,
+ )
+ if desc:
+ self.assertEqual(result.message, desc)
+
+ def test_encode_trace_state(self):
+ ts = TraceState([("key1", "val1"), ("key2", "val2")])
+ self.assertEqual(_encode_trace_state(ts), "key1=val1,key2=val2")
+
+ def test_encode_trace_state_none(self):
+ self.assertIsNone(_encode_trace_state(None))
+
+ def test_encode_events_empty(self):
+ self.assertEqual(_encode_events(()), [])
+ self.assertEqual(_encode_events([]), [])
+
+ def test_encode_links_empty(self):
+ self.assertEqual(_encode_links(()), [])
+ self.assertEqual(_encode_links([]), [])
+
+ def test_encode_parent_id(self):
+ ctx = SpanContext(TRACE_ID, 0xABCDEF1234567890, is_remote=True)
+ self.assertEqual(
+ _encode_context_span_id(ctx),
+ _encode_span_id(0xABCDEF1234567890),
+ )
+
+ def test_encode_parent_id_none(self):
+ self.assertIsNone(_encode_context_span_id(None))
+
+ def test_encode_spans_to_dict(self):
+ span = make_span()
+ span.set_attribute("key", "value")
+ result = encode_spans([span])
+ result_dict = result.to_dict()
+
+ self.assertIn("resourceSpans", result_dict)
+ sp = result_dict["resourceSpans"][0]["scopeSpans"][0]["spans"][0]
+
+ self.assertIsInstance(sp["traceId"], str)
+ self.assertEqual(len(sp["traceId"]), 32)
+ self.assertIsInstance(sp["spanId"], str)
+ self.assertEqual(len(sp["spanId"]), 16)
+ self.assertIsInstance(sp["startTimeUnixNano"], str)
+ self.assertIsInstance(sp["endTimeUnixNano"], str)
+
+ def test_encode_spans_json_roundtrip(self):
+ parent_ctx = SpanContext(TRACE_ID, PARENT_SPAN_ID, is_remote=True)
+ spans = [
+ make_span(name="span1", parent=parent_ctx),
+ make_span(
+ name="span2",
+ span_id=0xBBBB,
+ resource=Resource({"r": "v"}),
+ instrumentation_scope=InstrumentationScope(
+ "lib", "1.0", attributes={"sk": 1}
+ ),
+ ),
+ ]
+ result = encode_spans(spans)
+ json_str = result.to_json()
+ roundtripped = JSONExportTraceServiceRequest.from_dict(
+ json.loads(json_str)
+ )
+ assert_proto_json_equal(self, result, roundtripped)
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/pyproject.toml b/exporter/opentelemetry-exporter-otlp-proto-common/pyproject.toml
index d9de7eb..2fd934d 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/pyproject.toml
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenTelemetry Protobuf encoding"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -19,7 +19,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -27,7 +26,7 @@ classifiers = [
"Programming Language :: Python :: 3.14",
]
dependencies = [
- "opentelemetry-proto == 1.41.0",
+ "opentelemetry-proto == 1.42.1",
]
[project.urls]
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/__init__.py
index 2d336ae..7ad07b1 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.exporter.otlp.proto.common.version import __version__
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_exporter_metrics.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_exporter_metrics.py
new file mode 100644
index 0000000..6b3d1d7
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_exporter_metrics.py
@@ -0,0 +1,153 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+from __future__ import annotations
+
+from collections import Counter
+from collections.abc import Iterator
+from contextlib import AbstractContextManager, contextmanager
+from dataclasses import dataclass
+from time import perf_counter
+from typing import TYPE_CHECKING, Protocol
+
+from opentelemetry.metrics import MeterProvider, get_meter_provider
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OTEL_COMPONENT_NAME,
+ OTEL_COMPONENT_TYPE,
+ OtelComponentTypeValues,
+)
+from opentelemetry.semconv._incubating.metrics.otel_metrics import (
+ create_otel_sdk_exporter_log_exported,
+ create_otel_sdk_exporter_log_inflight,
+ create_otel_sdk_exporter_metric_data_point_exported,
+ create_otel_sdk_exporter_metric_data_point_inflight,
+ create_otel_sdk_exporter_operation_duration,
+ create_otel_sdk_exporter_span_exported,
+ create_otel_sdk_exporter_span_inflight,
+)
+from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
+from opentelemetry.semconv.attributes.server_attributes import (
+ SERVER_ADDRESS,
+ SERVER_PORT,
+)
+
+if TYPE_CHECKING:
+ from typing import Literal
+ from urllib.parse import ParseResult as UrlParseResult
+
+ from opentelemetry.util.types import Attributes, AttributeValue
+
+_component_counter = Counter()
+
+
+@dataclass
+class ExportResult:
+ error: Exception | None = None
+ error_attrs: Attributes = None
+
+
+class ExporterMetricsT(Protocol):
+ def export_operation(
+ self, num_items: int
+ ) -> AbstractContextManager[ExportResult]: ...
+
+
+class NoOpExporterMetrics:
+ @contextmanager
+ def export_operation(self, num_items: int) -> Iterator[ExportResult]:
+ yield ExportResult()
+
+
+class ExporterMetrics:
+ def __init__(
+ self,
+ component_type: OtelComponentTypeValues | None,
+ signal: Literal["traces", "metrics", "logs"],
+ endpoint: UrlParseResult,
+ meter_provider: MeterProvider | None,
+ ) -> None:
+ if signal == "traces":
+ create_exported = create_otel_sdk_exporter_span_exported
+ create_inflight = create_otel_sdk_exporter_span_inflight
+ elif signal == "logs":
+ create_exported = create_otel_sdk_exporter_log_exported
+ create_inflight = create_otel_sdk_exporter_log_inflight
+ else:
+ create_exported = (
+ create_otel_sdk_exporter_metric_data_point_exported
+ )
+ create_inflight = (
+ create_otel_sdk_exporter_metric_data_point_inflight
+ )
+
+ port = endpoint.port
+ if port is None:
+ if endpoint.scheme == "https":
+ port = 443
+ elif endpoint.scheme == "http":
+ port = 80
+
+ component_type_value = (
+ component_type.value if component_type else "unknown_otlp_exporter"
+ )
+ count = _component_counter[component_type_value]
+ _component_counter[component_type_value] = count + 1
+ self._standard_attrs: dict[str, AttributeValue] = {
+ OTEL_COMPONENT_TYPE: component_type_value,
+ OTEL_COMPONENT_NAME: f"{component_type_value}/{count}",
+ }
+ if endpoint.hostname:
+ self._standard_attrs[SERVER_ADDRESS] = endpoint.hostname
+ if port is not None:
+ self._standard_attrs[SERVER_PORT] = port
+
+ meter_provider = meter_provider or get_meter_provider()
+ meter = meter_provider.get_meter("opentelemetry-sdk")
+ self._inflight = create_inflight(meter)
+ self._exported = create_exported(meter)
+ self._duration = create_otel_sdk_exporter_operation_duration(meter)
+
+ @contextmanager
+ def export_operation(self, num_items: int) -> Iterator[ExportResult]:
+ start_time = perf_counter()
+ self._inflight.add(num_items, self._standard_attrs)
+
+ result = ExportResult()
+ try:
+ yield result
+ finally:
+ error = result.error
+ error_attrs = result.error_attrs
+
+ end_time = perf_counter()
+ self._inflight.add(-num_items, self._standard_attrs)
+ exported_attrs = (
+ {**self._standard_attrs, ERROR_TYPE: type(error).__qualname__}
+ if error
+ else self._standard_attrs
+ )
+ self._exported.add(num_items, exported_attrs)
+ duration_attrs = (
+ {**exported_attrs, **error_attrs}
+ if error_attrs
+ else exported_attrs
+ )
+ self._duration.record(end_time - start_time, duration_attrs)
+
+
+def create_exporter_metrics(
+ component_type: OtelComponentTypeValues | None,
+ signal: Literal["traces", "metrics", "logs"],
+ endpoint: UrlParseResult,
+ meter_provider: MeterProvider | None,
+ enabled: bool,
+) -> ExporterMetricsT:
+ if not enabled:
+ return NoOpExporterMetrics()
+
+ return ExporterMetrics(
+ component_type,
+ signal,
+ endpoint,
+ meter_provider,
+ )
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py
index f1abcfc..8dbbc21 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py
@@ -1,29 +1,13 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
import logging
-from collections.abc import Sequence
+from collections.abc import Callable, Mapping, Sequence
from typing import (
Any,
- Callable,
- Dict,
- List,
- Mapping,
- Optional,
TypeVar,
)
@@ -67,9 +51,7 @@ def _encode_resource(resource: Resource) -> PB2Resource:
return PB2Resource(attributes=_encode_attributes(resource.attributes))
-def _encode_value(
- value: Any, allow_null: bool = False
-) -> Optional[PB2AnyValue]:
+def _encode_value(value: Any, allow_null: bool = False) -> PB2AnyValue | None:
if allow_null is True and value is None:
return None
if isinstance(value, bool):
@@ -136,7 +118,7 @@ def _encode_trace_id(trace_id: int) -> bytes:
def _encode_attributes(
attributes: _ExtendedAttributes,
allow_null: bool = False,
-) -> Optional[List[PB2KeyValue]]:
+) -> list[PB2KeyValue] | None:
if attributes:
pb2_attributes = []
for key, value in attributes.items():
@@ -153,10 +135,10 @@ def _encode_attributes(
def _get_resource_data(
- sdk_resource_scope_data: Dict[Resource, _ResourceDataT],
+ sdk_resource_scope_data: dict[Resource, _ResourceDataT],
resource_class: Callable[..., _TypingResourceT],
name: str,
-) -> List[_TypingResourceT]:
+) -> list[_TypingResourceT]:
resource_data = []
for (
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py
index e04ee95..453b4e7 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py
@@ -1,18 +1,7 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from collections import defaultdict
-from typing import List, Sequence
+from collections.abc import Sequence
from opentelemetry.exporter.otlp.proto.common._internal import (
_encode_attributes,
@@ -72,7 +61,7 @@ def _encode_log(readable_log_record: ReadableLogRecord) -> PB2LogRecord:
def _encode_resource_logs(
batch: Sequence[ReadableLogRecord],
-) -> List[ResourceLogs]:
+) -> list[ResourceLogs]:
sdk_resource_logs = defaultdict(lambda: defaultdict(list))
for readable_log in batch:
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py
index 3ea2e26..fad7aaf 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py
@@ -1,21 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
import logging
from os import environ
-from typing import Dict, List
from opentelemetry.exporter.otlp.proto.common._internal import (
_encode_attributes,
@@ -79,8 +67,8 @@ class OTLPMetricExporterMixin:
)
def _get_temporality(
- self, preferred_temporality: Dict[type, AggregationTemporality]
- ) -> Dict[type, AggregationTemporality]:
+ self, preferred_temporality: dict[type, AggregationTemporality]
+ ) -> dict[type, AggregationTemporality]:
otel_exporter_otlp_metrics_temporality_preference = (
environ.get(
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE,
@@ -136,8 +124,8 @@ class OTLPMetricExporterMixin:
def _get_aggregation(
self,
- preferred_aggregation: Dict[type, Aggregation],
- ) -> Dict[type, Aggregation]:
+ preferred_aggregation: dict[type, Aggregation],
+ ) -> dict[type, Aggregation]:
otel_exporter_otlp_metrics_default_histogram_aggregation = environ.get(
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION,
"explicit_bucket_histogram",
@@ -344,7 +332,7 @@ def _encode_metric(metric, pb2_metric):
)
-def _encode_exemplars(sdk_exemplars: List[Exemplar]) -> List[pb2.Exemplar]:
+def _encode_exemplars(sdk_exemplars: list[Exemplar]) -> list[pb2.Exemplar]:
"""
Converts a list of SDK Exemplars into a list of protobuf Exemplars.
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py
index 388d229..96276a3 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import logging
from collections import defaultdict
-from typing import List, Optional, Sequence
+from collections.abc import Sequence
from opentelemetry.exporter.otlp.proto.common._internal import (
_encode_attributes,
@@ -59,7 +48,7 @@ def encode_spans(
def _encode_resource_spans(
sdk_spans: Sequence[ReadableSpan],
-) -> List[PB2ResourceSpans]:
+) -> list[PB2ResourceSpans]:
# We need to inspect the spans and group + structure them as:
#
# Resource
@@ -105,7 +94,7 @@ def _encode_resource_spans(
return pb2_resource_spans
-def _span_flags(parent_span_context: Optional[SpanContext]) -> int:
+def _span_flags(parent_span_context: SpanContext | None) -> int:
flags = PB2SpanFlags.SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK
if parent_span_context and parent_span_context.is_remote:
flags |= PB2SpanFlags.SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK
@@ -136,7 +125,7 @@ def _encode_span(sdk_span: ReadableSpan) -> PB2SPan:
def _encode_events(
events: Sequence[Event],
-) -> Optional[List[PB2SPan.Event]]:
+) -> list[PB2SPan.Event] | None:
pb2_events = None
if events:
pb2_events = []
@@ -167,7 +156,7 @@ def _encode_links(links: Sequence[Link]) -> Sequence[PB2SPan.Link]:
return pb2_links
-def _encode_status(status: Status) -> Optional[PB2Status]:
+def _encode_status(status: Status) -> PB2Status | None:
pb2_status = None
if status is not None:
pb2_status = PB2Status(
@@ -177,7 +166,7 @@ def _encode_status(status: Status) -> Optional[PB2Status]:
return pb2_status
-def _encode_trace_state(trace_state: TraceState) -> Optional[str]:
+def _encode_trace_state(trace_state: TraceState) -> str | None:
pb2_trace_state = None
if trace_state is not None:
pb2_trace_state = ",".join(
@@ -186,7 +175,7 @@ def _encode_trace_state(trace_state: TraceState) -> Optional[str]:
return pb2_trace_state
-def _encode_parent_id(context: Optional[SpanContext]) -> Optional[bytes]:
+def _encode_parent_id(context: SpanContext | None) -> bytes | None:
if context:
return _encode_span_id(context.span_id)
return None
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_log_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_log_encoder.py
index f34ff82..e55fa05 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_log_encoder.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_log_encoder.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.exporter.otlp.proto.common._internal._log_encoder import (
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/metrics_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/metrics_encoder.py
index 14f8fc3..be1c83a 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/metrics_encoder.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/metrics_encoder.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.exporter.otlp.proto.common._internal.metrics_encoder import (
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/trace_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/trace_encoder.py
index 2af5765..590ea7d 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/trace_encoder.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/trace_encoder.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.exporter.otlp.proto.common._internal.trace_encoder import (
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/version/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/version/__init__.py
index 49e1254..a400b9b 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/version/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.0"
+__version__ = "1.42.1"
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/test-requirements.txt b/exporter/opentelemetry-exporter-otlp-proto-common/test-requirements.txt
index 1fa93a7..fbb33f0 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/test-requirements.txt
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/test-requirements.txt
@@ -1,5 +1,4 @@
asgiref==3.7.2
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
@@ -9,7 +8,6 @@ pytest==7.4.4
tomli==2.0.1
typing_extensions==4.12.0
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e opentelemetry-sdk
-e opentelemetry-semantic-conventions
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py
index 5ffa11d..58ce2b3 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
from logging import ERROR
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_exporter_metrics.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_exporter_metrics.py
new file mode 100644
index 0000000..e6c1a60
--- /dev/null
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_exporter_metrics.py
@@ -0,0 +1,59 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+import unittest
+from unittest.mock import Mock, patch
+from urllib.parse import urlparse
+
+from opentelemetry.exporter.otlp.proto.common._exporter_metrics import (
+ ExporterMetrics,
+ NoOpExporterMetrics,
+ create_exporter_metrics,
+)
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
+
+
+class TestExporterMetrics(unittest.TestCase):
+ def test_factory_returns_noop_when_disabled(self):
+ meter_provider = Mock()
+
+ with patch(
+ "opentelemetry.exporter.otlp.proto.common."
+ "_exporter_metrics.get_meter_provider"
+ ) as get_meter_provider:
+ metrics = create_exporter_metrics(
+ OtelComponentTypeValues.OTLP_HTTP_SPAN_EXPORTER,
+ "traces",
+ urlparse("http://localhost:4318/v1/traces"),
+ meter_provider,
+ False,
+ )
+
+ self.assertIsInstance(metrics, NoOpExporterMetrics)
+ meter_provider.get_meter.assert_not_called()
+ get_meter_provider.assert_not_called()
+
+ def test_factory_returns_exporter_metrics_when_enabled(self):
+ meter_provider = Mock()
+ meter_provider.get_meter.return_value = Mock()
+
+ metrics = create_exporter_metrics(
+ OtelComponentTypeValues.OTLP_HTTP_SPAN_EXPORTER,
+ "traces",
+ urlparse("http://localhost:4318/v1/traces"),
+ meter_provider,
+ True,
+ )
+
+ self.assertIsInstance(metrics, ExporterMetrics)
+ meter_provider.get_meter.assert_called_once_with("opentelemetry-sdk")
+
+ def test_noop_export_operation_yields_result(self):
+ metrics = NoOpExporterMetrics()
+
+ with metrics.export_operation(1) as result:
+ result.error = RuntimeError("error")
+
+ self.assertIsInstance(result.error, RuntimeError)
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py
index ef14727..a84134f 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py
@@ -1,19 +1,7 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
-from typing import List, Tuple
from opentelemetry._logs import LogRecord, SeverityNumber
from opentelemetry.exporter.otlp.proto.common._internal import (
@@ -87,7 +75,7 @@ class TestOTLPLogEncoder(unittest.TestCase):
)
@staticmethod
- def _get_sdk_log_data() -> List[ReadWriteLogRecord]:
+ def _get_sdk_log_data() -> list[ReadWriteLogRecord]:
# pylint:disable=too-many-locals
ctx_log1 = set_span_in_context(
NonRecordingSpan(
@@ -330,7 +318,7 @@ class TestOTLPLogEncoder(unittest.TestCase):
def get_test_logs(
self,
- ) -> Tuple[List[ReadWriteLogRecord], ExportLogsServiceRequest]:
+ ) -> tuple[list[ReadWriteLogRecord], ExportLogsServiceRequest]:
sdk_logs = self._get_sdk_log_data()
pb2_service_request = ExportLogsServiceRequest(
@@ -646,7 +634,7 @@ class TestOTLPLogEncoder(unittest.TestCase):
return sdk_logs, pb2_service_request
@staticmethod
- def _get_test_logs_dropped_attributes() -> List[ReadWriteLogRecord]:
+ def _get_test_logs_dropped_attributes() -> list[ReadWriteLogRecord]:
ctx_log1 = set_span_in_context(
NonRecordingSpan(
SpanContext(
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_metrics_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_metrics_encoder.py
index d2ef292..d3b3dc8 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_metrics_encoder.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_metrics_encoder.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access,too-many-lines
import unittest
diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py
index bf78526..0c68594 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py
@@ -1,21 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access
import unittest
-from typing import List, Tuple
from opentelemetry.exporter.otlp.proto.common._internal import (
_encode_span_id,
@@ -63,7 +51,7 @@ class TestOTLPTraceEncoder(unittest.TestCase):
self.assertEqual(encode_spans(otel_spans), expected_encoding)
@staticmethod
- def get_exhaustive_otel_span_list() -> List[SDKSpan]:
+ def get_exhaustive_otel_span_list() -> list[SDKSpan]:
trace_id = 0x3E0C63257DE34C926F9EFCD03927272E
base_time = 683647322 * 10**9 # in ns
@@ -194,7 +182,7 @@ class TestOTLPTraceEncoder(unittest.TestCase):
def get_exhaustive_test_spans(
self,
- ) -> Tuple[List[SDKSpan], PB2ExportTraceServiceRequest]:
+ ) -> tuple[list[SDKSpan], PB2ExportTraceServiceRequest]:
otel_spans = self.get_exhaustive_otel_span_list()
trace_id = _encode_trace_id(otel_spans[0].context.trace_id)
span_kind = _SPAN_KIND_MAP[SDKSpanKind.INTERNAL]
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/benchmarks/test_benchmark_trace_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/benchmarks/test_benchmark_trace_exporter.py
index 9051dbe..db583f6 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/benchmarks/test_benchmark_trace_exporter.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/benchmarks/test_benchmark_trace_exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=invalid-name
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/pyproject.toml b/exporter/opentelemetry-exporter-otlp-proto-grpc/pyproject.toml
index fefbff7..ea7338c 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/pyproject.toml
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenTelemetry Collector Protobuf over gRPC Exporter"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -19,7 +19,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -32,9 +31,9 @@ dependencies = [
"grpcio >= 1.66.2, < 2.0.0; python_version == '3.13'",
"grpcio >= 1.75.1, < 2.0.0; python_version >= '3.14'",
"opentelemetry-api ~= 1.15",
- "opentelemetry-proto == 1.41.0",
- "opentelemetry-sdk ~= 1.41.0",
- "opentelemetry-exporter-otlp-proto-common == 1.41.0",
+ "opentelemetry-proto == 1.42.1",
+ "opentelemetry-sdk ~= 1.42.1",
+ "opentelemetry-exporter-otlp-proto-common == 1.42.1",
"typing-extensions >= 4.6.0",
]
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/__init__.py
index 93972cc..590d4b8 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py
index 63d8ac9..6afc133 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py
@@ -1,27 +1,19 @@
# Copyright The OpenTelemetry Authors
-# 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.
+# SPDX-License-Identifier: Apache-2.0
+from collections.abc import Iterable, Sequence
+from collections.abc import Sequence as TypingSequence
from os import environ
-from typing import Dict, Literal, Optional, Sequence, Tuple, Union
-from typing import Sequence as TypingSequence
+from typing import Literal
-from grpc import ChannelCredentials, Compression
+from grpc import ChannelCredentials, Compression, StatusCode
from opentelemetry.exporter.otlp.proto.common._log_encoder import encode_logs
from opentelemetry.exporter.otlp.proto.grpc.exporter import (
OTLPExporterMixin,
_get_credentials,
environ_to_compression,
)
+from opentelemetry.metrics import MeterProvider
from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import (
ExportLogsServiceRequest,
)
@@ -44,6 +36,9 @@ from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_LOGS_INSECURE,
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
)
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
class OTLPLogExporter(
@@ -57,15 +52,19 @@ class OTLPLogExporter(
):
def __init__(
self,
- endpoint: Optional[str] = None,
- insecure: Optional[bool] = None,
- credentials: Optional[ChannelCredentials] = None,
- headers: Optional[
- Union[TypingSequence[Tuple[str, str]], Dict[str, str], str]
- ] = None,
- timeout: Optional[float] = None,
- compression: Optional[Compression] = None,
- channel_options: Optional[Tuple[Tuple[str, str]]] = None,
+ endpoint: str | None = None,
+ insecure: bool | None = None,
+ credentials: ChannelCredentials | None = None,
+ headers: TypingSequence[tuple[str, str]]
+ | dict[str, str]
+ | str
+ | None = None,
+ timeout: float | None = None,
+ compression: Compression | None = None,
+ channel_options: tuple[tuple[str, str]] | None = None,
+ retryable_error_codes: Iterable[StatusCode] | None = None,
+ *,
+ meter_provider: MeterProvider | None = None,
):
insecure_logs = environ.get(OTEL_EXPORTER_OTLP_LOGS_INSECURE)
if insecure is None and insecure_logs is not None:
@@ -105,6 +104,10 @@ class OTLPLogExporter(
stub=LogsServiceStub,
result=LogRecordExportResult,
channel_options=channel_options,
+ retryable_error_codes=retryable_error_codes,
+ component_type=OtelComponentTypeValues.OTLP_GRPC_LOG_EXPORTER,
+ signal="logs",
+ meter_provider=meter_provider,
)
def _translate_data(
@@ -112,6 +115,9 @@ class OTLPLogExporter(
) -> ExportLogsServiceRequest:
return encode_logs(data)
+ def _count_data(self, data: Sequence[ReadableLogRecord]):
+ return len(data)
+
def export( # type: ignore [reportIncompatibleMethodOverride]
self,
batch: Sequence[ReadableLogRecord],
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py
index d52f61c..5b79a95 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""OTLP Exporter
@@ -20,28 +9,28 @@ logic to handle transient collector outages.
"""
+import os
import random
import threading
from abc import ABC, abstractmethod
-from collections.abc import Sequence # noqa: F401
+from collections.abc import (
+ Callable,
+ Iterable,
+ Sequence, # noqa: F401
+)
+from collections.abc import Sequence as TypingSequence
from logging import getLogger
from os import environ
from time import time
from typing import ( # noqa: F401
Any,
- Callable,
- Dict,
Generic,
- List,
Literal,
NewType,
Optional,
- Tuple,
- Type,
TypeVar,
Union,
)
-from typing import Sequence as TypingSequence
from urllib.parse import urlparse
from google.rpc.error_details_pb2 import RetryInfo
@@ -56,12 +45,16 @@ from grpc import (
secure_channel,
ssl_channel_credentials,
)
+from opentelemetry.exporter.otlp.proto.common._exporter_metrics import (
+ create_exporter_metrics,
+)
from opentelemetry.exporter.otlp.proto.common._internal import (
_get_resource_data,
)
from opentelemetry.exporter.otlp.proto.grpc import (
_OTLP_GRPC_CHANNEL_OPTIONS,
)
+from opentelemetry.metrics import MeterProvider
from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import (
ExportLogsServiceRequest,
)
@@ -91,6 +84,7 @@ from opentelemetry.sdk._logs.export import LogRecordExportResult
from opentelemetry.sdk._shared_internal import DuplicateFilter
from opentelemetry.sdk.environment_variables import (
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER,
+ _OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES,
OTEL_EXPORTER_OTLP_CERTIFICATE,
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_CLIENT_KEY,
@@ -99,11 +93,18 @@ from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_HEADERS,
OTEL_EXPORTER_OTLP_INSECURE,
OTEL_EXPORTER_OTLP_TIMEOUT,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
)
from opentelemetry.sdk.metrics.export import MetricExportResult, MetricsData
from opentelemetry.sdk.resources import Resource as SDKResource
from opentelemetry.sdk.trace import ReadableSpan
from opentelemetry.sdk.trace.export import SpanExportResult
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
+from opentelemetry.semconv._incubating.attributes.rpc_attributes import (
+ RPC_RESPONSE_STATUS_CODE,
+)
from opentelemetry.util._importlib_metadata import entry_points
from opentelemetry.util.re import parse_env_headers
@@ -159,7 +160,7 @@ class InvalidCompressionValueException(Exception):
)
-def environ_to_compression(environ_key: str) -> Optional[Compression]:
+def environ_to_compression(environ_key: str) -> Compression | None:
environ_value = (
environ[environ_key].lower().strip()
if environ_key in environ
@@ -177,14 +178,14 @@ def environ_to_compression(environ_key: str) -> Optional[Compression]:
"Use one of the encoders from opentelemetry-exporter-otlp-proto-common instead. Deprecated since version 1.18.0.",
)
def get_resource_data(
- sdk_resource_scope_data: Dict[SDKResource, ResourceDataT],
+ sdk_resource_scope_data: dict[SDKResource, ResourceDataT],
resource_class: Callable[..., TypingResourceT],
name: str,
-) -> List[TypingResourceT]:
+) -> list[TypingResourceT]:
return _get_resource_data(sdk_resource_scope_data, resource_class, name)
-def _read_file(file_path: str) -> Optional[bytes]:
+def _read_file(file_path: str) -> bytes | None:
try:
with open(file_path, "rb") as file:
return file.read()
@@ -197,9 +198,9 @@ def _read_file(file_path: str) -> Optional[bytes]:
def _load_credentials(
- certificate_file: Optional[str],
- client_key_file: Optional[str],
- client_certificate_file: Optional[str],
+ certificate_file: str | None,
+ client_key_file: str | None,
+ client_certificate_file: str | None,
) -> ChannelCredentials:
root_certificates = (
_read_file(certificate_file) if certificate_file else None
@@ -219,7 +220,7 @@ def _load_credentials(
def _get_credentials(
- creds: Optional[ChannelCredentials],
+ creds: ChannelCredentials | None,
credential_entry_point_env_key: str,
certificate_file_env_key: str,
client_key_file_env_key: str,
@@ -288,15 +289,21 @@ class OTLPExporterMixin(
self,
stub: ExportStubT,
result: ExportResultT,
- endpoint: Optional[str] = None,
- insecure: Optional[bool] = None,
- credentials: Optional[ChannelCredentials] = None,
- headers: Optional[
- Union[TypingSequence[Tuple[str, str]], Dict[str, str], str]
- ] = None,
- timeout: Optional[float] = None,
- compression: Optional[Compression] = None,
- channel_options: Optional[Tuple[Tuple[str, str]]] = None,
+ endpoint: str | None = None,
+ insecure: bool | None = None,
+ credentials: ChannelCredentials | None = None,
+ headers: TypingSequence[tuple[str, str]]
+ | dict[str, str]
+ | str
+ | None = None,
+ timeout: float | None = None,
+ compression: Compression | None = None,
+ channel_options: tuple[tuple[str, str]] | None = None,
+ retryable_error_codes: Iterable[StatusCode] | None = None,
+ *,
+ component_type: OtelComponentTypeValues | None = None,
+ signal: Literal["traces", "metrics", "logs"] = "traces",
+ meter_provider: MeterProvider | None = None,
):
super().__init__()
self._result = result
@@ -355,6 +362,22 @@ class OTLPExporterMixin(
else compression
) or Compression.NoCompression
+ self._retryable_error_codes = retryable_error_codes or os.environ.get(
+ _OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES
+ )
+ if isinstance(self._retryable_error_codes, str):
+ self._retryable_error_codes = frozenset(
+ StatusCode[code.strip().upper()]
+ for code in self._retryable_error_codes.split(",")
+ if code.strip()
+ )
+ elif self._retryable_error_codes is not None:
+ self._retryable_error_codes = frozenset(
+ self._retryable_error_codes
+ )
+ else:
+ self._retryable_error_codes = _RETRYABLE_ERROR_CODES
+
self._channel = None
self._client = None
@@ -370,6 +393,20 @@ class OTLPExporterMixin(
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE,
)
+ self._component_type = component_type
+ self._signal: Literal["traces", "metrics", "logs"] = signal
+ self._parsed_url = parsed_url
+ self._metrics = create_exporter_metrics(
+ self._component_type,
+ signal,
+ parsed_url,
+ meter_provider,
+ os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "")
+ .strip()
+ .lower()
+ == "true",
+ )
+
self._initialize_channel_and_stub()
def _initialize_channel_and_stub(self):
@@ -402,6 +439,13 @@ class OTLPExporterMixin(
) -> ExportServiceRequestT:
pass
+ @abstractmethod
+ def _count_data(
+ self,
+ data: SDKDataT,
+ ) -> int:
+ pass
+
def _export(
self,
data: SDKDataT,
@@ -410,80 +454,88 @@ class OTLPExporterMixin(
logger.warning("Exporter already shutdown, ignoring batch")
return self._result.FAILURE # type: ignore [reportReturnType]
- # FIXME remove this check if the export type for traces
- # gets updated to a class that represents the proto
- # TracesData and use the code below instead.
- deadline_sec = time() + self._timeout
- for retry_num in range(_MAX_RETRYS):
- try:
- if self._client is None:
- return self._result.FAILURE
- self._client.Export(
- request=self._translate_data(data),
- metadata=self._headers,
- timeout=deadline_sec - time(),
- )
- return self._result.SUCCESS # type: ignore [reportReturnType]
- except RpcError as error:
- retry_info_bin = dict(error.trailing_metadata()).get( # type: ignore [reportAttributeAccessIssue]
- "google.rpc.retryinfo-bin" # type: ignore [reportArgumentType]
- )
- # multiplying by a random number between .8 and 1.2 introduces a +/20% jitter to each backoff.
- backoff_seconds = 2**retry_num * random.uniform(0.8, 1.2)
- if retry_info_bin is not None:
- retry_info = RetryInfo()
- retry_info.ParseFromString(retry_info_bin)
- backoff_seconds = (
- retry_info.retry_delay.seconds
- + retry_info.retry_delay.nanos / 1.0e9
+ with self._metrics.export_operation(self._count_data(data)) as result:
+ # FIXME remove this check if the export type for traces
+ # gets updated to a class that represents the proto
+ # TracesData and use the code below instead.
+ deadline_sec = time() + self._timeout
+ for retry_num in range(_MAX_RETRYS):
+ try:
+ if self._client is None:
+ return self._result.FAILURE
+ self._client.Export(
+ request=self._translate_data(data),
+ metadata=self._headers,
+ timeout=deadline_sec - time(),
)
-
- # For UNAVAILABLE errors, reinitialize the channel to force reconnection
- if error.code() == StatusCode.UNAVAILABLE and retry_num == 0: # type: ignore
- logger.debug(
- "Reinitializing gRPC channel for %s exporter due to UNAVAILABLE error",
- self._exporting,
+ return self._result.SUCCESS # type: ignore [reportReturnType]
+ except RpcError as error:
+ retry_info_bin = dict(error.trailing_metadata()).get( # type: ignore [reportAttributeAccessIssue]
+ "google.rpc.retryinfo-bin" # type: ignore [reportArgumentType]
)
- try:
- if self._channel:
- self._channel.close()
- except Exception as e:
+ # multiplying by a random number between .8 and 1.2 introduces a +/20% jitter to each backoff.
+ backoff_seconds = 2**retry_num * random.uniform(0.8, 1.2)
+ if retry_info_bin is not None:
+ retry_info = RetryInfo()
+ retry_info.ParseFromString(retry_info_bin)
+ backoff_seconds = (
+ retry_info.retry_delay.seconds
+ + retry_info.retry_delay.nanos / 1.0e9
+ )
+
+ # For UNAVAILABLE errors, reinitialize the channel to force reconnection
+ if (
+ error.code() == StatusCode.UNAVAILABLE
+ and retry_num == 0
+ ): # type: ignore
logger.debug(
- "Error closing channel for %s exporter to %s: %s",
+ "Reinitializing gRPC channel for %s exporter due to UNAVAILABLE error",
+ self._exporting,
+ )
+ try:
+ if self._channel:
+ self._channel.close()
+ except Exception as e:
+ logger.debug(
+ "Error closing channel for %s exporter to %s: %s",
+ self._exporting,
+ self._endpoint,
+ str(e),
+ )
+ # Enable channel reconnection for subsequent calls
+ self._initialize_channel_and_stub()
+
+ if (
+ error.code() not in self._retryable_error_codes # type: ignore [reportAttributeAccessIssue]
+ or retry_num + 1 == _MAX_RETRYS
+ or backoff_seconds > (deadline_sec - time())
+ or self._shutdown
+ ):
+ logger.error(
+ "Failed to export %s to %s, error code: %s",
self._exporting,
self._endpoint,
- str(e),
+ error.code(), # type: ignore [reportAttributeAccessIssue]
+ exc_info=error.code() == StatusCode.UNKNOWN, # type: ignore [reportAttributeAccessIssue]
)
- # Enable channel reconnection for subsequent calls
- self._initialize_channel_and_stub()
-
- if (
- error.code() not in _RETRYABLE_ERROR_CODES # type: ignore [reportAttributeAccessIssue]
- or retry_num + 1 == _MAX_RETRYS
- or backoff_seconds > (deadline_sec - time())
- or self._shutdown
- ):
- logger.error(
- "Failed to export %s to %s, error code: %s",
+ result.error = error
+ result.error_attrs = {
+ RPC_RESPONSE_STATUS_CODE: error.code().name
+ }
+ return self._result.FAILURE # type: ignore [reportReturnType]
+ logger.warning(
+ "Transient error %s encountered while exporting %s to %s, retrying in %.2fs.",
+ error.code(), # type: ignore [reportAttributeAccessIssue]
self._exporting,
self._endpoint,
- error.code(), # type: ignore [reportAttributeAccessIssue]
- exc_info=error.code() == StatusCode.UNKNOWN, # type: ignore [reportAttributeAccessIssue]
+ backoff_seconds,
)
- return self._result.FAILURE # type: ignore [reportReturnType]
- logger.warning(
- "Transient error %s encountered while exporting %s to %s, retrying in %.2fs.",
- error.code(), # type: ignore [reportAttributeAccessIssue]
- self._exporting,
- self._endpoint,
- backoff_seconds,
- )
- shutdown = self._shutdown_in_progress.wait(backoff_seconds)
- if shutdown:
- logger.warning("Shutdown in progress, aborting retry.")
- break
- # Not possible to reach here but the linter is complaining.
- return self._result.FAILURE # type: ignore [reportReturnType]
+ shutdown = self._shutdown_in_progress.wait(backoff_seconds)
+ if shutdown:
+ logger.warning("Shutdown in progress, aborting retry.")
+ break
+ # Not possible to reach here but the linter is complaining.
+ return self._result.FAILURE # type: ignore [reportReturnType]
def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None:
"""
@@ -508,3 +560,15 @@ class OTLPExporterMixin(
warning messages.
"""
pass
+
+ def _set_meter_provider(self, meter_provider: MeterProvider) -> None:
+ self._metrics = create_exporter_metrics(
+ self._component_type,
+ self._signal,
+ self._parsed_url,
+ meter_provider,
+ os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "")
+ .strip()
+ .lower()
+ == "true",
+ )
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py
index af77f6d..de97551 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py
@@ -1,25 +1,15 @@
# Copyright The OpenTelemetry Authors
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
+from collections.abc import Iterable
+from collections.abc import Sequence as TypingSequence
from dataclasses import replace
from logging import getLogger
from os import environ
-from typing import Iterable, List, Tuple, Union
-from typing import Sequence as TypingSequence
-from grpc import ChannelCredentials, Compression
+from grpc import ChannelCredentials, Compression, StatusCode
from opentelemetry.exporter.otlp.proto.common._internal.metrics_encoder import (
OTLPMetricExporterMixin,
)
@@ -32,6 +22,7 @@ from opentelemetry.exporter.otlp.proto.grpc.exporter import ( # noqa: F401
environ_to_compression,
get_resource_data,
)
+from opentelemetry.metrics import MeterProvider
from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import (
ExportMetricsServiceRequest,
)
@@ -72,6 +63,9 @@ from opentelemetry.sdk.metrics.export import ( # noqa: F401
from opentelemetry.sdk.metrics.export import ( # noqa: F401
Histogram as HistogramType,
)
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
_logger = getLogger(__name__)
@@ -100,7 +94,9 @@ class OTLPMetricExporter(
endpoint: str | None = None,
insecure: bool | None = None,
credentials: ChannelCredentials | None = None,
- headers: Union[TypingSequence[Tuple[str, str]], dict[str, str], str]
+ headers: TypingSequence[tuple[str, str]]
+ | dict[str, str]
+ | str
| None = None,
timeout: float | None = None,
compression: Compression | None = None,
@@ -108,7 +104,10 @@ class OTLPMetricExporter(
| None = None,
preferred_aggregation: dict[type, Aggregation] | None = None,
max_export_batch_size: int | None = None,
- channel_options: Tuple[Tuple[str, str]] | None = None,
+ channel_options: tuple[tuple[str, str]] | None = None,
+ retryable_error_codes: Iterable[StatusCode] | None = None,
+ *,
+ meter_provider: MeterProvider | None = None,
):
insecure_metrics = environ.get(OTEL_EXPORTER_OTLP_METRICS_INSECURE)
if insecure is None and insecure_metrics is not None:
@@ -153,6 +152,10 @@ class OTLPMetricExporter(
timeout=timeout or environ_timeout,
compression=compression,
channel_options=channel_options,
+ retryable_error_codes=retryable_error_codes,
+ component_type=OtelComponentTypeValues.OTLP_GRPC_METRIC_EXPORTER,
+ signal="metrics",
+ meter_provider=meter_provider,
)
self._max_export_batch_size: int | None = max_export_batch_size
@@ -162,6 +165,16 @@ class OTLPMetricExporter(
) -> ExportMetricsServiceRequest:
return encode_metrics(data)
+ def _count_data(self, data: MetricsData):
+ num_items = 0
+
+ for resource_metrics in data.resource_metrics:
+ for scope_metrics in resource_metrics.scope_metrics:
+ for metric in scope_metrics.metrics:
+ num_items += len(metric.data.data_points)
+
+ return num_items
+
def export(
self,
metrics_data: MetricsData,
@@ -187,10 +200,10 @@ class OTLPMetricExporter(
) -> Iterable[MetricsData]:
assert self._max_export_batch_size is not None
batch_size: int = 0
- split_resource_metrics: List[ResourceMetrics] = []
+ split_resource_metrics: list[ResourceMetrics] = []
for resource_metrics in metrics_data.resource_metrics:
- split_scope_metrics: List[ScopeMetrics] = []
+ split_scope_metrics: list[ScopeMetrics] = []
split_resource_metrics.append(
replace(
resource_metrics,
@@ -198,7 +211,7 @@ class OTLPMetricExporter(
)
)
for scope_metrics in resource_metrics.scope_metrics:
- split_metrics: List[Metric] = []
+ split_metrics: list[Metric] = []
split_scope_metrics.append(
replace(
scope_metrics,
@@ -206,7 +219,7 @@ class OTLPMetricExporter(
)
)
for metric in scope_metrics.metrics:
- split_data_points: List[DataPointT] = []
+ split_data_points: list[DataPointT] = []
split_metrics.append(
replace(
metric,
@@ -268,6 +281,9 @@ class OTLPMetricExporter(
def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None:
OTLPExporterMixin.shutdown(self, timeout_millis=timeout_millis)
+ def set_meter_provider(self, meter_provider: MeterProvider):
+ return self._set_meter_provider(meter_provider)
+
@property
def _exporting(self) -> str:
return "metrics"
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py
index 19b189e..7d396c1 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py
@@ -1,24 +1,14 @@
# Copyright The OpenTelemetry Authors
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""OTLP Span Exporter"""
import logging
+from collections.abc import Iterable, Sequence
+from collections.abc import Sequence as TypingSequence
from os import environ
-from typing import Dict, Optional, Sequence, Tuple, Union
-from typing import Sequence as TypingSequence
-from grpc import ChannelCredentials, Compression
+from grpc import ChannelCredentials, Compression, StatusCode
from opentelemetry.exporter.otlp.proto.common.trace_encoder import (
encode_spans,
)
@@ -28,6 +18,7 @@ from opentelemetry.exporter.otlp.proto.grpc.exporter import ( # noqa: F401
environ_to_compression,
get_resource_data,
)
+from opentelemetry.metrics import MeterProvider
from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import (
ExportTraceServiceRequest,
)
@@ -58,6 +49,9 @@ from opentelemetry.sdk.environment_variables import (
)
from opentelemetry.sdk.trace import ReadableSpan
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
logger = logging.getLogger(__name__)
@@ -86,15 +80,19 @@ class OTLPSpanExporter(
def __init__(
self,
- endpoint: Optional[str] = None,
- insecure: Optional[bool] = None,
- credentials: Optional[ChannelCredentials] = None,
- headers: Optional[
- Union[TypingSequence[Tuple[str, str]], Dict[str, str], str]
- ] = None,
- timeout: Optional[float] = None,
- compression: Optional[Compression] = None,
- channel_options: Optional[Tuple[Tuple[str, str]]] = None,
+ endpoint: str | None = None,
+ insecure: bool | None = None,
+ credentials: ChannelCredentials | None = None,
+ headers: TypingSequence[tuple[str, str]]
+ | dict[str, str]
+ | str
+ | None = None,
+ timeout: float | None = None,
+ compression: Compression | None = None,
+ channel_options: tuple[tuple[str, str]] | None = None,
+ retryable_error_codes: Iterable[StatusCode] | None = None,
+ *,
+ meter_provider: MeterProvider | None = None,
):
insecure_spans = environ.get(OTEL_EXPORTER_OTLP_TRACES_INSECURE)
if insecure is None and insecure_spans is not None:
@@ -135,6 +133,10 @@ class OTLPSpanExporter(
timeout=timeout or environ_timeout,
compression=compression,
channel_options=channel_options,
+ retryable_error_codes=retryable_error_codes,
+ component_type=OtelComponentTypeValues.OTLP_GRPC_SPAN_EXPORTER,
+ signal="traces",
+ meter_provider=meter_provider,
)
def _translate_data(
@@ -142,6 +144,9 @@ class OTLPSpanExporter(
) -> ExportTraceServiceRequest:
return encode_spans(data)
+ def _count_data(self, data: Sequence[ReadableSpan]):
+ return len(data)
+
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
return self._export(spans)
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/version/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/version/__init__.py
index 49e1254..a400b9b 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/version/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.0"
+__version__ = "1.42.1"
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.latest.txt b/exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.latest.txt
index 748f3ee..e6b4831 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.latest.txt
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.latest.txt
@@ -1,5 +1,5 @@
# This file was autogenerated by uv via the following command:
-# uv pip compile --python 3.9 --universal -c dev-requirements.txt exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in -o exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.latest.txt
+# uv pip compile --python 3.10 --universal -c dev-requirements.txt exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in -o exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.latest.txt
-e exporter/opentelemetry-exporter-otlp-proto-common
# via
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
@@ -33,25 +33,27 @@ asgiref==3.7.2
# via
# -c dev-requirements.txt
# opentelemetry-test-utils
+certifi==2026.4.22
+ # via requests
+charset-normalizer==3.4.7
+ # via requests
colorama==0.4.6
# via
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
# pytest
-exceptiongroup==1.3.0 ; python_full_version < '3.11'
+exceptiongroup==1.3.1 ; python_full_version < '3.11'
# via pytest
-googleapis-common-protos==1.70.0
+googleapis-common-protos==1.74.0
# via opentelemetry-exporter-otlp-proto-grpc
-grpcio==1.73.0 ; python_full_version < '3.14'
+grpcio==1.80.0
# via opentelemetry-exporter-otlp-proto-grpc
-grpcio==1.76.0 ; python_full_version >= '3.14'
- # via opentelemetry-exporter-otlp-proto-grpc
-importlib-metadata==8.7.0
- # via opentelemetry-api
-iniconfig==2.1.0
+idna==3.14
+ # via requests
+iniconfig==2.3.0
# via
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
# pytest
-packaging==25.0
+packaging==26.0
# via
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
# pytest
@@ -59,7 +61,7 @@ pluggy==1.6.0
# via
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
# pytest
-protobuf==6.31.1
+protobuf==6.33.6
# via
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
# googleapis-common-protos
@@ -68,9 +70,13 @@ pytest==7.4.4
# via
# -c dev-requirements.txt
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
-tomli==2.2.1 ; python_full_version < '3.11'
+requests==2.32.3
+ # via
+ # -c dev-requirements.txt
+ # opentelemetry-test-utils
+tomli==2.4.1 ; python_full_version < '3.11'
# via pytest
-typing-extensions==4.14.0
+typing-extensions==4.15.0
# via
# asgiref
# exceptiongroup
@@ -79,5 +85,5 @@ typing-extensions==4.14.0
# opentelemetry-exporter-otlp-proto-grpc
# opentelemetry-sdk
# opentelemetry-semantic-conventions
-zipp==3.23.0
- # via importlib-metadata
+urllib3==2.7.0
+ # via requests
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.oldest.txt b/exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.oldest.txt
index 8fe252e..330ca72 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.oldest.txt
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.oldest.txt
@@ -1,5 +1,5 @@
# This file was autogenerated by uv via the following command:
-# uv pip compile --python 3.9 --universal --resolution lowest -c dev-requirements.txt exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in -o exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.oldest.txt
+# uv pip compile --python 3.10 --universal --resolution lowest -c dev-requirements.txt exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in -o exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.oldest.txt
-e exporter/opentelemetry-exporter-otlp-proto-common
# via
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
@@ -33,6 +33,10 @@ asgiref==3.7.2
# via
# -c dev-requirements.txt
# opentelemetry-test-utils
+certifi==2017.4.17
+ # via requests
+charset-normalizer==2.0.0
+ # via requests
colorama==0.4.6
# via
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
@@ -47,8 +51,8 @@ grpcio==1.66.2 ; python_full_version == '3.13.*'
# via opentelemetry-exporter-otlp-proto-grpc
grpcio==1.75.1 ; python_full_version >= '3.14'
# via opentelemetry-exporter-otlp-proto-grpc
-importlib-metadata==6.0.0
- # via opentelemetry-api
+idna==2.5
+ # via requests
iniconfig==2.0.0
# via
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
@@ -70,6 +74,10 @@ pytest==7.4.4
# via
# -c dev-requirements.txt
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
+requests==2.32.3
+ # via
+ # -c dev-requirements.txt
+ # opentelemetry-test-utils
tomli==1.0.0 ; python_full_version < '3.11'
# via pytest
typing-extensions==4.6.0 ; python_full_version < '3.14'
@@ -86,5 +94,5 @@ typing-extensions==4.12.0 ; python_full_version >= '3.14'
# opentelemetry-exporter-otlp-proto-grpc
# opentelemetry-sdk
# opentelemetry-semantic-conventions
-zipp==0.5.0
- # via importlib-metadata
+urllib3==1.21.1
+ # via requests
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/logs/test_otlp_logs_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/logs/test_otlp_logs_exporter.py
index 94e8cc9..5f501cd 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/logs/test_otlp_logs_exporter.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/logs/test_otlp_logs_exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-lines
@@ -402,6 +391,10 @@ class TestOTLPLogExporter(TestCase):
expected, self.exporter._translate_data([self.log_data_1])
)
+ def test_count_log_data(self):
+ # pylint: disable=protected-access
+ self.assertEqual(1, self.exporter._count_data([self.log_data_1]))
+
def test_translate_multiple_logs(self):
expected = ExportLogsServiceRequest(
resource_logs=[
@@ -539,3 +532,12 @@ class TestOTLPLogExporter(TestCase):
[self.log_data_1, self.log_data_2, self.log_data_3]
),
)
+
+ def test_count_multiple_logs(self):
+ self.assertEqual(
+ 3,
+ # pylint: disable=protected-access
+ self.exporter._count_data(
+ [self.log_data_1, self.log_data_2, self.log_data_3]
+ ),
+ )
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py
index de27d0f..91749fa 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py
@@ -1,26 +1,16 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import threading
import time
import unittest
+from collections.abc import Sequence
from concurrent.futures import ( # pylint: disable=no-name-in-module
ThreadPoolExecutor,
)
from logging import WARNING, getLogger
from platform import system
-from typing import Any, Optional, Sequence
+from typing import Any
from unittest import TestCase
from unittest.mock import Mock, patch
@@ -37,6 +27,7 @@ from opentelemetry.exporter.otlp.proto.common.trace_encoder import (
encode_spans,
)
from opentelemetry.exporter.otlp.proto.grpc.exporter import ( # noqa: F401
+ _RETRYABLE_ERROR_CODES,
InvalidCompressionValueException,
OTLPExporterMixin,
environ_to_compression,
@@ -54,12 +45,18 @@ from opentelemetry.proto.collector.trace.v1.trace_service_pb2_grpc import (
from opentelemetry.sdk.environment_variables import (
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER,
OTEL_EXPORTER_OTLP_COMPRESSION,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
)
+from opentelemetry.sdk.metrics import MeterProvider
+from opentelemetry.sdk.metrics.export import InMemoryMetricReader
from opentelemetry.sdk.trace import ReadableSpan, _Span
from opentelemetry.sdk.trace.export import (
SpanExporter,
SpanExportResult,
)
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
from opentelemetry.test.mock_test_classes import IterEntryPoint
logger = getLogger(__name__)
@@ -78,13 +75,23 @@ class OTLPSpanExporterForTesting(
],
):
def __init__(self, **kwargs):
- super().__init__(TraceServiceStub, SpanExportResult, **kwargs)
+ super().__init__(
+ TraceServiceStub,
+ SpanExportResult,
+ component_type=OtelComponentTypeValues.OTLP_GRPC_SPAN_EXPORTER,
+ signal="traces",
+ meter_provider=kwargs.pop("meter_provider", None),
+ **kwargs,
+ )
def _translate_data(
self, data: Sequence[ReadableSpan]
) -> ExportTraceServiceRequest:
return encode_spans(data)
+ def _count_data(self, data: Sequence[ReadableSpan]) -> int:
+ return len(data)
+
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
return self._export(spans)
@@ -100,8 +107,8 @@ class TraceServiceServicerWithExportParams(TraceServiceServicer):
def __init__(
self,
export_result: StatusCode,
- optional_retry_nanos: Optional[int] = None,
- optional_export_sleep: Optional[float] = None,
+ optional_retry_nanos: int | None = None,
+ optional_export_sleep: float | None = None,
):
self.export_result = export_result
self.optional_export_sleep = optional_export_sleep
@@ -149,11 +156,12 @@ class ThreadWithReturnValue(threading.Thread):
# an argument that has a member that points to the thread.
del self._target, self._args, self._kwargs # type: ignore
- def join(self, timeout: Optional[float] = None) -> Any:
+ def join(self, timeout: float | None = None) -> Any:
super().join(timeout=timeout)
return self._return
+# pylint: disable-next=too-many-public-methods
class TestOTLPExporterMixin(TestCase):
def setUp(self):
self.server = server(ThreadPoolExecutor(max_workers=10))
@@ -161,7 +169,14 @@ class TestOTLPExporterMixin(TestCase):
self.server.add_insecure_port("127.0.0.1:4317")
self.server.start()
- self.exporter = OTLPSpanExporterForTesting(insecure=True)
+
+ self.metric_reader = InMemoryMetricReader()
+ self.meter_provider = MeterProvider(
+ metric_readers=[self.metric_reader]
+ )
+ self.exporter = OTLPSpanExporterForTesting(
+ insecure=True, meter_provider=self.meter_provider
+ )
self.span = _Span(
"a",
context=Mock(
@@ -364,18 +379,44 @@ class TestOTLPExporterMixin(TestCase):
),
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: " true "}
+ )
def test_shutdown(self):
add_TraceServiceServicer_to_server(
TraceServiceServicerWithExportParams(StatusCode.OK),
self.server,
)
+ exporter = OTLPSpanExporterForTesting(
+ insecure=True, meter_provider=self.meter_provider
+ )
self.assertEqual(
- self.exporter.export([self.span]), SpanExportResult.SUCCESS
+ exporter.export([self.span]), SpanExportResult.SUCCESS
)
- self.exporter.shutdown()
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(
+ metrics[0].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertEqual(metrics[1].name, "otel.sdk.exporter.span.exported")
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertEqual(metrics[2].name, "otel.sdk.exporter.span.inflight")
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+
+ exporter.shutdown()
with self.assertLogs(level=WARNING) as warning:
self.assertEqual(
- self.exporter.export([self.span]), SpanExportResult.FAILURE
+ exporter.export([self.span]), SpanExportResult.FAILURE
)
self.assertEqual(
warning.records[0].message,
@@ -437,6 +478,9 @@ class TestOTLPExporterMixin(TestCase):
system() == "Windows",
"For gRPC + windows there's some added delay in the RPCs which breaks the assertion over amount of time passed.",
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
def test_retry_info_is_respected(self):
mock_trace_service = TraceServiceServicerWithExportParams(
StatusCode.UNAVAILABLE,
@@ -446,7 +490,9 @@ class TestOTLPExporterMixin(TestCase):
mock_trace_service,
self.server,
)
- exporter = OTLPSpanExporterForTesting(insecure=True, timeout=10)
+ exporter = OTLPSpanExporterForTesting(
+ insecure=True, timeout=10, meter_provider=self.meter_provider
+ )
before = time.time()
self.assertEqual(
exporter.export([self.span]),
@@ -457,6 +503,51 @@ class TestOTLPExporterMixin(TestCase):
# 1 second plus wiggle room so the test passes consistently.
self.assertAlmostEqual(after - before, 1, 1)
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(
+ metrics[0].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[0].data.data_points[0].attributes["error.type"],
+ "_InactiveRpcError",
+ )
+ self.assertEqual(
+ metrics[0]
+ .data.data_points[0]
+ .attributes["rpc.response.status_code"],
+ "UNAVAILABLE",
+ )
+ self.assertEqual(metrics[1].name, "otel.sdk.exporter.span.exported")
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[1].data.data_points[0].attributes["error.type"],
+ "_InactiveRpcError",
+ )
+ self.assertNotIn(
+ "rpc.response.status_code",
+ metrics[1].data.data_points[0].attributes,
+ )
+ self.assertEqual(metrics[2].name, "otel.sdk.exporter.span.inflight")
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "rpc.response.status_code",
+ metrics[2].data.data_points[0].attributes,
+ )
+
@unittest.skipIf(
system() == "Windows",
"For gRPC + windows there's some added delay in the RPCs which breaks the assertion over amount of time passed.",
@@ -532,7 +623,13 @@ class TestOTLPExporterMixin(TestCase):
(),
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
def test_permanent_failure(self):
+ exporter = OTLPSpanExporterForTesting(
+ insecure=True, meter_provider=self.meter_provider
+ )
with self.assertLogs(level=WARNING) as warning:
add_TraceServiceServicer_to_server(
TraceServiceServicerWithExportParams(
@@ -541,13 +638,58 @@ class TestOTLPExporterMixin(TestCase):
self.server,
)
self.assertEqual(
- self.exporter.export([self.span]), SpanExportResult.FAILURE
+ exporter.export([self.span]), SpanExportResult.FAILURE
)
self.assertEqual(
warning.records[-1].message,
"Failed to export traces to localhost:4317, error code: StatusCode.ALREADY_EXISTS",
)
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(
+ metrics[0].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[0].data.data_points[0].attributes["error.type"],
+ "_InactiveRpcError",
+ )
+ self.assertEqual(
+ metrics[0]
+ .data.data_points[0]
+ .attributes["rpc.response.status_code"],
+ "ALREADY_EXISTS",
+ )
+ self.assertEqual(metrics[1].name, "otel.sdk.exporter.span.exported")
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[1].data.data_points[0].attributes["error.type"],
+ "_InactiveRpcError",
+ )
+ self.assertNotIn(
+ "rpc.response.status_code",
+ metrics[1].data.data_points[0].attributes,
+ )
+ self.assertEqual(metrics[2].name, "otel.sdk.exporter.span.inflight")
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "rpc.response.status_code",
+ metrics[2].data.data_points[0].attributes,
+ )
+
def test_unavailable_reconnects(self):
"""Test that the exporter reconnects on UNAVAILABLE error"""
add_TraceServiceServicer_to_server(
@@ -570,4 +712,81 @@ class TestOTLPExporterMixin(TestCase):
# Since the initial channel was created in setUp (unpatched), this call
# must be from the reconnection logic.
self.assertTrue(mock_insecure_channel.called)
- # Verify that reconnection enabled flag is set
+
+ def test_retryable_error_codes_initialization(self):
+ # pylint: disable=protected-access
+ self.assertEqual(
+ self.exporter._retryable_error_codes, _RETRYABLE_ERROR_CODES
+ )
+ custom_codes = [StatusCode.INTERNAL, StatusCode.UNKNOWN]
+ exporter = OTLPSpanExporterForTesting(
+ insecure=True, retryable_error_codes=custom_codes
+ )
+ self.assertEqual(
+ exporter._retryable_error_codes, frozenset(custom_codes)
+ )
+
+ @patch.dict(
+ "os.environ",
+ {
+ "OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES": ",INTERNAL, unknown,,,dEAdline_Exceeded "
+ },
+ )
+ def test_retryable_error_codes_initialization_from_env(self):
+ expected_codes = frozenset(
+ {
+ StatusCode.INTERNAL,
+ StatusCode.UNKNOWN,
+ StatusCode.DEADLINE_EXCEEDED,
+ }
+ )
+ exporter = OTLPSpanExporterForTesting()
+ # pylint: disable=protected-access
+ self.assertEqual(exporter._retryable_error_codes, expected_codes)
+
+ @unittest.skipIf(
+ system() == "Windows",
+ "For gRPC + windows there's some added delay in the RPCs which breaks the assertion over amount of time passed.",
+ )
+ def test_retryable_error_codes_custom(self):
+ # Test that a custom error code is retried if specified
+ custom_codes = [StatusCode.INTERNAL]
+ mock_trace_service = TraceServiceServicerWithExportParams(
+ StatusCode.INTERNAL,
+ optional_retry_nanos=200000000, # .2 seconds
+ )
+ add_TraceServiceServicer_to_server(
+ mock_trace_service,
+ self.server,
+ )
+ exporter = OTLPSpanExporterForTesting(
+ insecure=True, retryable_error_codes=custom_codes, timeout=10
+ )
+
+ self.assertEqual(
+ exporter.export([self.span]),
+ SpanExportResult.FAILURE,
+ )
+
+ self.assertEqual(mock_trace_service.num_requests, 6)
+
+ # Test that a default retryable code is NOT retried if not in custom_codes
+ mock_trace_service.num_requests = 0
+ mock_trace_service.export_result = StatusCode.UNAVAILABLE
+ self.assertEqual(
+ exporter.export([self.span]),
+ SpanExportResult.FAILURE,
+ )
+ self.assertEqual(mock_trace_service.num_requests, 1)
+
+ def assert_standard_metric_attrs(self, attributes):
+ self.assertEqual(
+ attributes["otel.component.type"], "otlp_grpc_span_exporter"
+ )
+ self.assertTrue(
+ attributes["otel.component.name"].startswith(
+ "otlp_grpc_span_exporter/"
+ )
+ )
+ self.assertEqual(attributes["server.address"], "localhost")
+ self.assertEqual(attributes["server.port"], 4317)
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_metrics_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_metrics_exporter.py
index 16fd666..496fe30 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_metrics_exporter.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_metrics_exporter.py
@@ -1,22 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-lines
from logging import WARNING
from os import environ
from os.path import dirname
-from typing import List
from unittest import TestCase
from unittest.mock import patch
@@ -345,7 +333,7 @@ class TestOTLPMetricExporter(TestCase):
]
)
# WHEN
- split_metrics_data: List[MetricsData] = list(
+ split_metrics_data: list[MetricsData] = list(
# pylint: disable=protected-access
OTLPMetricExporter(max_export_batch_size=2)._split_metrics_data(
metrics_data=metrics_data,
@@ -424,7 +412,7 @@ class TestOTLPMetricExporter(TestCase):
]
)
# WHEN
- split_metrics_data: List[MetricsData] = list(
+ split_metrics_data: list[MetricsData] = list(
# pylint: disable=protected-access
OTLPMetricExporter(max_export_batch_size=3)._split_metrics_data(
metrics_data=metrics_data,
@@ -515,7 +503,7 @@ class TestOTLPMetricExporter(TestCase):
]
)
# WHEN
- split_metrics_data: List[MetricsData] = list(
+ split_metrics_data: list[MetricsData] = list(
# pylint: disable=protected-access
OTLPMetricExporter(max_export_batch_size=2)._split_metrics_data(
metrics_data=metrics_data,
@@ -590,6 +578,69 @@ class TestOTLPMetricExporter(TestCase):
split_metrics_data,
)
+ def test_count_metrics_data(self):
+ # GIVEN
+ metrics_data = MetricsData(
+ resource_metrics=[
+ _resource_metrics(
+ index=1,
+ scope_metrics=[
+ _scope_metrics(
+ index=1,
+ metrics=[
+ _gauge(
+ index=1,
+ data_points=[
+ _number_data_point(11),
+ ],
+ ),
+ _gauge(
+ index=2,
+ data_points=[
+ _number_data_point(12),
+ ],
+ ),
+ ],
+ ),
+ _scope_metrics(
+ index=2,
+ metrics=[
+ _gauge(
+ index=3,
+ data_points=[
+ _number_data_point(13),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ _resource_metrics(
+ index=2,
+ scope_metrics=[
+ _scope_metrics(
+ index=3,
+ metrics=[
+ _gauge(
+ index=4,
+ data_points=[
+ _number_data_point(14),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ]
+ )
+ # WHEN
+ # pylint: disable=protected-access
+ count = OTLPMetricExporter(max_export_batch_size=2)._count_data(
+ metrics_data,
+ )
+ # THEN
+ self.assertEqual(count, 4)
+
@patch("opentelemetry.exporter.otlp.proto.grpc.exporter.secure_channel")
def test_insecure_https_endpoint(self, mock_secure_channel):
OTLPMetricExporter(endpoint="https://ab.c:123", insecure=True)
@@ -766,7 +817,7 @@ class TestOTLPMetricExporter(TestCase):
def _resource_metrics(
- index: int, scope_metrics: List[ScopeMetrics]
+ index: int, scope_metrics: list[ScopeMetrics]
) -> ResourceMetrics:
return ResourceMetrics(
resource=Resource(
@@ -778,7 +829,7 @@ def _resource_metrics(
)
-def _scope_metrics(index: int, metrics: List[Metric]) -> ScopeMetrics:
+def _scope_metrics(index: int, metrics: list[Metric]) -> ScopeMetrics:
return ScopeMetrics(
scope=InstrumentationScope(name=f"scope_{index}"),
schema_url=f"scope_url_{index}",
@@ -786,7 +837,7 @@ def _scope_metrics(index: int, metrics: List[Metric]) -> ScopeMetrics:
)
-def _gauge(index: int, data_points: List[NumberDataPoint]) -> Metric:
+def _gauge(index: int, data_points: list[NumberDataPoint]) -> Metric:
return Metric(
name=f"gauge_{index}",
description="description",
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py
index cbe6298..64cd091 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-lines
@@ -481,6 +470,10 @@ class TestOTLPSpanExporter(TestCase):
# pylint: disable=protected-access
self.assertEqual(expected, self.exporter._translate_data([self.span]))
+ def test_count_spans(self):
+ # pylint: disable=protected-access
+ self.assertEqual(1, self.exporter._count_data([self.span]))
+
def test_translate_spans_multi(self):
expected = ExportTraceServiceRequest(
resource_spans=[
@@ -660,6 +653,13 @@ class TestOTLPSpanExporter(TestCase):
self.exporter._translate_data([self.span, self.span2, self.span3]),
)
+ def test_count_spans_multi(self):
+ self.assertEqual(
+ # pylint: disable=protected-access
+ 3,
+ self.exporter._count_data([self.span, self.span2, self.span3]),
+ )
+
def _check_translated_status(
self,
translated: ExportTraceServiceRequest,
diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/pyproject.toml b/exporter/opentelemetry-exporter-otlp-proto-http/pyproject.toml
index d828473..5f7b616 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-http/pyproject.toml
+++ b/exporter/opentelemetry-exporter-otlp-proto-http/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenTelemetry Collector Protobuf over HTTP Exporter"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -19,7 +19,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -29,9 +28,9 @@ classifiers = [
dependencies = [
"googleapis-common-protos ~= 1.52",
"opentelemetry-api ~= 1.15",
- "opentelemetry-proto == 1.41.0",
- "opentelemetry-sdk ~= 1.41.0",
- "opentelemetry-exporter-otlp-proto-common == 1.41.0",
+ "opentelemetry-proto == 1.42.1",
+ "opentelemetry-sdk ~= 1.42.1",
+ "opentelemetry-exporter-otlp-proto-common == 1.42.1",
"requests ~= 2.7",
"typing-extensions >= 4.5.0",
]
diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py
index 745a604..69f51ec 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_common/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_common/__init__.py
index 1bdb7d2..57bd7ca 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_common/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_common/__init__.py
@@ -1,19 +1,8 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from os import environ
-from typing import Literal, Optional
+from typing import Literal
import requests
@@ -37,7 +26,7 @@ def _load_session_from_envvar(
"OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER",
"OTEL_PYTHON_EXPORTER_OTLP_HTTP_METRICS_CREDENTIAL_PROVIDER",
],
-) -> Optional[requests.Session]:
+) -> requests.Session | None:
_credential_env = environ.get(
_OTEL_PYTHON_EXPORTER_OTLP_HTTP_CREDENTIAL_PROVIDER
) or environ.get(cred_envvar)
diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py
index 7aea76b..56906b9 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py
@@ -1,30 +1,24 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import gzip
import logging
+import os
import random
import threading
import zlib
+from collections.abc import Sequence
from io import BytesIO
from os import environ
from time import time
-from typing import Dict, Optional, Sequence
+from urllib.parse import urlparse
import requests
from requests.exceptions import ConnectionError
+from opentelemetry.exporter.otlp.proto.common._exporter_metrics import (
+ create_exporter_metrics,
+)
from opentelemetry.exporter.otlp.proto.common._log_encoder import encode_logs
from opentelemetry.exporter.otlp.proto.http import (
_OTLP_HTTP_HEADERS,
@@ -34,6 +28,7 @@ from opentelemetry.exporter.otlp.proto.http._common import (
_is_retryable,
_load_session_from_envvar,
)
+from opentelemetry.metrics import MeterProvider
from opentelemetry.sdk._logs import ReadableLogRecord
from opentelemetry.sdk._logs.export import (
LogRecordExporter,
@@ -56,6 +51,13 @@ from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
OTEL_EXPORTER_OTLP_TIMEOUT,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
+)
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
+from opentelemetry.semconv.attributes.http_attributes import (
+ HTTP_RESPONSE_STATUS_CODE,
)
from opentelemetry.util.re import parse_env_headers
@@ -74,14 +76,16 @@ _MAX_RETRYS = 6
class OTLPLogExporter(LogRecordExporter):
def __init__(
self,
- endpoint: Optional[str] = None,
- certificate_file: Optional[str] = None,
- client_key_file: Optional[str] = None,
- client_certificate_file: Optional[str] = None,
- headers: Optional[Dict[str, str]] = None,
- timeout: Optional[float] = None,
- compression: Optional[Compression] = None,
- session: Optional[requests.Session] = None,
+ endpoint: str | None = None,
+ certificate_file: str | None = None,
+ client_key_file: str | None = None,
+ client_certificate_file: str | None = None,
+ headers: dict[str, str] | None = None,
+ timeout: float | None = None,
+ compression: Compression | None = None,
+ session: requests.Session | None = None,
+ *,
+ meter_provider: MeterProvider | None = None,
):
self._shutdown_is_occuring = threading.Event()
self._endpoint = endpoint or environ.get(
@@ -139,8 +143,19 @@ class OTLPLogExporter(LogRecordExporter):
)
self._shutdown = False
+ self._metrics = create_exporter_metrics(
+ OtelComponentTypeValues.OTLP_HTTP_LOG_EXPORTER,
+ "logs",
+ urlparse(self._endpoint),
+ meter_provider,
+ os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "")
+ .strip()
+ .lower()
+ == "true",
+ )
+
def _export(
- self, serialized_data: bytes, timeout_sec: Optional[float] = None
+ self, serialized_data: bytes, timeout_sec: float | None = None
):
data = serialized_data
if self._compression == Compression.Gzip:
@@ -183,52 +198,69 @@ class OTLPLogExporter(LogRecordExporter):
_logger.warning("Exporter already shutdown, ignoring batch")
return LogRecordExportResult.FAILURE
- serialized_data = encode_logs(batch).SerializeToString()
- deadline_sec = time() + self._timeout
- for retry_num in range(_MAX_RETRYS):
- # multiplying by a random number between .8 and 1.2 introduces a +/20% jitter to each backoff.
- backoff_seconds = 2**retry_num * random.uniform(0.8, 1.2)
- try:
- resp = self._export(serialized_data, deadline_sec - time())
- if resp.ok:
- return LogRecordExportResult.SUCCESS
- except requests.exceptions.RequestException as error:
- reason = error
- retryable = isinstance(error, ConnectionError)
- status_code = None
- else:
- reason = resp.reason
- retryable = _is_retryable(resp)
- status_code = resp.status_code
+ with self._metrics.export_operation(len(batch)) as result:
+ serialized_data = encode_logs(batch).SerializeToString()
+ deadline_sec = time() + self._timeout
+ for retry_num in range(_MAX_RETRYS):
+ # multiplying by a random number between .8 and 1.2 introduces a +/20% jitter to each backoff.
+ backoff_seconds = 2**retry_num * random.uniform(0.8, 1.2)
+ export_error: Exception | None = None
+ try:
+ resp = self._export(serialized_data, deadline_sec - time())
+ if resp.ok:
+ return LogRecordExportResult.SUCCESS
+ except requests.exceptions.RequestException as error:
+ reason = error
+ export_error = error
+ retryable = isinstance(error, ConnectionError)
+ status_code = None
+ else:
+ reason = resp.reason
+ retryable = _is_retryable(resp)
+ status_code = resp.status_code
- if not retryable:
- _logger.error(
- "Failed to export logs batch code: %s, reason: %s",
- status_code,
- reason,
- )
- return LogRecordExportResult.FAILURE
+ if not retryable:
+ _logger.error(
+ "Failed to export logs batch code: %s, reason: %s",
+ status_code,
+ reason,
+ )
+ error_attrs = (
+ {HTTP_RESPONSE_STATUS_CODE: status_code}
+ if status_code is not None
+ else None
+ )
+ result.error = export_error
+ result.error_attrs = error_attrs
+ return LogRecordExportResult.FAILURE
- if (
- retry_num + 1 == _MAX_RETRYS
- or backoff_seconds > (deadline_sec - time())
- or self._shutdown
- ):
- _logger.error(
- "Failed to export logs batch due to timeout, "
- "max retries or shutdown."
+ if (
+ retry_num + 1 == _MAX_RETRYS
+ or backoff_seconds > (deadline_sec - time())
+ or self._shutdown
+ ):
+ _logger.error(
+ "Failed to export logs batch due to timeout, "
+ "max retries or shutdown."
+ )
+ error_attrs = (
+ {HTTP_RESPONSE_STATUS_CODE: status_code}
+ if status_code is not None
+ else None
+ )
+ result.error = export_error
+ result.error_attrs = error_attrs
+ return LogRecordExportResult.FAILURE
+ _logger.warning(
+ "Transient error %s encountered while exporting logs batch, retrying in %.2fs.",
+ reason,
+ backoff_seconds,
)
- return LogRecordExportResult.FAILURE
- _logger.warning(
- "Transient error %s encountered while exporting logs batch, retrying in %.2fs.",
- reason,
- backoff_seconds,
- )
- shutdown = self._shutdown_is_occuring.wait(backoff_seconds)
- if shutdown:
- _logger.warning("Shutdown in progress, aborting retry.")
- break
- return LogRecordExportResult.FAILURE
+ shutdown = self._shutdown_is_occuring.wait(backoff_seconds)
+ if shutdown:
+ _logger.warning("Shutdown in progress, aborting retry.")
+ break
+ return LogRecordExportResult.FAILURE
def force_flush(self, timeout_millis: float = 10_000) -> bool:
"""Nothing is buffered in this exporter, so this method does nothing."""
diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/metric_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/metric_exporter/__init__.py
index c1d51e3..eb1e69c 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/metric_exporter/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/metric_exporter/__init__.py
@@ -1,38 +1,30 @@
# Copyright The OpenTelemetry Authors
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
import gzip
import logging
+import os
import random
import threading
import zlib
+from collections.abc import Callable, Iterable
from io import BytesIO
from os import environ
from time import time
from typing import ( # noqa: F401
Any,
- Callable,
- Dict,
- Iterable,
- List,
Optional,
)
+from urllib.parse import urlparse
import requests
from requests.exceptions import ConnectionError
from typing_extensions import deprecated
+from opentelemetry.exporter.otlp.proto.common._exporter_metrics import (
+ create_exporter_metrics,
+)
from opentelemetry.exporter.otlp.proto.common._internal import (
_get_resource_data,
)
@@ -50,7 +42,8 @@ from opentelemetry.exporter.otlp.proto.http._common import (
_is_retryable,
_load_session_from_envvar,
)
-from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import (
+from opentelemetry.metrics import MeterProvider
+from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import ( # noqa: F401
ExportMetricsServiceRequest,
)
from opentelemetry.proto.common.v1.common_pb2 import ( # noqa: F401
@@ -81,6 +74,7 @@ from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_METRICS_HEADERS,
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
OTEL_EXPORTER_OTLP_TIMEOUT,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
)
from opentelemetry.sdk.metrics._internal.aggregation import Aggregation
from opentelemetry.sdk.metrics.export import ( # noqa: F401
@@ -95,6 +89,12 @@ from opentelemetry.sdk.metrics.export import ( # noqa: F401
Histogram as HistogramType,
)
from opentelemetry.sdk.resources import Resource as SDKResource
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
+from opentelemetry.semconv.attributes.http_attributes import (
+ HTTP_RESPONSE_STATUS_CODE,
+)
from opentelemetry.util.re import parse_env_headers
_logger = logging.getLogger(__name__)
@@ -122,6 +122,8 @@ class OTLPMetricExporter(MetricExporter, OTLPMetricExporterMixin):
| None = None,
preferred_aggregation: dict[type, Aggregation] | None = None,
max_export_batch_size: int | None = None,
+ *,
+ meter_provider: MeterProvider | None = None,
):
"""OTLP HTTP metrics exporter
@@ -204,8 +206,19 @@ class OTLPMetricExporter(MetricExporter, OTLPMetricExporterMixin):
self._max_export_batch_size: int | None = max_export_batch_size
self._shutdown = False
+ self._metrics = create_exporter_metrics(
+ OtelComponentTypeValues.OTLP_HTTP_METRIC_EXPORTER,
+ "metrics",
+ urlparse(self._endpoint),
+ meter_provider,
+ os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "")
+ .strip()
+ .lower()
+ == "true",
+ )
+
def _export(
- self, serialized_data: bytes, timeout_sec: Optional[float] = None
+ self, serialized_data: bytes, timeout_sec: float | None = None
):
data = serialized_data
if self._compression == Compression.Gzip:
@@ -245,6 +258,7 @@ class OTLPMetricExporter(MetricExporter, OTLPMetricExporterMixin):
self,
export_request: ExportMetricsServiceRequest,
deadline_sec: float,
+ num_items: int,
) -> MetricExportResult:
"""Export serialized data with retry logic until success, non-transient error, or exponential backoff maxed out.
@@ -255,68 +269,94 @@ class OTLPMetricExporter(MetricExporter, OTLPMetricExporterMixin):
Returns:
MetricExportResult: SUCCESS if export succeeded, FAILURE otherwise
"""
- serialized_data = export_request.SerializeToString()
- for retry_num in range(_MAX_RETRYS):
- # multiplying by a random number between .8 and 1.2 introduces a +/20% jitter to each backoff.
- backoff_seconds = 2**retry_num * random.uniform(0.8, 1.2)
- try:
- resp = self._export(serialized_data, deadline_sec - time())
- if resp.ok:
- return MetricExportResult.SUCCESS
- except requests.exceptions.RequestException as error:
- reason = error
- retryable = isinstance(error, ConnectionError)
- status_code = None
- else:
- reason = resp.reason
- retryable = _is_retryable(resp)
- status_code = resp.status_code
-
- if not retryable:
- _logger.error(
- "Failed to export metrics batch code: %s, reason: %s",
- status_code,
+ with self._metrics.export_operation(num_items) as result:
+ serialized_data = export_request.SerializeToString()
+ deadline_sec = time() + self._timeout
+ for retry_num in range(_MAX_RETRYS):
+ # multiplying by a random number between .8 and 1.2 introduces a +/20% jitter to each backoff.
+ backoff_seconds = 2**retry_num * random.uniform(0.8, 1.2)
+ export_error: Exception | None = None
+ try:
+ resp = self._export(serialized_data, deadline_sec - time())
+ if resp.ok:
+ return MetricExportResult.SUCCESS
+ except requests.exceptions.RequestException as error:
+ reason = error
+ export_error = error
+ retryable = isinstance(error, ConnectionError)
+ status_code = None
+ else:
+ reason = resp.reason
+ retryable = _is_retryable(resp)
+ status_code = resp.status_code
+
+ if not retryable:
+ _logger.error(
+ "Failed to export metrics batch code: %s, reason: %s",
+ status_code,
+ reason,
+ )
+ error_attrs = (
+ {HTTP_RESPONSE_STATUS_CODE: status_code}
+ if status_code is not None
+ else None
+ )
+ result.error = export_error
+ result.error_attrs = error_attrs
+ return MetricExportResult.FAILURE
+ if (
+ retry_num + 1 == _MAX_RETRYS
+ or backoff_seconds > (deadline_sec - time())
+ or self._shutdown
+ ):
+ _logger.error(
+ "Failed to export metrics batch due to timeout, "
+ "max retries or shutdown."
+ )
+ error_attrs = (
+ {HTTP_RESPONSE_STATUS_CODE: status_code}
+ if status_code is not None
+ else None
+ )
+ result.error = export_error
+ result.error_attrs = error_attrs
+ return MetricExportResult.FAILURE
+
+ _logger.warning(
+ "Transient error %s encountered while exporting metrics batch, retrying in %.2fs.",
reason,
+ backoff_seconds,
)
- return MetricExportResult.FAILURE
- if (
- retry_num + 1 == _MAX_RETRYS
- or backoff_seconds > (deadline_sec - time())
- or self._shutdown
- ):
- _logger.error(
- "Failed to export metrics batch due to timeout, "
- "max retries or shutdown."
- )
- return MetricExportResult.FAILURE
-
- _logger.warning(
- "Transient error %s encountered while exporting metrics batch, retrying in %.2fs.",
- reason,
- backoff_seconds,
- )
- shutdown = self._shutdown_in_progress.wait(backoff_seconds)
- if shutdown:
- _logger.warning("Shutdown in progress, aborting retry.")
- break
- return MetricExportResult.FAILURE
+ shutdown = self._shutdown_in_progress.wait(backoff_seconds)
+ if shutdown:
+ _logger.warning("Shutdown in progress, aborting retry.")
+ break
+ return MetricExportResult.FAILURE
def export(
self,
metrics_data: MetricsData,
- timeout_millis: Optional[float] = 10000,
+ timeout_millis: float | None = 10000,
**kwargs,
) -> MetricExportResult:
if self._shutdown:
_logger.warning("Exporter already shutdown, ignoring batch")
return MetricExportResult.FAILURE
+ num_items = 0
+ for resource_metrics in metrics_data.resource_metrics:
+ for scope_metrics in resource_metrics.scope_metrics:
+ for metric in scope_metrics.metrics:
+ num_items += len(metric.data.data_points)
+
export_request = encode_metrics(metrics_data)
deadline_sec = time() + self._timeout
# If no batch size configured, export as single batch with retries as configured
if self._max_export_batch_size is None:
- return self._export_with_retries(export_request, deadline_sec)
+ return self._export_with_retries(
+ export_request, deadline_sec, num_items
+ )
# Else, export in batches of configured size
batched_export_requests = _split_metrics_data(
@@ -327,6 +367,7 @@ class OTLPMetricExporter(MetricExporter, OTLPMetricExporterMixin):
export_result = self._export_with_retries(
split_metrics_data,
deadline_sec,
+ num_items,
)
if export_result != MetricExportResult.SUCCESS:
return MetricExportResult.FAILURE
@@ -350,6 +391,18 @@ class OTLPMetricExporter(MetricExporter, OTLPMetricExporterMixin):
"""Nothing is buffered in this exporter, so this method does nothing."""
return True
+ def set_meter_provider(self, meter_provider: MeterProvider) -> None:
+ self._metrics = create_exporter_metrics(
+ OtelComponentTypeValues.OTLP_HTTP_METRIC_EXPORTER,
+ "metrics",
+ urlparse(self._endpoint),
+ meter_provider,
+ os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "")
+ .strip()
+ .lower()
+ == "true",
+ )
+
def _split_metrics_data(
metrics_data: ExportMetricsServiceRequest,
@@ -503,8 +556,8 @@ def _split_metrics_data(
def _get_split_resource_metrics_pb2(
- split_resource_metrics: List[Dict],
-) -> List[pb2.ResourceMetrics]:
+ split_resource_metrics: list[dict],
+) -> list[pb2.ResourceMetrics]:
"""Helper that returns a list of pb2.ResourceMetrics objects based on split_resource_metrics.
Example input:
@@ -664,10 +717,10 @@ def _get_split_resource_metrics_pb2(
"Use one of the encoders from opentelemetry-exporter-otlp-proto-common instead. Deprecated since version 1.18.0.",
)
def get_resource_data(
- sdk_resource_scope_data: Dict[SDKResource, Any], # ResourceDataT?
+ sdk_resource_scope_data: dict[SDKResource, Any], # ResourceDataT?
resource_class: Callable[..., PB2Resource],
name: str,
-) -> List[PB2Resource]:
+) -> list[PB2Resource]:
return _get_resource_data(sdk_resource_scope_data, resource_class, name)
diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py
index d02f94a..2be2401 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py
@@ -1,30 +1,24 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import gzip
import logging
+import os
import random
import threading
import zlib
+from collections.abc import Sequence
from io import BytesIO
from os import environ
from time import time
-from typing import Dict, Optional, Sequence
+from urllib.parse import urlparse
import requests
from requests.exceptions import ConnectionError
+from opentelemetry.exporter.otlp.proto.common._exporter_metrics import (
+ create_exporter_metrics,
+)
from opentelemetry.exporter.otlp.proto.common.trace_encoder import (
encode_spans,
)
@@ -36,6 +30,7 @@ from opentelemetry.exporter.otlp.proto.http._common import (
_is_retryable,
_load_session_from_envvar,
)
+from opentelemetry.metrics import MeterProvider
from opentelemetry.sdk.environment_variables import (
_OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER,
OTEL_EXPORTER_OTLP_CERTIFICATE,
@@ -52,9 +47,16 @@ from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
OTEL_EXPORTER_OTLP_TRACES_HEADERS,
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
)
from opentelemetry.sdk.trace import ReadableSpan
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
+from opentelemetry.semconv.attributes.http_attributes import (
+ HTTP_RESPONSE_STATUS_CODE,
+)
from opentelemetry.util.re import parse_env_headers
_logger = logging.getLogger(__name__)
@@ -70,14 +72,16 @@ _MAX_RETRYS = 6
class OTLPSpanExporter(SpanExporter):
def __init__(
self,
- endpoint: Optional[str] = None,
- certificate_file: Optional[str] = None,
- client_key_file: Optional[str] = None,
- client_certificate_file: Optional[str] = None,
- headers: Optional[Dict[str, str]] = None,
- timeout: Optional[float] = None,
- compression: Optional[Compression] = None,
- session: Optional[requests.Session] = None,
+ endpoint: str | None = None,
+ certificate_file: str | None = None,
+ client_key_file: str | None = None,
+ client_certificate_file: str | None = None,
+ headers: dict[str, str] | None = None,
+ timeout: float | None = None,
+ compression: Compression | None = None,
+ session: requests.Session | None = None,
+ *,
+ meter_provider: MeterProvider | None = None,
):
self._shutdown_in_progress = threading.Event()
self._endpoint = endpoint or environ.get(
@@ -134,8 +138,19 @@ class OTLPSpanExporter(SpanExporter):
)
self._shutdown = False
+ self._metrics = create_exporter_metrics(
+ OtelComponentTypeValues.OTLP_HTTP_SPAN_EXPORTER,
+ "traces",
+ urlparse(self._endpoint),
+ meter_provider,
+ os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "")
+ .strip()
+ .lower()
+ == "true",
+ )
+
def _export(
- self, serialized_data: bytes, timeout_sec: Optional[float] = None
+ self, serialized_data: bytes, timeout_sec: float | None = None
):
data = serialized_data
if self._compression == Compression.Gzip:
@@ -176,52 +191,69 @@ class OTLPSpanExporter(SpanExporter):
_logger.warning("Exporter already shutdown, ignoring batch")
return SpanExportResult.FAILURE
- serialized_data = encode_spans(spans).SerializePartialToString()
- deadline_sec = time() + self._timeout
- for retry_num in range(_MAX_RETRYS):
- # multiplying by a random number between .8 and 1.2 introduces a +/20% jitter to each backoff.
- backoff_seconds = 2**retry_num * random.uniform(0.8, 1.2)
- try:
- resp = self._export(serialized_data, deadline_sec - time())
- if resp.ok:
- return SpanExportResult.SUCCESS
- except requests.exceptions.RequestException as error:
- reason = error
- retryable = isinstance(error, ConnectionError)
- status_code = None
- else:
- reason = resp.reason
- retryable = _is_retryable(resp)
- status_code = resp.status_code
-
- if not retryable:
- _logger.error(
- "Failed to export span batch code: %s, reason: %s",
- status_code,
+ with self._metrics.export_operation(len(spans)) as result:
+ serialized_data = encode_spans(spans).SerializePartialToString()
+ deadline_sec = time() + self._timeout
+ for retry_num in range(_MAX_RETRYS):
+ # multiplying by a random number between .8 and 1.2 introduces a +/20% jitter to each backoff.
+ backoff_seconds = 2**retry_num * random.uniform(0.8, 1.2)
+ export_error: Exception | None = None
+ try:
+ resp = self._export(serialized_data, deadline_sec - time())
+ if resp.ok:
+ return SpanExportResult.SUCCESS
+ except requests.exceptions.RequestException as error:
+ reason = error
+ export_error = error
+ retryable = isinstance(error, ConnectionError)
+ status_code = None
+ else:
+ reason = resp.reason
+ retryable = _is_retryable(resp)
+ status_code = resp.status_code
+
+ if not retryable:
+ _logger.error(
+ "Failed to export span batch code: %s, reason: %s",
+ status_code,
+ reason,
+ )
+ error_attrs = (
+ {HTTP_RESPONSE_STATUS_CODE: status_code}
+ if status_code is not None
+ else None
+ )
+ result.error = export_error
+ result.error_attrs = error_attrs
+ return SpanExportResult.FAILURE
+
+ if (
+ retry_num + 1 == _MAX_RETRYS
+ or backoff_seconds > (deadline_sec - time())
+ or self._shutdown
+ ):
+ _logger.error(
+ "Failed to export span batch due to timeout, "
+ "max retries or shutdown."
+ )
+ error_attrs = (
+ {HTTP_RESPONSE_STATUS_CODE: status_code}
+ if status_code is not None
+ else None
+ )
+ result.error = export_error
+ result.error_attrs = error_attrs
+ return SpanExportResult.FAILURE
+ _logger.warning(
+ "Transient error %s encountered while exporting span batch, retrying in %.2fs.",
reason,
+ backoff_seconds,
)
- return SpanExportResult.FAILURE
-
- if (
- retry_num + 1 == _MAX_RETRYS
- or backoff_seconds > (deadline_sec - time())
- or self._shutdown
- ):
- _logger.error(
- "Failed to export span batch due to timeout, "
- "max retries or shutdown."
- )
- return SpanExportResult.FAILURE
- _logger.warning(
- "Transient error %s encountered while exporting span batch, retrying in %.2fs.",
- reason,
- backoff_seconds,
- )
- shutdown = self._shutdown_in_progress.wait(backoff_seconds)
- if shutdown:
- _logger.warning("Shutdown in progress, aborting retry.")
- break
- return SpanExportResult.FAILURE
+ shutdown = self._shutdown_in_progress.wait(backoff_seconds)
+ if shutdown:
+ _logger.warning("Shutdown in progress, aborting retry.")
+ break
+ return SpanExportResult.FAILURE
def shutdown(self):
if self._shutdown:
diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py
index aec46da..252dab8 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import logging # noqa: F401
from collections import abc # noqa: F401
-from typing import Any, List, Optional, Sequence # noqa: F401
+from collections.abc import Sequence # noqa: F401
from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ( # noqa: F401
ExportTraceServiceRequest as PB2ExportTraceServiceRequest,
diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version/__init__.py
index 49e1254..a400b9b 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.0"
+__version__ = "1.42.1"
diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/test-requirements.txt b/exporter/opentelemetry-exporter-otlp-proto-http/test-requirements.txt
index 9592ce3..c49d477 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-http/test-requirements.txt
+++ b/exporter/opentelemetry-exporter-otlp-proto-http/test-requirements.txt
@@ -3,7 +3,6 @@ certifi==2024.7.4
charset-normalizer==3.3.2
googleapis-common-protos==1.70.0
idna==3.7
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
@@ -17,7 +16,6 @@ tomli==2.0.1
typing_extensions==4.12.0
urllib3==2.2.2
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e tests/opentelemetry-test-utils
-e exporter/opentelemetry-exporter-otlp-proto-common
diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/tests/metrics/test_otlp_metrics_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-http/tests/metrics/test_otlp_metrics_exporter.py
index 8fe57f3..84a11e8 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-http/tests/metrics/test_otlp_metrics_exporter.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-http/tests/metrics/test_otlp_metrics_exporter.py
@@ -1,23 +1,11 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-lines
import threading
import time
from logging import WARNING
from os import environ
-from typing import List
from unittest import TestCase
from unittest.mock import ANY, MagicMock, Mock, patch
@@ -69,10 +57,12 @@ from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE,
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
OTEL_EXPORTER_OTLP_TIMEOUT,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
)
from opentelemetry.sdk.metrics import (
Counter,
Histogram,
+ MeterProvider,
ObservableCounter,
ObservableGauge,
ObservableUpDownCounter,
@@ -80,6 +70,7 @@ from opentelemetry.sdk.metrics import (
)
from opentelemetry.sdk.metrics.export import (
AggregationTemporality,
+ InMemoryMetricReader,
MetricExportResult,
MetricsData,
ResourceMetrics,
@@ -104,10 +95,14 @@ OS_ENV_HEADERS = "envHeader1=val1,envHeader2=val2,User-agent=Overridden"
OS_ENV_TIMEOUT = "30"
-# pylint: disable=protected-access
+# pylint: disable=protected-access,too-many-public-methods
class TestOTLPMetricExporter(TestCase):
# pylint: disable=too-many-public-methods
def setUp(self):
+ self.metric_reader = InMemoryMetricReader()
+ self.meter_provider = MeterProvider(
+ metric_readers=[self.metric_reader]
+ )
self.metrics = {
"sum_int": MetricsData(
resource_metrics=[
@@ -328,6 +323,9 @@ class TestOTLPMetricExporter(TestCase):
),
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: " true "}
+ )
@patch.object(Session, "post")
def test_success(self, mock_post):
resp = Response()
@@ -335,12 +333,40 @@ class TestOTLPMetricExporter(TestCase):
mock_post.return_value = resp
exporter = OTLPMetricExporter()
+ exporter.set_meter_provider(self.meter_provider)
self.assertEqual(
exporter.export(self.metrics["sum_int"]),
MetricExportResult.SUCCESS,
)
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(
+ metrics[0].name, "otel.sdk.exporter.metric_data_point.exported"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[1].name, "otel.sdk.exporter.metric_data_point.inflight"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[2].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
@patch.object(Session, "post")
def test_failure(self, mock_post):
resp = Response()
@@ -348,12 +374,60 @@ class TestOTLPMetricExporter(TestCase):
mock_post.return_value = resp
exporter = OTLPMetricExporter()
+ exporter.set_meter_provider(self.meter_provider)
self.assertEqual(
exporter.export(self.metrics["sum_int"]),
MetricExportResult.FAILURE,
)
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(
+ metrics[0].name, "otel.sdk.exporter.metric_data_point.exported"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[0].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[0].data.data_points[0].attributes,
+ )
+ self.assertEqual(
+ metrics[1].name, "otel.sdk.exporter.metric_data_point.inflight"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[1].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[1].data.data_points[0].attributes,
+ )
+ self.assertEqual(
+ metrics[2].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[2].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[2]
+ .data.data_points[0]
+ .attributes["http.response.status_code"],
+ 401,
+ )
+
@patch.object(Session, "post")
def test_serialization(self, mock_post):
resp = Response()
@@ -399,7 +473,7 @@ class TestOTLPMetricExporter(TestCase):
),
]
)
- split_metrics_data: List[ExportMetricsServiceRequest] = list(
+ split_metrics_data: list[ExportMetricsServiceRequest] = list(
# pylint: disable=protected-access
_split_metrics_data(
metrics_data=metrics_data,
@@ -478,7 +552,7 @@ class TestOTLPMetricExporter(TestCase):
]
)
- split_metrics_data: List[ExportMetricsServiceRequest] = list(
+ split_metrics_data: list[ExportMetricsServiceRequest] = list(
# pylint: disable=protected-access
_split_metrics_data(
metrics_data=metrics_data,
@@ -570,7 +644,7 @@ class TestOTLPMetricExporter(TestCase):
]
)
- split_metrics_data: List[ExportMetricsServiceRequest] = list(
+ split_metrics_data: list[ExportMetricsServiceRequest] = list(
# pylint: disable=protected-access
_split_metrics_data(
metrics_data=metrics_data,
@@ -1197,6 +1271,21 @@ class TestOTLPMetricExporter(TestCase):
MetricExportResult.SUCCESS,
)
+ @patch.dict("os.environ", {}, clear=True)
+ @patch.object(OTLPMetricExporter, "_export", return_value=Mock(ok=True))
+ def test_exporter_metrics_disabled_after_set_meter_provider(
+ self, _mock_export
+ ):
+ exporter = OTLPMetricExporter()
+ exporter.set_meter_provider(self.meter_provider)
+
+ self.assertEqual(
+ exporter.export(self.metrics["sum_int"]),
+ MetricExportResult.SUCCESS,
+ )
+
+ self.assertIsNone(self.metric_reader.get_metrics_data())
+
def test_preferred_aggregation_override(self):
histogram_aggregation = ExplicitBucketHistogramAggregation(
boundaries=[0.05, 0.1, 0.5, 1, 5, 10],
@@ -1212,9 +1301,14 @@ class TestOTLPMetricExporter(TestCase):
exporter._preferred_aggregation[Histogram], histogram_aggregation
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
@patch.object(Session, "post")
def test_retry_timeout(self, mock_post):
- exporter = OTLPMetricExporter(timeout=1.5)
+ exporter = OTLPMetricExporter(
+ timeout=1.5, meter_provider=self.meter_provider
+ )
resp = Response()
resp.status_code = 503
@@ -1237,6 +1331,53 @@ class TestOTLPMetricExporter(TestCase):
warning.records[0].message,
)
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(
+ metrics[0].name, "otel.sdk.exporter.metric_data_point.exported"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[0].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[0].data.data_points[0].attributes,
+ )
+ self.assertEqual(
+ metrics[1].name, "otel.sdk.exporter.metric_data_point.inflight"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[1].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[1].data.data_points[0].attributes,
+ )
+ self.assertEqual(
+ metrics[2].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[2].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[2]
+ .data.data_points[0]
+ .attributes["http.response.status_code"],
+ 503,
+ )
+
@patch.object(Session, "post")
def test_export_no_collector_available_retryable(self, mock_post):
exporter = OTLPMetricExporter(timeout=1.5)
@@ -1316,9 +1457,21 @@ class TestOTLPMetricExporter(TestCase):
assert after - before < 0.2
+ def assert_standard_metric_attrs(self, attributes):
+ self.assertEqual(
+ attributes["otel.component.type"], "otlp_http_metric_exporter"
+ )
+ self.assertTrue(
+ attributes["otel.component.name"].startswith(
+ "otlp_http_metric_exporter/"
+ )
+ )
+ self.assertEqual(attributes["server.address"], "localhost")
+ self.assertEqual(attributes["server.port"], 4318)
+
def _resource_metrics(
- index: int, scope_metrics: List[pb2.ScopeMetrics]
+ index: int, scope_metrics: list[pb2.ScopeMetrics]
) -> pb2.ResourceMetrics:
return pb2.ResourceMetrics(
resource={
@@ -1329,7 +1482,7 @@ def _resource_metrics(
)
-def _scope_metrics(index: int, metrics: List[pb2.Metric]) -> pb2.ScopeMetrics:
+def _scope_metrics(index: int, metrics: list[pb2.Metric]) -> pb2.ScopeMetrics:
return pb2.ScopeMetrics(
scope=InstrumentationScope(name=f"scope_{index}"),
schema_url=f"scope_url_{index}",
@@ -1337,7 +1490,7 @@ def _scope_metrics(index: int, metrics: List[pb2.Metric]) -> pb2.ScopeMetrics:
)
-def _gauge(index: int, data_points: List[pb2.NumberDataPoint]) -> pb2.Metric:
+def _gauge(index: int, data_points: list[pb2.NumberDataPoint]) -> pb2.Metric:
return pb2.Metric(
name=f"gauge_{index}",
description="description",
diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py
index c86ac1f..d7f3592 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access
@@ -18,7 +7,6 @@ import threading
import time
import unittest
from logging import WARNING
-from typing import List
from unittest.mock import MagicMock, Mock, patch
import requests
@@ -58,7 +46,10 @@ from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
OTEL_EXPORTER_OTLP_TIMEOUT,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
)
+from opentelemetry.sdk.metrics import MeterProvider
+from opentelemetry.sdk.metrics.export import InMemoryMetricReader
from opentelemetry.sdk.resources import Resource as SDKResource
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
from opentelemetry.test.mock_test_classes import IterEntryPoint
@@ -78,6 +69,12 @@ ENV_TIMEOUT = "30"
class TestOTLPHTTPLogExporter(unittest.TestCase):
+ def setUp(self):
+ self.metric_reader = InMemoryMetricReader()
+ self.meter_provider = MeterProvider(
+ metric_readers=[self.metric_reader]
+ )
+
def test_constructor_default(self):
exporter = OTLPLogExporter()
@@ -350,7 +347,7 @@ class TestOTLPHTTPLogExporter(unittest.TestCase):
self.fail("No log records found")
@staticmethod
- def _get_sdk_log_data() -> List[ReadWriteLogRecord]:
+ def _get_sdk_log_data() -> list[ReadWriteLogRecord]:
ctx_log1 = set_span_in_context(
NonRecordingSpan(
SpanContext(
@@ -459,9 +456,14 @@ class TestOTLPHTTPLogExporter(unittest.TestCase):
LogRecordExportResult.SUCCESS,
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: " true "}
+ )
@patch.object(Session, "post")
def test_retry_timeout(self, mock_post):
- exporter = OTLPLogExporter(timeout=1.5)
+ exporter = OTLPLogExporter(
+ timeout=1.5, meter_provider=self.meter_provider
+ )
resp = Response()
resp.status_code = 503
@@ -484,6 +486,49 @@ class TestOTLPHTTPLogExporter(unittest.TestCase):
warning.records[0].message,
)
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(metrics[0].name, "otel.sdk.exporter.log.exported")
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[0].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[0].data.data_points[0].attributes,
+ )
+ self.assertEqual(metrics[1].name, "otel.sdk.exporter.log.inflight")
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[1].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[1].data.data_points[0].attributes,
+ )
+ self.assertEqual(
+ metrics[2].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[2].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[2]
+ .data.data_points[0]
+ .attributes["http.response.status_code"],
+ 503,
+ )
+
@patch.object(Session, "post")
def test_export_no_collector_available_retryable(self, mock_post):
exporter = OTLPLogExporter(timeout=1.5)
@@ -502,9 +547,14 @@ class TestOTLPHTTPLogExporter(unittest.TestCase):
warning.records[0].message,
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
@patch.object(Session, "post")
def test_export_no_collector_available(self, mock_post):
- exporter = OTLPLogExporter(timeout=1.5)
+ exporter = OTLPLogExporter(
+ timeout=1.5, meter_provider=self.meter_provider
+ )
mock_post.side_effect = requests.exceptions.RequestException()
with self.assertLogs(level=WARNING) as warning:
@@ -518,6 +568,49 @@ class TestOTLPHTTPLogExporter(unittest.TestCase):
warning.records[0].message,
)
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(metrics[0].name, "otel.sdk.exporter.log.exported")
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[0].data.data_points[0].attributes["error.type"],
+ "RequestException",
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[0].data.data_points[0].attributes,
+ )
+ self.assertEqual(metrics[1].name, "otel.sdk.exporter.log.inflight")
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[1].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[1].data.data_points[0].attributes,
+ )
+ self.assertEqual(
+ metrics[2].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[2].data.data_points[0].attributes["error.type"],
+ "RequestException",
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[2].data.data_points[0].attributes,
+ )
+
@patch.object(Session, "post")
def test_timeout_set_correctly(self, mock_post):
resp = Response()
@@ -562,3 +655,15 @@ class TestOTLPHTTPLogExporter(unittest.TestCase):
)
assert after - before < 0.2
+
+ def assert_standard_metric_attrs(self, attributes):
+ self.assertEqual(
+ attributes["otel.component.type"], "otlp_http_log_exporter"
+ )
+ self.assertTrue(
+ attributes["otel.component.name"].startswith(
+ "otlp_http_log_exporter/"
+ )
+ )
+ self.assertEqual(attributes["server.address"], "localhost")
+ self.assertEqual(attributes["server.port"], 4318)
diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py
index 5f61344..1580e5a 100644
--- a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import threading
import time
@@ -48,7 +37,10 @@ from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
OTEL_EXPORTER_OTLP_TRACES_HEADERS,
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
)
+from opentelemetry.sdk.metrics import MeterProvider
+from opentelemetry.sdk.metrics.export import InMemoryMetricReader
from opentelemetry.sdk.trace import _Span
from opentelemetry.sdk.trace.export import SpanExportResult
from opentelemetry.test.mock_test_classes import IterEntryPoint
@@ -73,6 +65,12 @@ BASIC_SPAN = _Span(
# pylint: disable=protected-access
class TestOTLPSpanExporter(unittest.TestCase):
+ def setUp(self):
+ self.metric_reader = InMemoryMetricReader()
+ self.meter_provider = MeterProvider(
+ metric_readers=[self.metric_reader]
+ )
+
def test_constructor_default(self):
exporter = OTLPSpanExporter()
@@ -279,9 +277,25 @@ class TestOTLPSpanExporter(unittest.TestCase):
OTLPSpanExporter().export(MagicMock()), SpanExportResult.SUCCESS
)
+ @patch.dict("os.environ", {}, clear=True)
+ @patch.object(OTLPSpanExporter, "_export", return_value=Mock(ok=True))
+ def test_exporter_metrics_disabled_by_default(self, _mock_export):
+ exporter = OTLPSpanExporter(meter_provider=self.meter_provider)
+
+ self.assertEqual(
+ exporter.export([BASIC_SPAN]), SpanExportResult.SUCCESS
+ )
+
+ self.assertIsNone(self.metric_reader.get_metrics_data())
+
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: " true "}
+ )
@patch.object(Session, "post")
def test_retry_timeout(self, mock_post):
- exporter = OTLPSpanExporter(timeout=1.5)
+ exporter = OTLPSpanExporter(
+ timeout=1.5, meter_provider=self.meter_provider
+ )
resp = Response()
resp.status_code = 503
@@ -304,6 +318,49 @@ class TestOTLPSpanExporter(unittest.TestCase):
warning.records[0].message,
)
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(
+ metrics[0].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[0].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[0]
+ .data.data_points[0]
+ .attributes["http.response.status_code"],
+ 503,
+ )
+ self.assertEqual(metrics[1].name, "otel.sdk.exporter.span.exported")
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[1].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[1].data.data_points[0].attributes,
+ )
+ self.assertEqual(metrics[2].name, "otel.sdk.exporter.span.inflight")
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[2].data.data_points[0].attributes,
+ )
+
@patch.object(Session, "post")
def test_export_no_collector_available_retryable(self, mock_post):
exporter = OTLPSpanExporter(timeout=1.5)
@@ -322,9 +379,14 @@ class TestOTLPSpanExporter(unittest.TestCase):
warning.records[0].message,
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
@patch.object(Session, "post")
def test_export_no_collector_available(self, mock_post):
- exporter = OTLPSpanExporter(timeout=1.5)
+ exporter = OTLPSpanExporter(
+ timeout=1.5, meter_provider=self.meter_provider
+ )
mock_post.side_effect = requests.exceptions.RequestException()
with self.assertLogs(level=WARNING) as warning:
@@ -338,6 +400,49 @@ class TestOTLPSpanExporter(unittest.TestCase):
warning.records[0].message,
)
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(
+ metrics[0].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[0].data.data_points[0].attributes["error.type"],
+ "RequestException",
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[0].data.data_points[0].attributes,
+ )
+ self.assertEqual(metrics[1].name, "otel.sdk.exporter.span.exported")
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[1].data.data_points[0].attributes["error.type"],
+ "RequestException",
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[1].data.data_points[0].attributes,
+ )
+ self.assertEqual(metrics[2].name, "otel.sdk.exporter.span.inflight")
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "http.response.status_code",
+ metrics[2].data.data_points[0].attributes,
+ )
+
@patch.object(Session, "post")
def test_timeout_set_correctly(self, mock_post):
resp = Response()
@@ -380,3 +485,15 @@ class TestOTLPSpanExporter(unittest.TestCase):
)
assert after - before < 0.2
+
+ def assert_standard_metric_attrs(self, attributes):
+ self.assertEqual(
+ attributes["otel.component.type"], "otlp_http_span_exporter"
+ )
+ self.assertTrue(
+ attributes["otel.component.name"].startswith(
+ "otlp_http_span_exporter/"
+ )
+ )
+ self.assertEqual(attributes["server.address"], "localhost")
+ self.assertEqual(attributes["server.port"], 4318)
diff --git a/exporter/opentelemetry-exporter-otlp/pyproject.toml b/exporter/opentelemetry-exporter-otlp/pyproject.toml
index 2c34d3c..01c3308 100644
--- a/exporter/opentelemetry-exporter-otlp/pyproject.toml
+++ b/exporter/opentelemetry-exporter-otlp/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenTelemetry Collector Exporters"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -19,7 +19,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -28,8 +27,8 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
- "opentelemetry-exporter-otlp-proto-grpc == 1.41.0",
- "opentelemetry-exporter-otlp-proto-http == 1.41.0",
+ "opentelemetry-exporter-otlp-proto-grpc == 1.42.1",
+ "opentelemetry-exporter-otlp-proto-http == 1.42.1",
]
[project.entry-points.opentelemetry_logs_exporter]
diff --git a/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/version/__init__.py b/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/version/__init__.py
index 49e1254..a400b9b 100644
--- a/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/version/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.0"
+__version__ = "1.42.1"
diff --git a/exporter/opentelemetry-exporter-otlp/test-requirements.txt b/exporter/opentelemetry-exporter-otlp/test-requirements.txt
index 0b2ccf8..e1119b8 100644
--- a/exporter/opentelemetry-exporter-otlp/test-requirements.txt
+++ b/exporter/opentelemetry-exporter-otlp/test-requirements.txt
@@ -1,5 +1,4 @@
asgiref==3.7.2
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
@@ -8,7 +7,6 @@ pytest==7.4.4
tomli==2.0.1
typing_extensions==4.12.0
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e tests/opentelemetry-test-utils
-e exporter/opentelemetry-exporter-otlp-proto-common
diff --git a/exporter/opentelemetry-exporter-otlp/tests/test_otlp.py b/exporter/opentelemetry-exporter-otlp/tests/test_otlp.py
index 7e18002..315f427 100644
--- a/exporter/opentelemetry-exporter-otlp/tests/test_otlp.py
+++ b/exporter/opentelemetry-exporter-otlp/tests/test_otlp.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
diff --git a/exporter/opentelemetry-exporter-prometheus/pyproject.toml b/exporter/opentelemetry-exporter-prometheus/pyproject.toml
index b6355f3..53ebc23 100644
--- a/exporter/opentelemetry-exporter-prometheus/pyproject.toml
+++ b/exporter/opentelemetry-exporter-prometheus/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "Prometheus Metric Exporter for OpenTelemetry"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -19,7 +19,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -29,7 +28,7 @@ classifiers = [
dependencies = [
"opentelemetry-api ~= 1.12",
# DONOTMERGE: confirm that this will becomes ~= 1.21 in the next release
- "opentelemetry-sdk ~= 1.41.0",
+ "opentelemetry-sdk ~= 1.42.1",
"prometheus_client >= 0.5.0, < 1.0.0",
]
diff --git a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py
index 608d8f6..9b3d2ef 100644
--- a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py
+++ b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
This library allows export of metrics data to `Prometheus <https://prometheus.io/>`_.
@@ -63,13 +52,13 @@ API
"""
from collections import deque
+from collections.abc import Iterable, Sequence
from itertools import chain
from json import dumps
from logging import getLogger
from os import environ
-from typing import Deque, Dict, Iterable, Sequence, Tuple, Union
-from prometheus_client import start_http_server
+from prometheus_client import CollectorRegistry, start_http_server
from prometheus_client.core import (
REGISTRY,
CounterMetricFamily,
@@ -118,7 +107,7 @@ _TARGET_INFO_DESCRIPTION = "Target metadata"
def _convert_buckets(
bucket_counts: Sequence[int], explicit_bounds: Sequence[float]
-) -> Sequence[Tuple[str, int]]:
+) -> Sequence[tuple[str, int]]:
buckets = []
total_count = 0
for upper_bound, count in zip(
@@ -135,7 +124,11 @@ class PrometheusMetricReader(MetricReader):
"""Prometheus metric exporter for OpenTelemetry."""
def __init__(
- self, disable_target_info: bool = False, prefix: str = ""
+ self,
+ disable_target_info: bool = False,
+ prefix: str = "",
+ *,
+ registry: CollectorRegistry = REGISTRY,
) -> None:
super().__init__(
preferred_temporality={
@@ -151,7 +144,8 @@ class PrometheusMetricReader(MetricReader):
self._collector = _CustomCollector(
disable_target_info=disable_target_info, prefix=prefix
)
- REGISTRY.register(self._collector)
+ self._registry = registry
+ self._registry.register(self._collector)
self._collector._callback = self.collect
self._prefix = prefix
@@ -166,7 +160,7 @@ class PrometheusMetricReader(MetricReader):
self._collector.add_metrics_data(metrics_data)
def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None:
- REGISTRY.unregister(self._collector)
+ self._registry.unregister(self._collector)
class _CustomCollector:
@@ -178,7 +172,7 @@ class _CustomCollector:
def __init__(self, disable_target_info: bool = False, prefix: str = ""):
self._callback = None
- self._metrics_datas: Deque[MetricsData] = deque()
+ self._metrics_datas: deque[MetricsData] = deque()
self._disable_target_info = disable_target_info
self._target_info = None
self._prefix = prefix
@@ -224,7 +218,7 @@ class _CustomCollector:
def _translate_to_prometheus(
self,
metrics_data: MetricsData,
- metric_family_id_metric_family: Dict[str, PrometheusMetric],
+ metric_family_id_metric_family: dict[str, PrometheusMetric],
):
metrics = []
@@ -383,14 +377,14 @@ class _CustomCollector:
)
# pylint: disable=no-self-use
- def _check_value(self, value: Union[int, float, str, Sequence]) -> str:
+ def _check_value(self, value: int | float | str | Sequence) -> str:
"""Check the label value and return is appropriate representation"""
if not isinstance(value, str):
return dumps(value, default=str)
return str(value)
def _create_info_metric(
- self, name: str, description: str, attributes: Dict[str, str]
+ self, name: str, description: str, attributes: dict[str, str]
) -> InfoMetricFamily:
"""Create an Info Metric Family with list of attributes"""
# sanitize the attribute names according to Prometheus rule
diff --git a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/_mapping.py b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/_mapping.py
index 077d2fb..7822c69 100644
--- a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/_mapping.py
+++ b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/_mapping.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from re import UNICODE, compile
diff --git a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/version/__init__.py b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/version/__init__.py
index 766799c..23888c2 100644
--- a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/version/__init__.py
+++ b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "0.62b0"
+__version__ = "0.63b1"
diff --git a/exporter/opentelemetry-exporter-prometheus/test-requirements.txt b/exporter/opentelemetry-exporter-prometheus/test-requirements.txt
index 925eb34..b98ce32 100644
--- a/exporter/opentelemetry-exporter-prometheus/test-requirements.txt
+++ b/exporter/opentelemetry-exporter-prometheus/test-requirements.txt
@@ -1,5 +1,4 @@
asgiref==3.7.2
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
@@ -9,7 +8,6 @@ pytest==7.4.4
tomli==2.0.1
typing_extensions==4.10.0
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e opentelemetry-sdk
-e tests/opentelemetry-test-utils
diff --git a/exporter/opentelemetry-exporter-prometheus/tests/__init__.py b/exporter/opentelemetry-exporter-prometheus/tests/__init__.py
index b0a6f42..e57cf4a 100644
--- a/exporter/opentelemetry-exporter-prometheus/tests/__init__.py
+++ b/exporter/opentelemetry-exporter-prometheus/tests/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py b/exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py
index 96846e0..d9aafd3 100644
--- a/exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py
+++ b/exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=no-self-use
diff --git a/exporter/opentelemetry-exporter-prometheus/tests/test_mapping.py b/exporter/opentelemetry-exporter-prometheus/tests/test_mapping.py
index f2641de..0aef56f 100644
--- a/exporter/opentelemetry-exporter-prometheus/tests/test_mapping.py
+++ b/exporter/opentelemetry-exporter-prometheus/tests/test_mapping.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from unittest import TestCase
diff --git a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py
index 26770c9..d3d49ba 100644
--- a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py
+++ b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py
@@ -1,22 +1,11 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from textwrap import dedent
from unittest import TestCase
from unittest.mock import Mock, patch
-from prometheus_client import generate_latest
+from prometheus_client import CollectorRegistry, generate_latest
from prometheus_client.core import (
CounterMetricFamily,
GaugeMetricFamily,
@@ -47,7 +36,7 @@ from opentelemetry.test.metrictestutil import (
)
-class TestPrometheusMetricReader(TestCase):
+class TestPrometheusMetricReader(TestCase): # pylint: disable=too-many-public-methods
def setUp(self):
self._mock_registry_register = Mock()
self._registry_register_patch = patch(
@@ -55,6 +44,19 @@ class TestPrometheusMetricReader(TestCase):
side_effect=self._mock_registry_register,
)
+ def test_custom_registry(self):
+ with self._registry_register_patch:
+ custom_registry = CollectorRegistry()
+ reader = PrometheusMetricReader(registry=custom_registry)
+ # global REGISTRY should NOT be used
+ self._mock_registry_register.assert_not_called()
+ # check custom_registry was registered
+ self.assertIn(
+ reader._collector, # pylint: disable=protected-access
+ custom_registry._collector_to_names, # pylint: disable=protected-access
+ )
+ reader.shutdown()
+
def verify_text_format(
self, metric: Metric, expect_prometheus_text: str, prefix: str = ""
) -> None:
diff --git a/exporter/opentelemetry-exporter-zipkin-json/pyproject.toml b/exporter/opentelemetry-exporter-zipkin-json/pyproject.toml
index 92d2a88..339a76c 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/pyproject.toml
+++ b/exporter/opentelemetry-exporter-zipkin-json/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "Zipkin Span JSON Exporter for OpenTelemetry"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -19,7 +19,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
diff --git a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/encoder/__init__.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/encoder/__init__.py
index bb90daa..3811ccb 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/encoder/__init__.py
+++ b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/encoder/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""Zipkin Exporter Transport Encoder
@@ -20,8 +9,9 @@ Base module and abstract class for concrete transport encoders to extend.
import abc
import json
import logging
+from collections.abc import Sequence
from enum import Enum
-from typing import Any, Dict, List, Optional, Sequence, TypeVar
+from typing import Any, TypeVar
from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint
from opentelemetry.sdk.trace import Event
@@ -114,7 +104,7 @@ class Encoder(abc.ABC):
pass
@staticmethod
- def _get_parent_id(span_context) -> Optional[int]:
+ def _get_parent_id(span_context) -> int | None:
if isinstance(span_context, Span):
parent_id = span_context.parent.span_id
elif isinstance(span_context, SpanContext):
@@ -124,8 +114,8 @@ class Encoder(abc.ABC):
return parent_id
def _extract_tags_from_dict(
- self, tags_dict: Optional[Dict]
- ) -> Dict[str, str]:
+ self, tags_dict: dict | None
+ ) -> dict[str, str]:
tags = {}
if not tags_dict:
return tags
@@ -194,7 +184,7 @@ class Encoder(abc.ABC):
return json.dumps(tag_value_elements, separators=(",", ":"))
- def _extract_tags_from_span(self, span: Span) -> Dict[str, str]:
+ def _extract_tags_from_span(self, span: Span) -> dict[str, str]:
tags = self._extract_tags_from_dict(span.attributes)
if span.resource:
tags.update(self._extract_tags_from_dict(span.resource.attributes))
@@ -228,8 +218,8 @@ class Encoder(abc.ABC):
return tags
def _extract_annotations_from_events(
- self, events: Optional[List[Event]]
- ) -> Optional[List[Dict]]:
+ self, events: list[Event] | None
+ ) -> list[dict] | None:
if not events:
return None
@@ -280,7 +270,7 @@ class JsonEncoder(Encoder):
return json.dumps(encoded_spans)
@staticmethod
- def _encode_local_endpoint(local_endpoint: NodeEndpoint) -> Dict:
+ def _encode_local_endpoint(local_endpoint: NodeEndpoint) -> dict:
encoded_local_endpoint = {"serviceName": local_endpoint.service_name}
if local_endpoint.ipv4 is not None:
encoded_local_endpoint["ipv4"] = str(local_endpoint.ipv4)
diff --git a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/__init__.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/__init__.py
index ba313db..3e277cb 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/__init__.py
+++ b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
OpenTelemetry Zipkin JSON Exporter
@@ -73,8 +62,8 @@ API
"""
import logging
+from collections.abc import Sequence
from os import environ
-from typing import Optional, Sequence
import requests
@@ -100,13 +89,13 @@ class ZipkinExporter(SpanExporter):
def __init__(
self,
version: Protocol = Protocol.V2,
- endpoint: Optional[str] = None,
+ endpoint: str | None = None,
local_node_ipv4: IpInput = None,
local_node_ipv6: IpInput = None,
- local_node_port: Optional[int] = None,
- max_tag_value_length: Optional[int] = None,
- timeout: Optional[int] = None,
- session: Optional[requests.Session] = None,
+ local_node_port: int | None = None,
+ max_tag_value_length: int | None = None,
+ timeout: int | None = None,
+ session: requests.Session | None = None,
):
"""Zipkin exporter.
diff --git a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v1/__init__.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v1/__init__.py
index c44a2dd..1fb4e1a 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v1/__init__.py
+++ b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v1/__init__.py
@@ -1,21 +1,8 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""Zipkin Export Encoders for JSON formats"""
-from typing import Dict, List
-
from opentelemetry.exporter.zipkin.encoder import Encoder, JsonEncoder
from opentelemetry.trace import Span
@@ -23,8 +10,8 @@ from opentelemetry.trace import Span
# pylint: disable=W0223
class V1Encoder(Encoder):
def _extract_binary_annotations(
- self, span: Span, encoded_local_endpoint: Dict
- ) -> List[Dict]:
+ self, span: Span, encoded_local_endpoint: dict
+ ) -> list[dict]:
binary_annotations = []
for tag_key, tag_value in self._extract_tags_from_span(span).items():
if isinstance(tag_value, str) and self.max_tag_value_length > 0:
@@ -45,7 +32,7 @@ class JsonV1Encoder(JsonEncoder, V1Encoder):
API spec: https://github.com/openzipkin/zipkin-api/blob/master/zipkin-api.yaml
"""
- def _encode_span(self, span: Span, encoded_local_endpoint: Dict) -> Dict:
+ def _encode_span(self, span: Span, encoded_local_endpoint: dict) -> dict:
context = span.get_span_context()
encoded_span = {
diff --git a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v2/__init__.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v2/__init__.py
index 579087c..a157fe7 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v2/__init__.py
+++ b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v2/__init__.py
@@ -1,21 +1,8 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""Zipkin Export Encoders for JSON formats"""
-from typing import Dict
-
from opentelemetry.exporter.zipkin.encoder import JsonEncoder
from opentelemetry.trace import Span, SpanKind
@@ -34,7 +21,7 @@ class JsonV2Encoder(JsonEncoder):
SpanKind.CONSUMER: "CONSUMER",
}
- def _encode_span(self, span: Span, encoded_local_endpoint: Dict) -> Dict:
+ def _encode_span(self, span: Span, encoded_local_endpoint: dict) -> dict:
context = span.get_span_context()
encoded_span = {
"traceId": self._encode_trace_id(context.trace_id),
diff --git a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version/__init__.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version/__init__.py
index 49e1254..a400b9b 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version/__init__.py
+++ b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.0"
+__version__ = "1.42.1"
diff --git a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/node_endpoint.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/node_endpoint.py
index 67f5d0a..0f85f6b 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/node_endpoint.py
+++ b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/node_endpoint.py
@@ -1,26 +1,14 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""Zipkin Exporter Endpoints"""
import ipaddress
-from typing import Optional, Union
from opentelemetry import trace
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
-IpInput = Union[str, int, None]
+IpInput = str | int | None
class NodeEndpoint:
@@ -37,7 +25,7 @@ class NodeEndpoint:
self,
ipv4: IpInput = None,
ipv6: IpInput = None,
- port: Optional[int] = None,
+ port: int | None = None,
):
self.ipv4 = ipv4
self.ipv6 = ipv6
@@ -53,7 +41,7 @@ class NodeEndpoint:
self.service_name = resource.attributes[SERVICE_NAME]
@property
- def ipv4(self) -> Optional[ipaddress.IPv4Address]:
+ def ipv4(self) -> ipaddress.IPv4Address | None:
return self._ipv4
@ipv4.setter
@@ -69,7 +57,7 @@ class NodeEndpoint:
self._ipv4 = ipv4_address
@property
- def ipv6(self) -> Optional[ipaddress.IPv6Address]:
+ def ipv6(self) -> ipaddress.IPv6Address | None:
return self._ipv6
@ipv6.setter
diff --git a/exporter/opentelemetry-exporter-zipkin-json/test-requirements.txt b/exporter/opentelemetry-exporter-zipkin-json/test-requirements.txt
index 87058ac..c8ddca0 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/test-requirements.txt
+++ b/exporter/opentelemetry-exporter-zipkin-json/test-requirements.txt
@@ -2,7 +2,6 @@ asgiref==3.7.2
certifi==2024.7.4
charset-normalizer==3.3.2
idna==3.7
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
@@ -13,7 +12,6 @@ tomli==2.0.1
typing_extensions==4.10.0
urllib3==2.2.2
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e opentelemetry-sdk
-e opentelemetry-semantic-conventions
diff --git a/exporter/opentelemetry-exporter-zipkin-json/tests/__init__.py b/exporter/opentelemetry-exporter-zipkin-json/tests/__init__.py
index b0a6f42..e57cf4a 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/tests/__init__.py
+++ b/exporter/opentelemetry-exporter-zipkin-json/tests/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/common_tests.py b/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/common_tests.py
index ada00c7..ff5fe4d 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/common_tests.py
+++ b/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/common_tests.py
@@ -1,19 +1,7 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import abc
import unittest
-from typing import Dict, List
from opentelemetry import trace as trace_api
from opentelemetry.exporter.zipkin.encoder import (
@@ -165,7 +153,7 @@ class CommonEncoderTestCases:
@staticmethod
def get_data_for_max_tag_length_test(
max_tag_length: int,
- ) -> (trace._Span, Dict):
+ ) -> (trace._Span, dict):
start_time = 683647322 * 10**9 # in ns
duration = 50 * 10**6
end_time = start_time + duration
@@ -327,7 +315,7 @@ class CommonEncoderTestCases:
return span, expected_outputs[max_tag_length]
@staticmethod
- def get_exhaustive_otel_span_list() -> List[trace._Span]:
+ def get_exhaustive_otel_span_list() -> list[trace._Span]:
trace_id = 0x6E0C63257DE34C926F9EFCD03927272E
base_time = 683647322 * 10**9 # in ns
diff --git a/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v1_json.py b/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v1_json.py
index 7ff4e9b..1df059e 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v1_json.py
+++ b/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v1_json.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import json
from opentelemetry import trace as trace_api
diff --git a/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v2_json.py b/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v2_json.py
index 37a0414..012562e 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v2_json.py
+++ b/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v2_json.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import json
from opentelemetry import trace as trace_api
diff --git a/exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py b/exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py
index 77e3ef5..c4d2b6b 100644
--- a/exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py
+++ b/exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import ipaddress
import os
diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/pyproject.toml b/exporter/opentelemetry-exporter-zipkin-proto-http/pyproject.toml
index 845a88b..7bec958 100644
--- a/exporter/opentelemetry-exporter-zipkin-proto-http/pyproject.toml
+++ b/exporter/opentelemetry-exporter-zipkin-proto-http/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "Zipkin Span Protobuf Exporter for OpenTelemetry"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -19,7 +19,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -29,7 +28,7 @@ classifiers = [
]
dependencies = [
"opentelemetry-api ~= 1.3",
- "opentelemetry-exporter-zipkin-json == 1.41.0",
+ "opentelemetry-exporter-zipkin-json == 1.42.1",
"opentelemetry-sdk ~= 1.11",
"protobuf ~= 3.12",
"requests ~= 2.7",
diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py
index dcb092c..124aa30 100644
--- a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py
+++ b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
OpenTelemetry Zipkin Protobuf Exporter
@@ -72,8 +61,8 @@ API
"""
import logging
+from collections.abc import Sequence
from os import environ
-from typing import Optional, Sequence
import requests
@@ -96,13 +85,13 @@ logger = logging.getLogger(__name__)
class ZipkinExporter(SpanExporter):
def __init__(
self,
- endpoint: Optional[str] = None,
+ endpoint: str | None = None,
local_node_ipv4: IpInput = None,
local_node_ipv6: IpInput = None,
- local_node_port: Optional[int] = None,
- max_tag_value_length: Optional[int] = None,
- timeout: Optional[int] = None,
- session: Optional[requests.Session] = None,
+ local_node_port: int | None = None,
+ max_tag_value_length: int | None = None,
+ timeout: int | None = None,
+ session: requests.Session | None = None,
):
"""Zipkin exporter.
diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/__init__.py b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/__init__.py
index d7ca3b8..0ddd7fc 100644
--- a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/__init__.py
+++ b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/__init__.py
@@ -1,23 +1,12 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""Zipkin Export Encoder for Protobuf
API spec: https://github.com/openzipkin/zipkin-api/blob/master/zipkin.proto
"""
-from typing import List, Optional, Sequence
+from collections.abc import Sequence
from opentelemetry.exporter.zipkin.encoder import Encoder
from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint
@@ -90,8 +79,8 @@ class ProtobufEncoder(Encoder):
return encoded_span
def _encode_annotations(
- self, span_events: Optional[List[Event]]
- ) -> Optional[List]:
+ self, span_events: list[Event] | None
+ ) -> list | None:
annotations = self._extract_annotations_from_events(span_events)
if annotations is None:
encoded_annotations = None
diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version/__init__.py b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version/__init__.py
index 49e1254..a400b9b 100644
--- a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version/__init__.py
+++ b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.0"
+__version__ = "1.42.1"
diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/test-requirements.txt b/exporter/opentelemetry-exporter-zipkin-proto-http/test-requirements.txt
index 1619da9..56edd9f 100644
--- a/exporter/opentelemetry-exporter-zipkin-proto-http/test-requirements.txt
+++ b/exporter/opentelemetry-exporter-zipkin-proto-http/test-requirements.txt
@@ -2,7 +2,6 @@ asgiref==3.7.2
certifi==2024.7.4
charset-normalizer==3.3.2
idna==3.7
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
@@ -14,7 +13,6 @@ tomli==2.0.1
typing_extensions==4.10.0
urllib3==2.2.2
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e exporter/opentelemetry-exporter-zipkin-json
-e opentelemetry-sdk
diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/__init__.py b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/__init__.py
index b0a6f42..e57cf4a 100644
--- a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/__init__.py
+++ b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/common_tests.py b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/common_tests.py
index ada00c7..ff5fe4d 100644
--- a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/common_tests.py
+++ b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/common_tests.py
@@ -1,19 +1,7 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import abc
import unittest
-from typing import Dict, List
from opentelemetry import trace as trace_api
from opentelemetry.exporter.zipkin.encoder import (
@@ -165,7 +153,7 @@ class CommonEncoderTestCases:
@staticmethod
def get_data_for_max_tag_length_test(
max_tag_length: int,
- ) -> (trace._Span, Dict):
+ ) -> (trace._Span, dict):
start_time = 683647322 * 10**9 # in ns
duration = 50 * 10**6
end_time = start_time + duration
@@ -327,7 +315,7 @@ class CommonEncoderTestCases:
return span, expected_outputs[max_tag_length]
@staticmethod
- def get_exhaustive_otel_span_list() -> List[trace._Span]:
+ def get_exhaustive_otel_span_list() -> list[trace._Span]:
trace_id = 0x6E0C63257DE34C926F9EFCD03927272E
base_time = 683647322 * 10**9 # in ns
diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/test_v2_protobuf.py b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/test_v2_protobuf.py
index 2f2c894..256cc3e 100644
--- a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/test_v2_protobuf.py
+++ b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/test_v2_protobuf.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import ipaddress
import json
diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/test_zipkin_exporter.py b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/test_zipkin_exporter.py
index 8a3c055..f2592fe 100644
--- a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/test_zipkin_exporter.py
+++ b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/test_zipkin_exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import ipaddress
import os
diff --git a/exporter/opentelemetry-exporter-zipkin/pyproject.toml b/exporter/opentelemetry-exporter-zipkin/pyproject.toml
index 76a1ecc..13e8ccc 100644
--- a/exporter/opentelemetry-exporter-zipkin/pyproject.toml
+++ b/exporter/opentelemetry-exporter-zipkin/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "Zipkin Span Exporters for OpenTelemetry"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -19,7 +19,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -28,8 +27,8 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
- "opentelemetry-exporter-zipkin-json == 1.41.0",
- "opentelemetry-exporter-zipkin-proto-http == 1.41.0",
+ "opentelemetry-exporter-zipkin-json == 1.42.1",
+ "opentelemetry-exporter-zipkin-proto-http == 1.42.1",
]
[project.entry-points.opentelemetry_traces_exporter]
diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/version/__init__.py b/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/version/__init__.py
index 49e1254..a400b9b 100644
--- a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/version/__init__.py
+++ b/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.0"
+__version__ = "1.42.1"
diff --git a/exporter/opentelemetry-exporter-zipkin/test-requirements.txt b/exporter/opentelemetry-exporter-zipkin/test-requirements.txt
index 1f9fd36..22d3ffc 100644
--- a/exporter/opentelemetry-exporter-zipkin/test-requirements.txt
+++ b/exporter/opentelemetry-exporter-zipkin/test-requirements.txt
@@ -1,5 +1,4 @@
asgiref==3.7.2
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
@@ -8,7 +7,6 @@ pytest==7.4.4
tomli==2.0.1
typing_extensions==4.10.0
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e exporter/opentelemetry-exporter-zipkin-json
-e exporter/opentelemetry-exporter-zipkin-proto-http
diff --git a/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin.py b/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin.py
index d8231af..6857d5e 100644
--- a/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin.py
+++ b/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
diff --git a/opentelemetry-api/pyproject.toml b/opentelemetry-api/pyproject.toml
index a75e158..e084b9e 100644
--- a/opentelemetry-api/pyproject.toml
+++ b/opentelemetry-api/pyproject.toml
@@ -7,7 +7,7 @@ name = "opentelemetry-api"
description = "OpenTelemetry Python API"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -17,7 +17,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -27,9 +26,6 @@ classifiers = [
]
dependencies = [
"typing-extensions >= 4.5.0",
- # FIXME This should be able to be removed after 3.12 is released if there is a reliable API
- # in importlib.metadata.
- "importlib-metadata >= 6.0, < 8.8.0",
]
dynamic = [
"version",
diff --git a/opentelemetry-api/src/opentelemetry/_events/__init__.py b/opentelemetry-api/src/opentelemetry/_events/__init__.py
index d86fd12..9c70b90 100644
--- a/opentelemetry-api/src/opentelemetry/_events/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/_events/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from abc import ABC, abstractmethod
from logging import getLogger
@@ -40,13 +29,13 @@ class Event(LogRecord):
def __init__(
self,
name: str,
- timestamp: Optional[int] = None,
- trace_id: Optional[int] = None,
- span_id: Optional[int] = None,
+ timestamp: int | None = None,
+ trace_id: int | None = None,
+ span_id: int | None = None,
trace_flags: Optional["TraceFlags"] = None,
- body: Optional[AnyValue] = None,
- severity_number: Optional[SeverityNumber] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ body: AnyValue | None = None,
+ severity_number: SeverityNumber | None = None,
+ attributes: _ExtendedAttributes | None = None,
):
attributes = attributes or {}
event_attributes = {
@@ -73,9 +62,9 @@ class EventLogger(ABC):
def __init__(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
):
self._name = name
self._version = version
@@ -104,9 +93,9 @@ class ProxyEventLogger(EventLogger):
def __init__(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
):
super().__init__(
name=name,
@@ -114,7 +103,7 @@ class ProxyEventLogger(EventLogger):
schema_url=schema_url,
attributes=attributes,
)
- self._real_event_logger: Optional[EventLogger] = None
+ self._real_event_logger: EventLogger | None = None
self._noop_event_logger = NoOpEventLogger(name)
@property
@@ -145,9 +134,9 @@ class EventLoggerProvider(ABC):
def get_event_logger(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
) -> EventLogger:
"""Returns an EventLoggerProvider for use."""
@@ -160,9 +149,9 @@ class NoOpEventLoggerProvider(EventLoggerProvider):
def get_event_logger(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
) -> EventLogger:
return NoOpEventLogger(
name, version=version, schema_url=schema_url, attributes=attributes
@@ -177,9 +166,9 @@ class ProxyEventLoggerProvider(EventLoggerProvider):
def get_event_logger(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
) -> EventLogger:
if _EVENT_LOGGER_PROVIDER:
return _EVENT_LOGGER_PROVIDER.get_event_logger(
@@ -197,7 +186,7 @@ class ProxyEventLoggerProvider(EventLoggerProvider):
_EVENT_LOGGER_PROVIDER_SET_ONCE = Once()
-_EVENT_LOGGER_PROVIDER: Optional[EventLoggerProvider] = None
+_EVENT_LOGGER_PROVIDER: EventLoggerProvider | None = None
_PROXY_EVENT_LOGGER_PROVIDER = ProxyEventLoggerProvider()
@@ -251,10 +240,10 @@ def set_event_logger_provider(
)
def get_event_logger(
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
- event_logger_provider: Optional[EventLoggerProvider] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
+ event_logger_provider: EventLoggerProvider | None = None,
) -> "EventLogger":
if event_logger_provider is None:
event_logger_provider = get_event_logger_provider()
diff --git a/opentelemetry-api/src/opentelemetry/_logs/__init__.py b/opentelemetry-api/src/opentelemetry/_logs/__init__.py
index 6215da2..1c4dfee 100644
--- a/opentelemetry-api/src/opentelemetry/_logs/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/_logs/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
The OpenTelemetry logging API describes the classes used to generate logs and events.
diff --git a/opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py b/opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py
index 780bcb4..2319a46 100644
--- a/opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
The OpenTelemetry logging API describes the classes used to generate logs and events.
@@ -39,7 +28,7 @@ from abc import ABC, abstractmethod
from logging import getLogger
from os import environ
from time import time_ns
-from typing import Optional, cast, overload
+from typing import cast, overload
from typing_extensions import deprecated
@@ -68,14 +57,15 @@ class LogRecord(ABC):
def __init__(
self,
*,
- timestamp: Optional[int] = None,
- observed_timestamp: Optional[int] = None,
- context: Optional[Context] = None,
- severity_text: Optional[str] = None,
- severity_number: Optional[SeverityNumber] = None,
+ timestamp: int | None = None,
+ observed_timestamp: int | None = None,
+ context: Context | None = None,
+ severity_text: str | None = None,
+ severity_number: SeverityNumber | None = None,
body: AnyValue = None,
- attributes: Optional[_ExtendedAttributes] = None,
- event_name: Optional[str] = None,
+ attributes: _ExtendedAttributes | None = None,
+ event_name: str | None = None,
+ exception: BaseException | None = None,
) -> None: ...
@overload
@@ -85,31 +75,32 @@ class LogRecord(ABC):
def __init__(
self,
*,
- timestamp: Optional[int] = None,
- observed_timestamp: Optional[int] = None,
- trace_id: Optional[int] = None,
- span_id: Optional[int] = None,
- trace_flags: Optional[TraceFlags] = None,
- severity_text: Optional[str] = None,
- severity_number: Optional[SeverityNumber] = None,
+ timestamp: int | None = None,
+ observed_timestamp: int | None = None,
+ trace_id: int | None = None,
+ span_id: int | None = None,
+ trace_flags: TraceFlags | None = None,
+ severity_text: str | None = None,
+ severity_number: SeverityNumber | None = None,
body: AnyValue = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ attributes: _ExtendedAttributes | None = None,
) -> None: ...
def __init__(
self,
*,
- timestamp: Optional[int] = None,
- observed_timestamp: Optional[int] = None,
- context: Optional[Context] = None,
- trace_id: Optional[int] = None,
- span_id: Optional[int] = None,
- trace_flags: Optional[TraceFlags] = None,
- severity_text: Optional[str] = None,
- severity_number: Optional[SeverityNumber] = None,
+ timestamp: int | None = None,
+ observed_timestamp: int | None = None,
+ context: Context | None = None,
+ trace_id: int | None = None,
+ span_id: int | None = None,
+ trace_flags: TraceFlags | None = None,
+ severity_text: str | None = None,
+ severity_number: SeverityNumber | None = None,
body: AnyValue = None,
- attributes: Optional[_ExtendedAttributes] = None,
- event_name: Optional[str] = None,
+ attributes: _ExtendedAttributes | None = None,
+ event_name: str | None = None,
+ exception: BaseException | None = None,
) -> None:
if not context:
context = get_current()
@@ -127,6 +118,7 @@ class LogRecord(ABC):
self.body = body
self.attributes = attributes
self.event_name = event_name
+ self.exception = exception
class Logger(ABC):
@@ -135,9 +127,9 @@ class Logger(ABC):
def __init__(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
) -> None:
super().__init__()
self._name = name
@@ -157,6 +149,7 @@ class Logger(ABC):
body: AnyValue | None = None,
attributes: _ExtendedAttributes | None = None,
event_name: str | None = None,
+ exception: BaseException | None = None,
) -> None: ...
@overload
@@ -178,6 +171,7 @@ class Logger(ABC):
body: AnyValue | None = None,
attributes: _ExtendedAttributes | None = None,
event_name: str | None = None,
+ exception: BaseException | None = None,
) -> None:
"""Emits a :class:`LogRecord` representing a log to the processing pipeline."""
@@ -200,6 +194,7 @@ class NoOpLogger(Logger):
body: AnyValue | None = None,
attributes: _ExtendedAttributes | None = None,
event_name: str | None = None,
+ exception: BaseException | None = None,
) -> None: ...
@overload
@@ -220,6 +215,7 @@ class NoOpLogger(Logger):
body: AnyValue | None = None,
attributes: _ExtendedAttributes | None = None,
event_name: str | None = None,
+ exception: BaseException | None = None,
) -> None:
pass
@@ -228,15 +224,15 @@ class ProxyLogger(Logger):
def __init__( # pylint: disable=super-init-not-called
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
):
self._name = name
self._version = version
self._schema_url = schema_url
self._attributes = attributes
- self._real_logger: Optional[Logger] = None
+ self._real_logger: Logger | None = None
self._noop_logger = NoOpLogger(name)
@property
@@ -266,6 +262,7 @@ class ProxyLogger(Logger):
body: AnyValue | None = None,
attributes: _ExtendedAttributes | None = None,
event_name: str | None = None,
+ exception: BaseException | None = None,
) -> None: ...
@overload
@@ -286,6 +283,7 @@ class ProxyLogger(Logger):
body: AnyValue | None = None,
attributes: _ExtendedAttributes | None = None,
event_name: str | None = None,
+ exception: BaseException | None = None,
) -> None:
if record:
self._logger.emit(record)
@@ -299,6 +297,7 @@ class ProxyLogger(Logger):
body=body,
attributes=attributes,
event_name=event_name,
+ exception=exception,
)
@@ -311,9 +310,9 @@ class LoggerProvider(ABC):
def get_logger(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
) -> Logger:
"""Returns a `Logger` for use by the given instrumentation library.
@@ -350,9 +349,9 @@ class NoOpLoggerProvider(LoggerProvider):
def get_logger(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
) -> Logger:
"""Returns a NoOpLogger."""
return NoOpLogger(
@@ -364,9 +363,9 @@ class ProxyLoggerProvider(LoggerProvider):
def get_logger(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
) -> Logger:
if _LOGGER_PROVIDER:
return _LOGGER_PROVIDER.get_logger(
@@ -384,7 +383,7 @@ class ProxyLoggerProvider(LoggerProvider):
_LOGGER_PROVIDER_SET_ONCE = Once()
-_LOGGER_PROVIDER: Optional[LoggerProvider] = None
+_LOGGER_PROVIDER: LoggerProvider | None = None
_PROXY_LOGGER_PROVIDER = ProxyLoggerProvider()
@@ -427,9 +426,9 @@ def set_logger_provider(logger_provider: LoggerProvider) -> None:
def get_logger(
instrumenting_module_name: str,
instrumenting_library_version: str = "",
- logger_provider: Optional[LoggerProvider] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ logger_provider: LoggerProvider | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
) -> Logger:
"""Returns a `Logger` for use within a python process.
diff --git a/opentelemetry-api/src/opentelemetry/_logs/severity/__init__.py b/opentelemetry-api/src/opentelemetry/_logs/severity/__init__.py
index 8763d1c..96ffcc6 100644
--- a/opentelemetry-api/src/opentelemetry/_logs/severity/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/_logs/severity/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import enum
diff --git a/opentelemetry-api/src/opentelemetry/attributes/__init__.py b/opentelemetry-api/src/opentelemetry/attributes/__init__.py
index e7799a6..09d01d3 100644
--- a/opentelemetry-api/src/opentelemetry/attributes/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/attributes/__init__.py
@@ -1,23 +1,11 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import copy
import logging
import threading
from collections import OrderedDict
-from collections.abc import MutableMapping
-from typing import Mapping, Optional, Sequence, Tuple, Union
+from collections.abc import Mapping, MutableMapping, Sequence
from opentelemetry.util import types
@@ -48,8 +36,8 @@ _logger = logging.getLogger(__name__)
def _clean_attribute(
- key: str, value: types.AttributeValue, max_len: Optional[int]
-) -> Optional[Union[types.AttributeValue, Tuple[Union[str, int, float], ...]]]:
+ key: str, value: types.AttributeValue, max_len: int | None
+) -> types.AttributeValue | tuple[str | int | float, ...] | None:
"""Checks if attribute value is valid and cleans it if required.
The function returns the cleaned value or None if the value is not valid.
@@ -127,7 +115,7 @@ def _clean_attribute(
def _clean_extended_attribute_value( # pylint: disable=too-many-branches
- value: types.AnyValue, max_len: Optional[int]
+ value: types.AnyValue, max_len: int | None
) -> types.AnyValue:
# for primitive types just return the value and eventually shorten the string length
if value is None or isinstance(value, _VALID_ATTR_VALUE_TYPES):
@@ -204,7 +192,7 @@ def _clean_extended_attribute_value( # pylint: disable=too-many-branches
def _clean_extended_attribute(
- key: str, value: types.AnyValue, max_len: Optional[int]
+ key: str, value: types.AnyValue, max_len: int | None
) -> types.AnyValue:
"""Checks if attribute value is valid and cleans it if required.
@@ -227,8 +215,8 @@ def _clean_extended_attribute(
def _clean_attribute_value(
- value: types.AttributeValue, limit: Optional[int]
-) -> Optional[types.AttributeValue]:
+ value: types.AttributeValue, limit: int | None
+) -> types.AttributeValue | None:
if value is None:
return None
@@ -253,10 +241,10 @@ class BoundedAttributes(MutableMapping): # type: ignore
def __init__(
self,
- maxlen: Optional[int] = None,
- attributes: Optional[types._ExtendedAttributes] = None,
+ maxlen: int | None = None,
+ attributes: types._ExtendedAttributes | None = None,
immutable: bool = True,
- max_value_len: Optional[int] = None,
+ max_value_len: int | None = None,
extended_attributes: bool = False,
):
if maxlen is not None:
@@ -270,10 +258,10 @@ class BoundedAttributes(MutableMapping): # type: ignore
self._extended_attributes = extended_attributes
# OrderedDict is not used until the maxlen is reached for efficiency.
- self._dict: Union[
- MutableMapping[str, types.AnyValue],
- OrderedDict[str, types.AnyValue],
- ] = {}
+ self._dict: (
+ MutableMapping[str, types.AnyValue]
+ | OrderedDict[str, types.AnyValue]
+ ) = {}
self._lock = threading.RLock()
if attributes:
for key, value in attributes.items():
diff --git a/opentelemetry-api/src/opentelemetry/baggage/__init__.py b/opentelemetry-api/src/opentelemetry/baggage/__init__.py
index c8e34c1..b1b36a9 100644
--- a/opentelemetry-api/src/opentelemetry/baggage/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/baggage/__init__.py
@@ -1,21 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
+from collections.abc import Mapping
from logging import getLogger
from re import compile
from types import MappingProxyType
-from typing import Dict, Mapping, Optional
from opentelemetry.context import create_key, get_value, set_value
from opentelemetry.context.context import Context
@@ -34,7 +23,7 @@ _PROPERT_PATTERN = compile(_BAGGAGE_PROPERTY_FORMAT)
def get_all(
- context: Optional[Context] = None,
+ context: Context | None = None,
) -> Mapping[str, object]:
"""Returns the name/value pairs in the Baggage
@@ -47,9 +36,7 @@ def get_all(
return MappingProxyType(_get_baggage_value(context=context))
-def get_baggage(
- name: str, context: Optional[Context] = None
-) -> Optional[object]:
+def get_baggage(name: str, context: Context | None = None) -> object | None:
"""Provides access to the value for a name/value pair in the
Baggage
@@ -65,7 +52,7 @@ def get_baggage(
def set_baggage(
- name: str, value: object, context: Optional[Context] = None
+ name: str, value: object, context: Context | None = None
) -> Context:
"""Sets a value in the Baggage
@@ -82,7 +69,7 @@ def set_baggage(
return set_value(_BAGGAGE_KEY, baggage, context=context)
-def remove_baggage(name: str, context: Optional[Context] = None) -> Context:
+def remove_baggage(name: str, context: Context | None = None) -> Context:
"""Removes a value from the Baggage
Args:
@@ -98,7 +85,7 @@ def remove_baggage(name: str, context: Optional[Context] = None) -> Context:
return set_value(_BAGGAGE_KEY, baggage, context=context)
-def clear(context: Optional[Context] = None) -> Context:
+def clear(context: Context | None = None) -> Context:
"""Removes all values from the Baggage
Args:
@@ -110,7 +97,7 @@ def clear(context: Optional[Context] = None) -> Context:
return set_value(_BAGGAGE_KEY, {}, context=context)
-def _get_baggage_value(context: Optional[Context] = None) -> Dict[str, object]:
+def _get_baggage_value(context: Context | None = None) -> dict[str, object]:
baggage = get_value(_BAGGAGE_KEY, context=context)
if isinstance(baggage, dict):
return baggage
diff --git a/opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py b/opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py
index 49fb378..1d873c5 100644
--- a/opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
#
-# 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 collections.abc import Iterable, Iterator, Mapping
from logging import getLogger
from re import split
-from typing import Iterable, List, Mapping, Optional, Set
from urllib.parse import quote_plus, unquote_plus
from opentelemetry.baggage import _is_valid_pair, get_all, set_baggage
@@ -26,6 +15,59 @@ from opentelemetry.util.re import _DELIMITER_PATTERN
_logger = getLogger(__name__)
+def _filter_valid_entries(
+ entries: Iterable[str],
+ max_pair_length: int,
+) -> Iterator[str]:
+ for entry in entries:
+ if not entry:
+ continue
+ if not entry.isascii():
+ _logger.warning(
+ "Baggage entry with key `%s` contains non-ASCII characters",
+ entry.split("=", 1)[0],
+ )
+ continue
+ if len(entry) > max_pair_length:
+ _logger.warning(
+ "Baggage entry with key `%s` exceeded the maximum number of bytes per list-member with length %d",
+ entry.split("=", 1)[0],
+ len(entry),
+ )
+ continue
+ yield entry
+
+
+def _apply_baggage_limits(
+ entries: Iterable[str],
+ max_pairs: int,
+ max_pair_length: int,
+ max_header_length: int,
+) -> Iterator[str]:
+ """Apply W3C Baggage size limits to a sequence of baggage entries.
+
+ Yields entries that fit within the W3C specification limits.
+ Logs warnings when entries are dropped.
+ """
+ length = 0
+ for index, entry in enumerate(
+ _filter_valid_entries(entries, max_pair_length)
+ ):
+ if index >= max_pairs:
+ _logger.warning(
+ "Baggage exceeded the maximum number of list-members"
+ )
+ return
+
+ length += (1 if index > 0 else 0) + len(entry)
+ if length > max_header_length:
+ _logger.warning(
+ "Baggage exceeded the maximum number of bytes per baggage-string"
+ )
+ return
+ yield entry
+
+
class W3CBaggagePropagator(textmap.TextMapPropagator):
"""Extracts and injects Baggage which is used to annotate telemetry."""
@@ -37,7 +79,7 @@ class W3CBaggagePropagator(textmap.TextMapPropagator):
def extract(
self,
carrier: textmap.CarrierT,
- context: Optional[Context] = None,
+ context: Context | None = None,
getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter,
) -> Context:
"""Extract Baggage from the carrier.
@@ -56,31 +98,21 @@ class W3CBaggagePropagator(textmap.TextMapPropagator):
if not header:
return context
- if len(header) > self._MAX_HEADER_LENGTH:
+ if len(header.encode()) > self._MAX_HEADER_LENGTH:
_logger.warning(
"Baggage header `%s` exceeded the maximum number of bytes per baggage-string",
header,
)
return context
- baggage_entries: List[str] = split(_DELIMITER_PATTERN, header)
- total_baggage_entries = self._MAX_PAIRS
+ baggage_entries = split(_DELIMITER_PATTERN, header)
- if len(baggage_entries) > self._MAX_PAIRS:
- _logger.warning(
- "Baggage header `%s` exceeded the maximum number of list-members",
- header,
- )
-
- for entry in baggage_entries:
- if len(entry) > self._MAX_PAIR_LENGTH:
- _logger.warning(
- "Baggage entry `%s` exceeded the maximum number of bytes per list-member",
- entry,
- )
- continue
- if not entry: # empty string
- continue
+ for entry in _apply_baggage_limits(
+ baggage_entries,
+ max_pairs=self._MAX_PAIRS,
+ max_pair_length=self._MAX_PAIR_LENGTH,
+ max_header_length=self._MAX_HEADER_LENGTH,
+ ):
try:
name, value = entry.split("=", 1)
except Exception: # pylint: disable=broad-exception-caught
@@ -101,16 +133,13 @@ class W3CBaggagePropagator(textmap.TextMapPropagator):
value,
context=context,
)
- total_baggage_entries -= 1
- if total_baggage_entries == 0:
- break
return context
def inject(
self,
carrier: textmap.CarrierT,
- context: Optional[Context] = None,
+ context: Context | None = None,
setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter,
) -> None:
"""Injects Baggage into the carrier.
@@ -122,25 +151,35 @@ class W3CBaggagePropagator(textmap.TextMapPropagator):
if not baggage_entries:
return
- baggage_string = _format_baggage(baggage_entries)
- setter.set(carrier, self._BAGGAGE_HEADER_NAME, baggage_string)
+ baggage_string = ",".join(
+ _apply_baggage_limits(
+ _encode_baggage_pairs(baggage_entries),
+ max_pairs=self._MAX_PAIRS,
+ max_pair_length=self._MAX_PAIR_LENGTH,
+ max_header_length=self._MAX_HEADER_LENGTH,
+ )
+ )
+
+ if baggage_string:
+ setter.set(carrier, self._BAGGAGE_HEADER_NAME, baggage_string)
@property
- def fields(self) -> Set[str]:
+ def fields(self) -> set[str]:
"""Returns a set with the fields set in `inject`."""
return {self._BAGGAGE_HEADER_NAME}
-def _format_baggage(baggage_entries: Mapping[str, object]) -> str:
- return ",".join(
- quote_plus(str(key)) + "=" + quote_plus(str(value))
- for key, value in baggage_entries.items()
- )
+def _encode_baggage_pairs(
+ baggage_entries: Mapping[str, object],
+) -> Iterator[str]:
+ """Yield URL-encoded 'key=value' pairs from baggage entries."""
+ for key, value in baggage_entries.items():
+ yield quote_plus(str(key)) + "=" + quote_plus(str(value))
def _extract_first_element(
- items: Optional[Iterable[textmap.CarrierT]],
-) -> Optional[textmap.CarrierT]:
+ items: Iterable[textmap.CarrierT] | None,
+) -> textmap.CarrierT | None:
if items is None:
return None
return next(iter(items), None)
diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py
index 9287d5b..6518f03 100644
--- a/opentelemetry-api/src/opentelemetry/context/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/context/__init__.py
@@ -1,21 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
import logging
-import typing
from contextvars import Token
from os import environ
from uuid import uuid4
@@ -82,7 +70,7 @@ def create_key(keyname: str) -> str:
return keyname + "-" + str(uuid4())
-def get_value(key: str, context: typing.Optional[Context] = None) -> object:
+def get_value(key: str, context: Context | None = None) -> object:
"""To access the local state of a concern, the RuntimeContext API
provides a function which takes a context and a key as input,
and returns a value.
@@ -98,7 +86,7 @@ def get_value(key: str, context: typing.Optional[Context] = None) -> object:
def set_value(
- key: str, value: object, context: typing.Optional[Context] = None
+ key: str, value: object, context: Context | None = None
) -> Context:
"""To record the local state of a cross-cutting concern, the
RuntimeContext API provides a function which takes a context, a
diff --git a/opentelemetry-api/src/opentelemetry/context/context.py b/opentelemetry-api/src/opentelemetry/context/context.py
index c1ef9cf..c8d87a4 100644
--- a/opentelemetry-api/src/opentelemetry/context/context.py
+++ b/opentelemetry-api/src/opentelemetry/context/context.py
@@ -1,25 +1,13 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
-import typing
from abc import ABC, abstractmethod
from contextvars import Token
-class Context(typing.Dict[str, object]):
+class Context(dict[str, object]):
def __setitem__(self, key: str, value: object) -> None:
raise ValueError
diff --git a/opentelemetry-api/src/opentelemetry/context/contextvars_context.py b/opentelemetry-api/src/opentelemetry/context/contextvars_context.py
index dceee26..388329c 100644
--- a/opentelemetry-api/src/opentelemetry/context/contextvars_context.py
+++ b/opentelemetry-api/src/opentelemetry/context/contextvars_context.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
diff --git a/opentelemetry-api/src/opentelemetry/environment_variables/__init__.py b/opentelemetry-api/src/opentelemetry/environment_variables/__init__.py
index bd8ed1c..00fe293 100644
--- a/opentelemetry-api/src/opentelemetry/environment_variables/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/environment_variables/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
OTEL_LOGS_EXPORTER = "OTEL_LOGS_EXPORTER"
"""
diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py
index 74284ad..b39a3de 100644
--- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
The OpenTelemetry metrics API describes the classes used to generate
diff --git a/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py
index 93658a7..9df5529 100644
--- a/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-ancestors
@@ -42,11 +31,12 @@ The following code shows how to obtain a meter using the global :class:`.MeterPr
import warnings
from abc import ABC, abstractmethod
+from collections.abc import Sequence
from dataclasses import dataclass
from logging import getLogger
from os import environ
from threading import Lock
-from typing import Dict, List, Optional, Sequence, Union, cast
+from typing import cast
from opentelemetry.environment_variables import OTEL_PYTHON_METER_PROVIDER
from opentelemetry.metrics._internal.instrument import (
@@ -84,15 +74,15 @@ _logger = getLogger(__name__)
# pylint: disable=invalid-name
-_ProxyInstrumentT = Union[
- _ProxyCounter,
- _ProxyHistogram,
- _ProxyGauge,
- _ProxyObservableCounter,
- _ProxyObservableGauge,
- _ProxyObservableUpDownCounter,
- _ProxyUpDownCounter,
-]
+_ProxyInstrumentT = (
+ _ProxyCounter
+ | _ProxyHistogram
+ | _ProxyGauge
+ | _ProxyObservableCounter
+ | _ProxyObservableGauge
+ | _ProxyObservableUpDownCounter
+ | _ProxyUpDownCounter
+)
class MeterProvider(ABC):
@@ -104,9 +94,9 @@ class MeterProvider(ABC):
def get_meter(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[Attributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: Attributes | None = None,
) -> "Meter":
"""Returns a `Meter` for use by the given instrumentation library.
@@ -143,9 +133,9 @@ class NoOpMeterProvider(MeterProvider):
def get_meter(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[Attributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: Attributes | None = None,
) -> "Meter":
"""Returns a NoOpMeter."""
return NoOpMeter(name, version=version, schema_url=schema_url)
@@ -154,15 +144,15 @@ class NoOpMeterProvider(MeterProvider):
class _ProxyMeterProvider(MeterProvider):
def __init__(self) -> None:
self._lock = Lock()
- self._meters: List[_ProxyMeter] = []
- self._real_meter_provider: Optional[MeterProvider] = None
+ self._meters: list[_ProxyMeter] = []
+ self._real_meter_provider: MeterProvider | None = None
def get_meter(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[Attributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: Attributes | None = None,
) -> "Meter":
with self._lock:
if self._real_meter_provider is not None:
@@ -186,7 +176,7 @@ class _InstrumentRegistrationStatus:
instrument_id: str
already_registered: bool
conflict: bool
- current_advisory: Optional[_MetricsHistogramAdvisory]
+ current_advisory: _MetricsHistogramAdvisory | None
class Meter(ABC):
@@ -199,16 +189,14 @@ class Meter(ABC):
def __init__(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
) -> None:
super().__init__()
self._name = name
self._version = version
self._schema_url = schema_url
- self._instrument_ids: Dict[
- str, Optional[_MetricsHistogramAdvisory]
- ] = {}
+ self._instrument_ids: dict[str, _MetricsHistogramAdvisory | None] = {}
self._instrument_ids_lock = Lock()
@property
@@ -219,14 +207,14 @@ class Meter(ABC):
return self._name
@property
- def version(self) -> Optional[str]:
+ def version(self) -> str | None:
"""
The version string of the instrumenting library.
"""
return self._version
@property
- def schema_url(self) -> Optional[str]:
+ def schema_url(self) -> str | None:
"""
Specifies the Schema URL of the emitted telemetry
"""
@@ -238,7 +226,7 @@ class Meter(ABC):
type_: type,
unit: str,
description: str,
- advisory: Optional[_MetricsHistogramAdvisory] = None,
+ advisory: _MetricsHistogramAdvisory | None = None,
) -> _InstrumentRegistrationStatus:
"""
Register an instrument with the name, type, unit and description as
@@ -331,7 +319,7 @@ class Meter(ABC):
def create_observable_counter(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> ObservableCounter:
@@ -431,7 +419,7 @@ class Meter(ABC):
unit: str = "",
description: str = "",
*,
- explicit_bucket_boundaries_advisory: Optional[Sequence[float]] = None,
+ explicit_bucket_boundaries_advisory: Sequence[float] | None = None,
) -> Histogram:
"""Creates a :class:`~opentelemetry.metrics.Histogram` instrument
@@ -462,7 +450,7 @@ class Meter(ABC):
def create_observable_gauge(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> ObservableGauge:
@@ -482,7 +470,7 @@ class Meter(ABC):
def create_observable_up_down_counter(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> ObservableUpDownCounter:
@@ -503,13 +491,13 @@ class _ProxyMeter(Meter):
def __init__(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
) -> None:
super().__init__(name, version=version, schema_url=schema_url)
self._lock = Lock()
- self._instruments: List[_ProxyInstrumentT] = []
- self._real_meter: Optional[Meter] = None
+ self._instruments: list[_ProxyInstrumentT] = []
+ self._real_meter: Meter | None = None
def on_set_meter_provider(self, meter_provider: MeterProvider) -> None:
"""Called when a real meter provider is set on the creating _ProxyMeterProvider
@@ -559,7 +547,7 @@ class _ProxyMeter(Meter):
def create_observable_counter(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> ObservableCounter:
@@ -580,7 +568,7 @@ class _ProxyMeter(Meter):
unit: str = "",
description: str = "",
*,
- explicit_bucket_boundaries_advisory: Optional[Sequence[float]] = None,
+ explicit_bucket_boundaries_advisory: Sequence[float] | None = None,
) -> Histogram:
with self._lock:
if self._real_meter:
@@ -612,7 +600,7 @@ class _ProxyMeter(Meter):
def create_observable_gauge(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> ObservableGauge:
@@ -630,7 +618,7 @@ class _ProxyMeter(Meter):
def create_observable_up_down_counter(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> ObservableUpDownCounter:
@@ -717,7 +705,7 @@ class NoOpMeter(Meter):
def create_observable_counter(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> ObservableCounter:
@@ -746,7 +734,7 @@ class NoOpMeter(Meter):
unit: str = "",
description: str = "",
*,
- explicit_bucket_boundaries_advisory: Optional[Sequence[float]] = None,
+ explicit_bucket_boundaries_advisory: Sequence[float] | None = None,
) -> Histogram:
"""Returns a no-op Histogram."""
status = self._register_instrument(
@@ -776,7 +764,7 @@ class NoOpMeter(Meter):
def create_observable_gauge(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> ObservableGauge:
@@ -802,7 +790,7 @@ class NoOpMeter(Meter):
def create_observable_up_down_counter(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> ObservableUpDownCounter:
@@ -827,16 +815,16 @@ class NoOpMeter(Meter):
_METER_PROVIDER_SET_ONCE = Once()
-_METER_PROVIDER: Optional[MeterProvider] = None
+_METER_PROVIDER: MeterProvider | None = None
_PROXY_METER_PROVIDER = _ProxyMeterProvider()
def get_meter(
name: str,
version: str = "",
- meter_provider: Optional[MeterProvider] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[Attributes] = None,
+ meter_provider: MeterProvider | None = None,
+ schema_url: str | None = None,
+ attributes: Attributes | None = None,
) -> "Meter":
"""Returns a `Meter` for use by the given instrumentation library.
diff --git a/opentelemetry-api/src/opentelemetry/metrics/_internal/instrument.py b/opentelemetry-api/src/opentelemetry/metrics/_internal/instrument.py
index cfd7a15..79bdfa4 100644
--- a/opentelemetry-api/src/opentelemetry/metrics/_internal/instrument.py
+++ b/opentelemetry-api/src/opentelemetry/metrics/_internal/instrument.py
@@ -1,34 +1,17 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-ancestors
from abc import ABC, abstractmethod
+from collections.abc import Callable, Generator, Iterable, Sequence
from dataclasses import dataclass
from logging import getLogger
from re import compile as re_compile
from typing import (
- Callable,
- Dict,
- Generator,
Generic,
- Iterable,
- Optional,
- Sequence,
TypeVar,
- Union,
)
# pylint: disable=unused-import; needed for typing and sphinx
@@ -47,7 +30,7 @@ _unit_regex = re_compile(r"[\x00-\x7F]{0,63}")
@dataclass(frozen=True)
class _MetricsHistogramAdvisory:
- explicit_bucket_boundaries: Optional[Sequence[float]] = None
+ explicit_bucket_boundaries: Sequence[float] | None = None
@dataclass(frozen=True)
@@ -64,10 +47,10 @@ class CallbackOptions:
InstrumentT = TypeVar("InstrumentT", bound="Instrument")
# pylint: disable=invalid-name
-CallbackT = Union[
- Callable[[CallbackOptions], Iterable[Observation]],
- Generator[Iterable[Observation], CallbackOptions, None],
-]
+CallbackT = (
+ Callable[[CallbackOptions], Iterable[Observation]]
+ | Generator[Iterable[Observation], CallbackOptions, None]
+)
class Instrument(ABC):
@@ -85,7 +68,7 @@ class Instrument(ABC):
@staticmethod
def _check_name_unit_description(
name: str, unit: str, description: str
- ) -> Dict[str, Optional[str]]:
+ ) -> dict[str, str | None]:
"""
Checks the following instrument name, unit and description for
compliance with the spec.
@@ -96,7 +79,7 @@ class Instrument(ABC):
original values.
"""
- result: Dict[str, Optional[str]] = {}
+ result: dict[str, str | None] = {}
if _name_regex.fullmatch(name) is not None:
result["name"] = name
@@ -128,7 +111,7 @@ class _ProxyInstrument(ABC, Generic[InstrumentT]):
self._name = name
self._unit = unit
self._description = description
- self._real_instrument: Optional[InstrumentT] = None
+ self._real_instrument: InstrumentT | None = None
def on_meter_set(self, meter: "metrics.Meter") -> None:
"""Called when a real meter is set on the creating _ProxyMeter"""
@@ -147,7 +130,7 @@ class _ProxyAsynchronousInstrument(_ProxyInstrument[InstrumentT]):
def __init__(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> None:
@@ -166,7 +149,7 @@ class Asynchronous(Instrument):
def __init__(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> None:
@@ -179,9 +162,9 @@ class Counter(Synchronous):
@abstractmethod
def add(
self,
- amount: Union[int, float],
- attributes: Optional[Attributes] = None,
- context: Optional[Context] = None,
+ amount: int | float,
+ attributes: Attributes | None = None,
+ context: Context | None = None,
) -> None:
"""Records an increment to the counter.
@@ -206,9 +189,9 @@ class NoOpCounter(Counter):
def add(
self,
- amount: Union[int, float],
- attributes: Optional[Attributes] = None,
- context: Optional[Context] = None,
+ amount: int | float,
+ attributes: Attributes | None = None,
+ context: Context | None = None,
) -> None:
return super().add(amount, attributes=attributes, context=context)
@@ -216,9 +199,9 @@ class NoOpCounter(Counter):
class _ProxyCounter(_ProxyInstrument[Counter], Counter):
def add(
self,
- amount: Union[int, float],
- attributes: Optional[Attributes] = None,
- context: Optional[Context] = None,
+ amount: int | float,
+ attributes: Attributes | None = None,
+ context: Context | None = None,
) -> None:
if self._real_instrument:
self._real_instrument.add(amount, attributes, context)
@@ -237,9 +220,9 @@ class UpDownCounter(Synchronous):
@abstractmethod
def add(
self,
- amount: Union[int, float],
- attributes: Optional[Attributes] = None,
- context: Optional[Context] = None,
+ amount: int | float,
+ attributes: Attributes | None = None,
+ context: Context | None = None,
) -> None:
"""Records an increment or decrement to the counter.
@@ -268,9 +251,9 @@ class NoOpUpDownCounter(UpDownCounter):
def add(
self,
- amount: Union[int, float],
- attributes: Optional[Attributes] = None,
- context: Optional[Context] = None,
+ amount: int | float,
+ attributes: Attributes | None = None,
+ context: Context | None = None,
) -> None:
return super().add(amount, attributes=attributes, context=context)
@@ -278,9 +261,9 @@ class NoOpUpDownCounter(UpDownCounter):
class _ProxyUpDownCounter(_ProxyInstrument[UpDownCounter], UpDownCounter):
def add(
self,
- amount: Union[int, float],
- attributes: Optional[Attributes] = None,
- context: Optional[Context] = None,
+ amount: int | float,
+ attributes: Attributes | None = None,
+ context: Context | None = None,
) -> None:
if self._real_instrument:
self._real_instrument.add(amount, attributes, context)
@@ -305,7 +288,7 @@ class NoOpObservableCounter(ObservableCounter):
def __init__(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> None:
@@ -344,7 +327,7 @@ class NoOpObservableUpDownCounter(ObservableUpDownCounter):
def __init__(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> None:
@@ -383,16 +366,16 @@ class Histogram(Synchronous):
name: str,
unit: str = "",
description: str = "",
- explicit_bucket_boundaries_advisory: Optional[Sequence[float]] = None,
+ explicit_bucket_boundaries_advisory: Sequence[float] | None = None,
) -> None:
pass
@abstractmethod
def record(
self,
- amount: Union[int, float],
- attributes: Optional[Attributes] = None,
- context: Optional[Context] = None,
+ amount: int | float,
+ attributes: Attributes | None = None,
+ context: Context | None = None,
) -> None:
"""Records a measurement.
@@ -418,7 +401,7 @@ class NoOpHistogram(Histogram):
name: str,
unit: str = "",
description: str = "",
- explicit_bucket_boundaries_advisory: Optional[Sequence[float]] = None,
+ explicit_bucket_boundaries_advisory: Sequence[float] | None = None,
) -> None:
super().__init__(
name,
@@ -429,9 +412,9 @@ class NoOpHistogram(Histogram):
def record(
self,
- amount: Union[int, float],
- attributes: Optional[Attributes] = None,
- context: Optional[Context] = None,
+ amount: int | float,
+ attributes: Attributes | None = None,
+ context: Context | None = None,
) -> None:
return super().record(amount, attributes=attributes, context=context)
@@ -442,7 +425,7 @@ class _ProxyHistogram(_ProxyInstrument[Histogram], Histogram):
name: str,
unit: str = "",
description: str = "",
- explicit_bucket_boundaries_advisory: Optional[Sequence[float]] = None,
+ explicit_bucket_boundaries_advisory: Sequence[float] | None = None,
) -> None:
super().__init__(name, unit=unit, description=description)
self._explicit_bucket_boundaries_advisory = (
@@ -451,9 +434,9 @@ class _ProxyHistogram(_ProxyInstrument[Histogram], Histogram):
def record(
self,
- amount: Union[int, float],
- attributes: Optional[Attributes] = None,
- context: Optional[Context] = None,
+ amount: int | float,
+ attributes: Attributes | None = None,
+ context: Context | None = None,
) -> None:
if self._real_instrument:
self._real_instrument.record(amount, attributes, context)
@@ -480,7 +463,7 @@ class NoOpObservableGauge(ObservableGauge):
def __init__(
self,
name: str,
- callbacks: Optional[Sequence[CallbackT]] = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
) -> None:
@@ -513,9 +496,9 @@ class Gauge(Synchronous):
@abstractmethod
def set(
self,
- amount: Union[int, float],
- attributes: Optional[Attributes] = None,
- context: Optional[Context] = None,
+ amount: int | float,
+ attributes: Attributes | None = None,
+ context: Context | None = None,
) -> None:
"""Records the current value of the gauge.
@@ -544,9 +527,9 @@ class NoOpGauge(Gauge):
def set(
self,
- amount: Union[int, float],
- attributes: Optional[Attributes] = None,
- context: Optional[Context] = None,
+ amount: int | float,
+ attributes: Attributes | None = None,
+ context: Context | None = None,
) -> None:
return super().set(amount, attributes=attributes, context=context)
@@ -557,9 +540,9 @@ class _ProxyGauge(
):
def set(
self,
- amount: Union[int, float],
- attributes: Optional[Attributes] = None,
- context: Optional[Context] = None,
+ amount: int | float,
+ attributes: Attributes | None = None,
+ context: Context | None = None,
) -> None:
if self._real_instrument:
self._real_instrument.set(amount, attributes, context)
diff --git a/opentelemetry-api/src/opentelemetry/metrics/_internal/observation.py b/opentelemetry-api/src/opentelemetry/metrics/_internal/observation.py
index ffc254b..26f2e07 100644
--- a/opentelemetry-api/src/opentelemetry/metrics/_internal/observation.py
+++ b/opentelemetry-api/src/opentelemetry/metrics/_internal/observation.py
@@ -1,18 +1,6 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 Optional, Union
+# SPDX-License-Identifier: Apache-2.0
+
from opentelemetry.context import Context
from opentelemetry.util.types import Attributes
@@ -31,16 +19,16 @@ class Observation:
def __init__(
self,
- value: Union[int, float],
+ value: int | float,
attributes: Attributes = None,
- context: Optional[Context] = None,
+ context: Context | None = None,
) -> None:
self._value = value
self._attributes = attributes
self._context = context
@property
- def value(self) -> Union[float, int]:
+ def value(self) -> float | int:
return self._value
@property
@@ -48,7 +36,7 @@ class Observation:
return self._attributes
@property
- def context(self) -> Optional[Context]:
+ def context(self) -> Context | None:
return self._context
def __eq__(self, other: object) -> bool:
diff --git a/opentelemetry-api/src/opentelemetry/propagate/__init__.py b/opentelemetry-api/src/opentelemetry/propagate/__init__.py
index 0238114..207f639 100644
--- a/opentelemetry-api/src/opentelemetry/propagate/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/propagate/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
API for propagation of context.
@@ -70,7 +59,6 @@ Example::
from logging import getLogger
from os import environ
-from typing import List, Optional
from opentelemetry.context.context import Context
from opentelemetry.environment_variables import OTEL_PROPAGATORS
@@ -82,7 +70,7 @@ logger = getLogger(__name__)
def extract(
carrier: textmap.CarrierT,
- context: Optional[Context] = None,
+ context: Context | None = None,
getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter,
) -> Context:
"""Uses the configured propagator to extract a Context from the carrier.
@@ -103,7 +91,7 @@ def extract(
def inject(
carrier: textmap.CarrierT,
- context: Optional[Context] = None,
+ context: Context | None = None,
setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter,
) -> None:
"""Uses the configured propagator to inject a Context into the carrier.
@@ -121,7 +109,7 @@ def inject(
get_global_textmap().inject(carrier, context=context, setter=setter)
-propagators: List[textmap.TextMapPropagator] = []
+propagators: list[textmap.TextMapPropagator] = []
# Single use variable here to hack black and make lint pass
environ_propagators = environ.get(
diff --git a/opentelemetry-api/src/opentelemetry/propagators/_envcarrier.py b/opentelemetry-api/src/opentelemetry/propagators/_envcarrier.py
index 0ea5f48..6b65148 100644
--- a/opentelemetry-api/src/opentelemetry/propagators/_envcarrier.py
+++ b/opentelemetry-api/src/opentelemetry/propagators/_envcarrier.py
@@ -1,28 +1,25 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import os
+import re
import typing
from collections.abc import MutableMapping
from opentelemetry.propagators.textmap import Getter, Setter
+def _normalize_key(key: str) -> str:
+ result = re.sub(r"[^A-Za-z0-9_]", "_", key).upper()
+ if result and result[0].isdigit():
+ result = "_" + result
+ return result
+
+
class EnvironmentGetter(Getter[typing.Mapping[str, str]]):
"""Getter implementation for extracting context and baggage from environment variables.
- EnvironmentGetter creates a case-insensitive lookup from the current environment
+ EnvironmentGetter creates a normalized lookup from the current environment
variables at initialization time and provides simple data access without validation.
Per the OpenTelemetry specification, environment variables are treated as immutable
@@ -36,15 +33,15 @@ class EnvironmentGetter(Getter[typing.Mapping[str, str]]):
"""
def __init__(self):
- # Create case-insensitive lookup from current environment
+ # Create a normalized lookup from current environment
# Per spec: "creates an in-memory copy of the current environment variables"
- self.carrier: typing.Dict[str, str] = {
- k.lower(): v for k, v in os.environ.items()
+ self.carrier: dict[str, str] = {
+ _normalize_key(k): v for k, v in os.environ.items()
}
def get(
self, carrier: typing.Mapping[str, str], key: str
- ) -> typing.Optional[typing.List[str]]:
+ ) -> list[str] | None:
"""Get a value from the environment carrier for the given key.
Args:
@@ -54,21 +51,19 @@ class EnvironmentGetter(Getter[typing.Mapping[str, str]]):
Returns:
A list with a single string value if the key exists, None otherwise.
"""
- val = self.carrier.get(key.lower())
+ val = self.carrier.get(_normalize_key(key))
if val is None:
return None
- if isinstance(val, typing.Iterable) and not isinstance(val, str):
- return list(val)
return [val]
- def keys(self, carrier: typing.Mapping[str, str]) -> typing.List[str]:
+ def keys(self, carrier: typing.Mapping[str, str]) -> list[str]:
"""Get all keys from the environment carrier.
Args:
carrier: Not used; maintained for interface compatibility with Getter[CarrierT]
Returns:
- List of all environment variable keys (lowercase).
+ List of all environment variable keys (normalized).
"""
return list(self.carrier.keys())
@@ -96,4 +91,4 @@ class EnvironmentSetter(Setter[MutableMapping[str, str]]):
key: The key to set (will be converted to uppercase)
value: The value to set
"""
- carrier[key.upper()] = value
+ carrier[_normalize_key(key)] = value
diff --git a/opentelemetry-api/src/opentelemetry/propagators/composite.py b/opentelemetry-api/src/opentelemetry/propagators/composite.py
index 08dddb0..f6d864d 100644
--- a/opentelemetry-api/src/opentelemetry/propagators/composite.py
+++ b/opentelemetry-api/src/opentelemetry/propagators/composite.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import logging
import typing
@@ -38,7 +27,7 @@ class CompositePropagator(textmap.TextMapPropagator):
def extract(
self,
carrier: textmap.CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter,
) -> Context:
"""Run each of the configured propagators with the given context and carrier.
@@ -55,7 +44,7 @@ class CompositePropagator(textmap.TextMapPropagator):
def inject(
self,
carrier: textmap.CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter,
) -> None:
"""Run each of the configured propagators with the given context and carrier.
@@ -69,7 +58,7 @@ class CompositePropagator(textmap.TextMapPropagator):
propagator.inject(carrier, context, setter=setter)
@property
- def fields(self) -> typing.Set[str]:
+ def fields(self) -> set[str]:
"""Returns a set with the fields set in `inject`.
See
diff --git a/opentelemetry-api/src/opentelemetry/propagators/textmap.py b/opentelemetry-api/src/opentelemetry/propagators/textmap.py
index 42f1124..de37e65 100644
--- a/opentelemetry-api/src/opentelemetry/propagators/textmap.py
+++ b/opentelemetry-api/src/opentelemetry/propagators/textmap.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import abc
import typing
@@ -19,7 +8,7 @@ from opentelemetry.context.context import Context
CarrierT = typing.TypeVar("CarrierT")
# pylint: disable=invalid-name
-CarrierValT = typing.Union[typing.List[str], str]
+CarrierValT = list[str] | str
class Getter(abc.ABC, typing.Generic[CarrierT]):
@@ -28,9 +17,7 @@ class Getter(abc.ABC, typing.Generic[CarrierT]):
"""
@abc.abstractmethod
- def get(
- self, carrier: CarrierT, key: str
- ) -> typing.Optional[typing.List[str]]:
+ def get(self, carrier: CarrierT, key: str) -> list[str] | None:
"""Function that can retrieve zero
or more values from the carrier. In the case that
the value does not exist, returns None.
@@ -44,7 +31,7 @@ class Getter(abc.ABC, typing.Generic[CarrierT]):
"""
@abc.abstractmethod
- def keys(self, carrier: CarrierT) -> typing.List[str]:
+ def keys(self, carrier: CarrierT) -> list[str]:
"""Function that can retrieve all the keys in a carrier object.
Args:
@@ -75,7 +62,7 @@ class Setter(abc.ABC, typing.Generic[CarrierT]):
class DefaultGetter(Getter[typing.Mapping[str, CarrierValT]]):
def get(
self, carrier: typing.Mapping[str, CarrierValT], key: str
- ) -> typing.Optional[typing.List[str]]:
+ ) -> list[str] | None:
"""Getter implementation to retrieve a value from a dictionary.
Args:
@@ -91,9 +78,7 @@ class DefaultGetter(Getter[typing.Mapping[str, CarrierValT]]):
return list(val)
return [val]
- def keys(
- self, carrier: typing.Mapping[str, CarrierValT]
- ) -> typing.List[str]:
+ def keys(self, carrier: typing.Mapping[str, CarrierValT]) -> list[str]:
"""Keys implementation that returns all keys from a dictionary."""
return list(carrier.keys())
@@ -134,7 +119,7 @@ class TextMapPropagator(abc.ABC):
def extract(
self,
carrier: CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
getter: Getter[CarrierT] = default_getter,
) -> Context:
"""Create a Context from values in the carrier.
@@ -162,7 +147,7 @@ class TextMapPropagator(abc.ABC):
def inject(
self,
carrier: CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
setter: Setter[CarrierT] = default_setter,
) -> None:
"""Inject values from a Context into a carrier.
@@ -185,7 +170,7 @@ class TextMapPropagator(abc.ABC):
@property
@abc.abstractmethod
- def fields(self) -> typing.Set[str]:
+ def fields(self) -> set[str]:
"""
Gets the fields set in the carrier by the `inject` method.
diff --git a/opentelemetry-api/src/opentelemetry/trace/__init__.py b/opentelemetry-api/src/opentelemetry/trace/__init__.py
index 7ec36a2..996576c 100644
--- a/opentelemetry-api/src/opentelemetry/trace/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/trace/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
The OpenTelemetry tracing API describes the classes used to generate
@@ -74,11 +63,11 @@ either implicit or explicit context propagation consistently throughout.
"""
import os
-import typing
from abc import ABC, abstractmethod
+from collections.abc import Iterator, Sequence
from enum import Enum
from logging import getLogger
-from typing import Iterator, Optional, Sequence, cast
+from typing import cast
from typing_extensions import deprecated
@@ -156,7 +145,7 @@ class Link(_LinkBase):
return 0
-_Links = Optional[Sequence[Link]]
+_Links = Sequence[Link] | None
class SpanKind(Enum):
@@ -193,9 +182,9 @@ class TracerProvider(ABC):
def get_tracer(
self,
instrumenting_module_name: str,
- instrumenting_library_version: typing.Optional[str] = None,
- schema_url: typing.Optional[str] = None,
- attributes: typing.Optional[types.Attributes] = None,
+ instrumenting_library_version: str | None = None,
+ schema_url: str | None = None,
+ attributes: types.Attributes | None = None,
) -> "Tracer":
"""Returns a `Tracer` for use by the given instrumentation library.
@@ -236,9 +225,9 @@ class NoOpTracerProvider(TracerProvider):
def get_tracer(
self,
instrumenting_module_name: str,
- instrumenting_library_version: typing.Optional[str] = None,
- schema_url: typing.Optional[str] = None,
- attributes: typing.Optional[types.Attributes] = None,
+ instrumenting_library_version: str | None = None,
+ schema_url: str | None = None,
+ attributes: types.Attributes | None = None,
) -> "Tracer":
# pylint:disable=no-self-use,unused-argument
return NoOpTracer()
@@ -258,9 +247,9 @@ class ProxyTracerProvider(TracerProvider):
def get_tracer(
self,
instrumenting_module_name: str,
- instrumenting_library_version: typing.Optional[str] = None,
- schema_url: typing.Optional[str] = None,
- attributes: typing.Optional[types.Attributes] = None,
+ instrumenting_library_version: str | None = None,
+ schema_url: str | None = None,
+ attributes: types.Attributes | None = None,
) -> "Tracer":
if _TRACER_PROVIDER:
return _TRACER_PROVIDER.get_tracer(
@@ -288,11 +277,11 @@ class Tracer(ABC):
def start_span(
self,
name: str,
- context: Optional[Context] = None,
+ context: Context | None = None,
kind: SpanKind = SpanKind.INTERNAL,
attributes: types.Attributes = None,
links: _Links = None,
- start_time: Optional[int] = None,
+ start_time: int | None = None,
record_exception: bool = True,
set_status_on_exception: bool = True,
) -> "Span":
@@ -343,11 +332,11 @@ class Tracer(ABC):
def start_as_current_span(
self,
name: str,
- context: Optional[Context] = None,
+ context: Context | None = None,
kind: SpanKind = SpanKind.INTERNAL,
attributes: types.Attributes = None,
links: _Links = None,
- start_time: Optional[int] = None,
+ start_time: int | None = None,
record_exception: bool = True,
set_status_on_exception: bool = True,
end_on_exit: bool = True,
@@ -419,15 +408,15 @@ class ProxyTracer(Tracer):
def __init__(
self,
instrumenting_module_name: str,
- instrumenting_library_version: typing.Optional[str] = None,
- schema_url: typing.Optional[str] = None,
- attributes: typing.Optional[types.Attributes] = None,
+ instrumenting_library_version: str | None = None,
+ schema_url: str | None = None,
+ attributes: types.Attributes | None = None,
):
self._instrumenting_module_name = instrumenting_module_name
self._instrumenting_library_version = instrumenting_library_version
self._schema_url = schema_url
self._attributes = attributes
- self._real_tracer: Optional[Tracer] = None
+ self._real_tracer: Tracer | None = None
self._noop_tracer = NoOpTracer()
@property
@@ -463,11 +452,11 @@ class NoOpTracer(Tracer):
def start_span(
self,
name: str,
- context: Optional[Context] = None,
+ context: Context | None = None,
kind: SpanKind = SpanKind.INTERNAL,
attributes: types.Attributes = None,
links: _Links = None,
- start_time: Optional[int] = None,
+ start_time: int | None = None,
record_exception: bool = True,
set_status_on_exception: bool = True,
) -> "Span":
@@ -491,11 +480,11 @@ class NoOpTracer(Tracer):
def start_as_current_span(
self,
name: str,
- context: Optional[Context] = None,
+ context: Context | None = None,
kind: SpanKind = SpanKind.INTERNAL,
attributes: types.Attributes = None,
links: _Links = None,
- start_time: Optional[int] = None,
+ start_time: int | None = None,
record_exception: bool = True,
set_status_on_exception: bool = True,
end_on_exit: bool = True,
@@ -528,16 +517,16 @@ class _DefaultTracer(NoOpTracer):
_TRACER_PROVIDER_SET_ONCE = Once()
-_TRACER_PROVIDER: Optional[TracerProvider] = None
+_TRACER_PROVIDER: TracerProvider | None = None
_PROXY_TRACER_PROVIDER = ProxyTracerProvider()
def get_tracer(
instrumenting_module_name: str,
- instrumenting_library_version: typing.Optional[str] = None,
- tracer_provider: Optional[TracerProvider] = None,
- schema_url: typing.Optional[str] = None,
- attributes: typing.Optional[types.Attributes] = None,
+ instrumenting_library_version: str | None = None,
+ tracer_provider: TracerProvider | None = None,
+ schema_url: str | None = None,
+ attributes: types.Attributes | None = None,
) -> "Tracer":
"""Returns a `Tracer` for use by the given instrumentation library.
diff --git a/opentelemetry-api/src/opentelemetry/trace/propagation/__init__.py b/opentelemetry-api/src/opentelemetry/trace/propagation/__init__.py
index d3529e1..93a0916 100644
--- a/opentelemetry-api/src/opentelemetry/trace/propagation/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/trace/propagation/__init__.py
@@ -1,17 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 Optional
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.context import create_key, get_value, set_value
from opentelemetry.context.context import Context
@@ -21,9 +9,7 @@ SPAN_KEY = "current-span"
_SPAN_KEY = create_key("current-span")
-def set_span_in_context(
- span: Span, context: Optional[Context] = None
-) -> Context:
+def set_span_in_context(span: Span, context: Context | None = None) -> Context:
"""Set the span in the given context.
Args:
@@ -35,7 +21,7 @@ def set_span_in_context(
return ctx
-def get_current_span(context: Optional[Context] = None) -> Span:
+def get_current_span(context: Context | None = None) -> Span:
"""Retrieve the current span.
Args:
diff --git a/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py b/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py
index af16a08..97c64d9 100644
--- a/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py
+++ b/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py
@@ -1,19 +1,7 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
#
import re
-import typing
from opentelemetry import trace
from opentelemetry.context.context import Context
@@ -36,7 +24,7 @@ class TraceContextTextMapPropagator(textmap.TextMapPropagator):
def extract(
self,
carrier: textmap.CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter,
) -> Context:
"""Extracts SpanContext from the carrier.
@@ -89,7 +77,7 @@ class TraceContextTextMapPropagator(textmap.TextMapPropagator):
def inject(
self,
carrier: textmap.CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter,
) -> None:
"""Injects SpanContext into the carrier.
@@ -109,7 +97,7 @@ class TraceContextTextMapPropagator(textmap.TextMapPropagator):
)
@property
- def fields(self) -> typing.Set[str]:
+ def fields(self) -> set[str]:
"""Returns a set with the fields set in `inject`.
See
diff --git a/opentelemetry-api/src/opentelemetry/trace/span.py b/opentelemetry-api/src/opentelemetry/trace/span.py
index b0cda47..60befed 100644
--- a/opentelemetry-api/src/opentelemetry/trace/span.py
+++ b/opentelemetry-api/src/opentelemetry/trace/span.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
import abc
import logging
import re
@@ -57,7 +60,7 @@ class Span(abc.ABC):
"""A span represents a single operation within a trace."""
@abc.abstractmethod
- def end(self, end_time: typing.Optional[int] = None) -> None:
+ def end(self, end_time: int | None = None) -> None:
"""Sets the current time as the span's end time.
The span's end time is the wall time at which the operation finished.
@@ -108,7 +111,7 @@ class Span(abc.ABC):
self,
name: str,
attributes: types.Attributes = None,
- timestamp: typing.Optional[int] = None,
+ timestamp: int | None = None,
) -> None:
"""Adds an `Event`.
@@ -159,8 +162,8 @@ class Span(abc.ABC):
@abc.abstractmethod
def set_status(
self,
- status: typing.Union[Status, StatusCode],
- description: typing.Optional[str] = None,
+ status: Status | StatusCode,
+ description: str | None = None,
) -> None:
"""Sets the Status of the Span. If used, this will override the default
Span status.
@@ -171,7 +174,7 @@ class Span(abc.ABC):
self,
exception: BaseException,
attributes: types.Attributes = None,
- timestamp: typing.Optional[int] = None,
+ timestamp: int | None = None,
escaped: bool = False,
) -> None:
"""Records an exception as a span event."""
@@ -185,9 +188,9 @@ class Span(abc.ABC):
def __exit__(
self,
- exc_type: typing.Optional[typing.Type[BaseException]],
- exc_val: typing.Optional[BaseException],
- exc_tb: typing.Optional[python_types.TracebackType],
+ exc_type: type[BaseException] | None,
+ exc_val: BaseException | None,
+ exc_tb: python_types.TracebackType | None,
) -> None:
"""Ends context manager and calls `end` on the `Span`."""
@@ -197,17 +200,21 @@ class Span(abc.ABC):
class TraceFlags(int):
"""A bitmask that represents options specific to the trace.
- The only supported option is the "sampled" flag (``0x01``). If set, this
- flag indicates that the trace may have been sampled upstream.
+ Supported flags:
+ - "sampled" (``0x01``): Indicates the trace may have been sampled upstream.
+ - "random-trace-id" (``0x02``): Indicates the trace ID was generated
+ randomly, with at least the 7 rightmost bytes (56 bits) selected with
+ uniform distribution.
See the `W3C Trace Context - Traceparent`_ spec for details.
.. _W3C Trace Context - Traceparent:
- https://www.w3.org/TR/trace-context/#trace-flags
+ https://www.w3.org/TR/trace-context-2/#trace-flags
"""
DEFAULT = 0x00
SAMPLED = 0x01
+ RANDOM_TRACE_ID = 0x02
@classmethod
def get_default(cls) -> "TraceFlags":
@@ -217,6 +224,10 @@ class TraceFlags(int):
def sampled(self) -> bool:
return bool(self & TraceFlags.SAMPLED)
+ @property
+ def random_trace_id(self) -> bool:
+ return bool(self & TraceFlags.RANDOM_TRACE_ID)
+
DEFAULT_TRACE_OPTIONS = TraceFlags.get_default()
@@ -234,9 +245,7 @@ class TraceState(typing.Mapping[str, str]):
def __init__(
self,
- entries: typing.Optional[
- typing.Sequence[typing.Tuple[str, str]]
- ] = None,
+ entries: typing.Sequence[tuple[str, str]] | None = None,
) -> None:
self._dict = {} # type: dict[str, str]
if entries is None:
@@ -365,7 +374,7 @@ class TraceState(typing.Mapping[str, str]):
return ",".join(key + "=" + value for key, value in self._dict.items())
@classmethod
- def from_header(cls, header_list: typing.List[str]) -> "TraceState":
+ def from_header(cls, header_list: list[str]) -> "TraceState":
"""Parses one or more w3c tracestate header into a TraceState.
Args:
@@ -383,7 +392,7 @@ class TraceState(typing.Mapping[str, str]):
"""
pairs = {} # type: dict[str, str]
for header in header_list:
- members: typing.List[str] = re.split(_delimiter_pattern, header)
+ members: list[str] = re.split(_delimiter_pattern, header)
for member in members:
# empty members are valid, but no need to process further.
if not member:
@@ -395,7 +404,7 @@ class TraceState(typing.Mapping[str, str]):
member,
)
return cls()
- groups: typing.Tuple[str, ...] = match.groups()
+ groups: tuple[str, ...] = match.groups()
key, _eq, value = groups
# duplicate keys are not legal in header
if key in pairs:
@@ -422,9 +431,7 @@ _TRACE_ID_MAX_VALUE = 2**128 - 1
_SPAN_ID_MAX_VALUE = 2**64 - 1
-class SpanContext(
- typing.Tuple[int, int, bool, "TraceFlags", "TraceState", bool]
-):
+class SpanContext(tuple[int, int, bool, "TraceFlags", "TraceState", bool]):
"""The state of a Span to propagate between processes.
This class includes the immutable attributes of a :class:`.Span` that must
@@ -463,7 +470,7 @@ class SpanContext(
def __getnewargs__(
self,
- ) -> typing.Tuple[int, int, bool, "TraceFlags", "TraceState"]:
+ ) -> tuple[int, int, bool, "TraceFlags", "TraceState"]:
return (
self.trace_id,
self.span_id,
@@ -525,7 +532,7 @@ class NonRecordingSpan(Span):
def is_recording(self) -> bool:
return False
- def end(self, end_time: typing.Optional[int] = None) -> None:
+ def end(self, end_time: int | None = None) -> None:
pass
def set_attributes(
@@ -540,7 +547,7 @@ class NonRecordingSpan(Span):
self,
name: str,
attributes: types.Attributes = None,
- timestamp: typing.Optional[int] = None,
+ timestamp: int | None = None,
) -> None:
pass
@@ -556,8 +563,8 @@ class NonRecordingSpan(Span):
def set_status(
self,
- status: typing.Union[Status, StatusCode],
- description: typing.Optional[str] = None,
+ status: Status | StatusCode,
+ description: str | None = None,
) -> None:
pass
@@ -565,7 +572,7 @@ class NonRecordingSpan(Span):
self,
exception: BaseException,
attributes: types.Attributes = None,
- timestamp: typing.Optional[int] = None,
+ timestamp: int | None = None,
escaped: bool = False,
) -> None:
pass
diff --git a/opentelemetry-api/src/opentelemetry/trace/status.py b/opentelemetry-api/src/opentelemetry/trace/status.py
index ada7fa1..1a81426 100644
--- a/opentelemetry-api/src/opentelemetry/trace/status.py
+++ b/opentelemetry-api/src/opentelemetry/trace/status.py
@@ -1,20 +1,8 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import enum
import logging
-import typing
logger = logging.getLogger(__name__)
@@ -44,7 +32,7 @@ class Status:
def __init__(
self,
status_code: StatusCode = StatusCode.UNSET,
- description: typing.Optional[str] = None,
+ description: str | None = None,
):
self._status_code = status_code
self._description = None
@@ -67,7 +55,7 @@ class Status:
return self._status_code
@property
- def description(self) -> typing.Optional[str]:
+ def description(self) -> str | None:
"""Status description"""
return self._description
diff --git a/opentelemetry-api/src/opentelemetry/util/_decorator.py b/opentelemetry-api/src/opentelemetry/util/_decorator.py
index b1a7f6c..b724b9f 100644
--- a/opentelemetry-api/src/opentelemetry/util/_decorator.py
+++ b/opentelemetry-api/src/opentelemetry/util/_decorator.py
@@ -1,21 +1,11 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import contextlib
import functools
import inspect
-from typing import TYPE_CHECKING, Callable, Generic, Iterator, TypeVar
+from collections.abc import Callable, Iterator
+from typing import TYPE_CHECKING, Generic, TypeVar
V = TypeVar("V")
R = TypeVar("R") # Return type
diff --git a/opentelemetry-api/src/opentelemetry/util/_importlib_metadata.py b/opentelemetry-api/src/opentelemetry/util/_importlib_metadata.py
index a527bd7..be779f1 100644
--- a/opentelemetry-api/src/opentelemetry/util/_importlib_metadata.py
+++ b/opentelemetry-api/src/opentelemetry/util/_importlib_metadata.py
@@ -1,23 +1,18 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-from functools import cache
+"""
+Caching and compatibility wrapper for standard library ``importlib.metadata``.
+
+This module caches ``entry_points()`` to avoid reloading entry points from disk on every call.
+It also normalizes minor differences across python versions 3.10+. References to issues:
-# FIXME: Use importlib.metadata (not importlib_metadata)
-# when support for 3.11 is dropped if the rest of
-# the supported versions at that time have the same API.
-from importlib_metadata import ( # type: ignore
+- https://github.com/open-telemetry/opentelemetry-python/pull/4735
+- https://github.com/open-telemetry/opentelemetry-python/pull/5203
+"""
+
+from functools import cache
+from importlib.metadata import (
Distribution,
EntryPoint,
EntryPoints,
@@ -26,20 +21,23 @@ from importlib_metadata import ( # type: ignore
requires,
version,
)
-from importlib_metadata import (
- entry_points as original_entry_points,
-)
+from importlib.metadata import entry_points as original_entry_points
+from typing import Any
+
+
+def _as_entry_points(eps: Any) -> EntryPoints:
+ if isinstance(eps, EntryPoints):
+ return eps
+ # Handle Python 3.10 SelectableGroups (dict-like)
+ return EntryPoints(ep for group_eps in eps.values() for ep in group_eps)
@cache
-def _original_entry_points_cached():
- return original_entry_points()
+def _original_entry_points_cached() -> EntryPoints:
+ return _as_entry_points(original_entry_points())
def entry_points(**params) -> EntryPoints:
- """Replacement for importlib_metadata.entry_points that caches getting all the entry points.
-
- That part can be very slow, and OTel uses this function many times."""
return _original_entry_points_cached().select(**params)
diff --git a/opentelemetry-api/src/opentelemetry/util/_once.py b/opentelemetry-api/src/opentelemetry/util/_once.py
index c0cee43..59f5623 100644
--- a/opentelemetry-api/src/opentelemetry/util/_once.py
+++ b/opentelemetry-api/src/opentelemetry/util/_once.py
@@ -1,19 +1,8 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
+from collections.abc import Callable
from threading import Lock
-from typing import Callable
class Once:
diff --git a/opentelemetry-api/src/opentelemetry/util/_providers.py b/opentelemetry-api/src/opentelemetry/util/_providers.py
index b748ead..ac801ba 100644
--- a/opentelemetry-api/src/opentelemetry/util/_providers.py
+++ b/opentelemetry-api/src/opentelemetry/util/_providers.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from logging import getLogger
from os import environ
diff --git a/opentelemetry-api/src/opentelemetry/util/re.py b/opentelemetry-api/src/opentelemetry/util/re.py
index 28ecd03..5ac7813 100644
--- a/opentelemetry-api/src/opentelemetry/util/re.py
+++ b/opentelemetry-api/src/opentelemetry/util/re.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
+from collections.abc import Mapping
from logging import getLogger
from re import compile, split
-from typing import Dict, List, Mapping
from urllib.parse import unquote
from typing_extensions import deprecated
@@ -75,8 +64,8 @@ def parse_env_headers(s: str, liberal: bool = False) -> Mapping[str, str]:
If ``liberal`` is True we try to parse ``s`` anyway to be more compatible
with other languages SDKs that accept non URL-encoded headers by default.
"""
- headers: Dict[str, str] = {}
- headers_list: List[str] = split(_DELIMITER_PATTERN, s)
+ headers: dict[str, str] = {}
+ headers_list: list[str] = split(_DELIMITER_PATTERN, s)
for header in headers_list:
if not header: # empty string
continue
diff --git a/opentelemetry-api/src/opentelemetry/util/types.py b/opentelemetry-api/src/opentelemetry/util/types.py
index 7455c74..b8a7538 100644
--- a/opentelemetry-api/src/opentelemetry/util/types.py
+++ b/opentelemetry-api/src/opentelemetry/util/types.py
@@ -1,57 +1,44 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-from typing import Mapping, Optional, Sequence, Tuple, Union
+from collections.abc import Mapping, Sequence
# This is the implementation of the "Any" type as specified by the specifications of OpenTelemetry data model for logs.
# For more details, refer to the OTel specification:
# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#type-any
-AnyValue = Union[
- str,
- bool,
- int,
- float,
- bytes,
- Sequence["AnyValue"],
- Mapping[str, "AnyValue"],
- None,
-]
+AnyValue = (
+ str
+ | bool
+ | int
+ | float
+ | bytes
+ | Sequence["AnyValue"]
+ | Mapping[str, "AnyValue"]
+ | None
+)
-AttributeValue = Union[
- str,
- bool,
- int,
- float,
- Sequence[str],
- Sequence[bool],
- Sequence[int],
- Sequence[float],
-]
-Attributes = Optional[Mapping[str, AttributeValue]]
-AttributesAsKey = Tuple[
- Tuple[
+AttributeValue = (
+ str
+ | bool
+ | int
+ | float
+ | Sequence[str]
+ | Sequence[bool]
+ | Sequence[int]
+ | Sequence[float]
+)
+Attributes = Mapping[str, AttributeValue] | None
+AttributesAsKey = tuple[
+ tuple[
str,
- Union[
- str,
- bool,
- int,
- float,
- Tuple[Optional[str], ...],
- Tuple[Optional[bool], ...],
- Tuple[Optional[int], ...],
- Tuple[Optional[float], ...],
- ],
+ str
+ | bool
+ | int
+ | float
+ | tuple[str | None, ...]
+ | tuple[bool | None, ...]
+ | tuple[int | None, ...]
+ | tuple[float | None, ...],
],
...,
]
diff --git a/opentelemetry-api/src/opentelemetry/version/__init__.py b/opentelemetry-api/src/opentelemetry/version/__init__.py
index 49e1254..a400b9b 100644
--- a/opentelemetry-api/src/opentelemetry/version/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.0"
+__version__ = "1.42.1"
diff --git a/opentelemetry-api/test-requirements.txt b/opentelemetry-api/test-requirements.txt
index 5ee2302..ff6405a 100644
--- a/opentelemetry-api/test-requirements.txt
+++ b/opentelemetry-api/test-requirements.txt
@@ -1,6 +1,4 @@
asgiref==3.7.2
-importlib-metadata==8.5.0 ; python_version < "3.9"
-importlib-metadata==8.7.0 ; python_version >= "3.9"
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
diff --git a/opentelemetry-api/tests/__init__.py b/opentelemetry-api/tests/__init__.py
index b0a6f42..e57cf4a 100644
--- a/opentelemetry-api/tests/__init__.py
+++ b/opentelemetry-api/tests/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/opentelemetry-api/tests/attributes/test_attributes.py b/opentelemetry-api/tests/attributes/test_attributes.py
index ae3900c..fb6259f 100644
--- a/opentelemetry-api/tests/attributes/test_attributes.py
+++ b/opentelemetry-api/tests/attributes/test_attributes.py
@@ -1,23 +1,12 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
import copy
import unittest
import unittest.mock
-from typing import MutableSequence
+from collections.abc import MutableSequence
from opentelemetry.attributes import (
BoundedAttributes,
diff --git a/opentelemetry-api/tests/baggage/propagation/test_propagation.py b/opentelemetry-api/tests/baggage/propagation/test_propagation.py
index b9de7f3..dca29ae 100644
--- a/opentelemetry-api/tests/baggage/propagation/test_propagation.py
+++ b/opentelemetry-api/tests/baggage/propagation/test_propagation.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
#
# type: ignore
diff --git a/opentelemetry-api/tests/baggage/test_baggage.py b/opentelemetry-api/tests/baggage/test_baggage.py
index 5eb73d5..5ead817 100644
--- a/opentelemetry-api/tests/baggage/test_baggage.py
+++ b/opentelemetry-api/tests/baggage/test_baggage.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
diff --git a/opentelemetry-api/tests/context/base_context.py b/opentelemetry-api/tests/context/base_context.py
index 395229b..45f32cf 100644
--- a/opentelemetry-api/tests/context/base_context.py
+++ b/opentelemetry-api/tests/context/base_context.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
from logging import ERROR
diff --git a/opentelemetry-api/tests/context/test_context.py b/opentelemetry-api/tests/context/test_context.py
index 18f6f68..ce5327d 100644
--- a/opentelemetry-api/tests/context/test_context.py
+++ b/opentelemetry-api/tests/context/test_context.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
from unittest.mock import patch
diff --git a/opentelemetry-api/tests/context/test_contextvars_context.py b/opentelemetry-api/tests/context/test_contextvars_context.py
index e9af310..5e3385b 100644
--- a/opentelemetry-api/tests/context/test_contextvars_context.py
+++ b/opentelemetry-api/tests/context/test_contextvars_context.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from unittest.mock import patch
diff --git a/opentelemetry-api/tests/distributedcontext/__init__.py b/opentelemetry-api/tests/distributedcontext/__init__.py
index b0a6f42..e57cf4a 100644
--- a/opentelemetry-api/tests/distributedcontext/__init__.py
+++ b/opentelemetry-api/tests/distributedcontext/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/opentelemetry-api/tests/events/test_event.py b/opentelemetry-api/tests/events/test_event.py
index 227dcf5..03238a0 100644
--- a/opentelemetry-api/tests/events/test_event.py
+++ b/opentelemetry-api/tests/events/test_event.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
import unittest
from opentelemetry._events import Event
diff --git a/opentelemetry-api/tests/events/test_event_logger_provider.py b/opentelemetry-api/tests/events/test_event_logger_provider.py
index 425697b..e9013b5 100644
--- a/opentelemetry-api/tests/events/test_event_logger_provider.py
+++ b/opentelemetry-api/tests/events/test_event_logger_provider.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
# type:ignore
import unittest
from unittest.mock import Mock, patch
diff --git a/opentelemetry-api/tests/events/test_proxy_event.py b/opentelemetry-api/tests/events/test_proxy_event.py
index 44121a9..562fb28 100644
--- a/opentelemetry-api/tests/events/test_proxy_event.py
+++ b/opentelemetry-api/tests/events/test_proxy_event.py
@@ -1,5 +1,7 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
# pylint: disable=W0212,W0222,W0221
-import typing
import unittest
import opentelemetry._events as events
@@ -11,9 +13,9 @@ class TestProvider(events.NoOpEventLoggerProvider):
def get_event_logger(
self,
name: str,
- version: typing.Optional[str] = None,
- schema_url: typing.Optional[str] = None,
- attributes: typing.Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
) -> events.EventLogger:
return LoggerTest(name)
diff --git a/opentelemetry-api/tests/logs/test_log_record.py b/opentelemetry-api/tests/logs/test_log_record.py
index a06ed8d..2b6f893 100644
--- a/opentelemetry-api/tests/logs/test_log_record.py
+++ b/opentelemetry-api/tests/logs/test_log_record.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
from unittest.mock import patch
@@ -25,3 +14,8 @@ class TestLogRecord(unittest.TestCase):
def test_log_record_observed_timestamp_default(self, time_ns_mock): # type: ignore
time_ns_mock.return_value = OBSERVED_TIMESTAMP
self.assertEqual(LogRecord().observed_timestamp, OBSERVED_TIMESTAMP)
+
+ def test_log_record_exception(self):
+ exc = ValueError("boom")
+ log_record = LogRecord(exception=exc)
+ self.assertIs(log_record.exception, exc)
diff --git a/opentelemetry-api/tests/logs/test_logger_provider.py b/opentelemetry-api/tests/logs/test_logger_provider.py
index 2bd4041..71ba26a 100644
--- a/opentelemetry-api/tests/logs/test_logger_provider.py
+++ b/opentelemetry-api/tests/logs/test_logger_provider.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type:ignore
import unittest
diff --git a/opentelemetry-api/tests/logs/test_proxy.py b/opentelemetry-api/tests/logs/test_proxy.py
index d72ccc7..71772eb 100644
--- a/opentelemetry-api/tests/logs/test_proxy.py
+++ b/opentelemetry-api/tests/logs/test_proxy.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=W0212,W0222,W0221
-import typing
import unittest
+from unittest.mock import Mock
import opentelemetry._logs._internal as _logs_internal
from opentelemetry import _logs
@@ -26,9 +15,9 @@ class TestProvider(_logs.NoOpLoggerProvider):
def get_logger(
self,
name: str,
- version: typing.Optional[str] = None,
- schema_url: typing.Optional[str] = None,
- attributes: typing.Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
) -> _logs.Logger:
return LoggerTest(name)
@@ -36,7 +25,7 @@ class TestProvider(_logs.NoOpLoggerProvider):
class LoggerTest(_logs.NoOpLogger):
def emit(
self,
- record: typing.Optional[_logs.LogRecord] = None,
+ record: _logs.LogRecord | None = None,
*,
timestamp=None,
observed_timestamp=None,
@@ -46,6 +35,7 @@ class LoggerTest(_logs.NoOpLogger):
body=None,
attributes=None,
event_name=None,
+ exception: BaseException | None = None,
) -> None:
pass
@@ -74,3 +64,13 @@ class TestProxy(LoggingGlobalsTest, unittest.TestCase):
# references to the old provider still work but return real logger now
real_logger = provider.get_logger("proxy-test")
self.assertIsInstance(real_logger, LoggerTest)
+
+ def test_proxy_logger_forwards_record_with_exception(self):
+ logger = _logs_internal.ProxyLogger("proxy-test")
+ logger._real_logger = Mock(spec=LoggerTest("proxy-test"))
+ record = _logs.LogRecord(exception=ValueError("boom"))
+
+ self.assertIsNotNone(logger._real_logger)
+ logger.emit(record)
+
+ logger._real_logger.emit.assert_called_once_with(record)
diff --git a/opentelemetry-api/tests/metrics/test_instruments.py b/opentelemetry-api/tests/metrics/test_instruments.py
index 982cb6b..0f844b1 100644
--- a/opentelemetry-api/tests/metrics/test_instruments.py
+++ b/opentelemetry-api/tests/metrics/test_instruments.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
from inspect import Signature, isabstract, signature
diff --git a/opentelemetry-api/tests/metrics/test_meter.py b/opentelemetry-api/tests/metrics/test_meter.py
index 5a7ef3b..b910046 100644
--- a/opentelemetry-api/tests/metrics/test_meter.py
+++ b/opentelemetry-api/tests/metrics/test_meter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
from logging import WARNING
diff --git a/opentelemetry-api/tests/metrics/test_meter_provider.py b/opentelemetry-api/tests/metrics/test_meter_provider.py
index dfaf94b..3ea3e20 100644
--- a/opentelemetry-api/tests/metrics/test_meter_provider.py
+++ b/opentelemetry-api/tests/metrics/test_meter_provider.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
# pylint: disable=protected-access
diff --git a/opentelemetry-api/tests/metrics/test_observation.py b/opentelemetry-api/tests/metrics/test_observation.py
index a1a863f..adc40d5 100644
--- a/opentelemetry-api/tests/metrics/test_observation.py
+++ b/opentelemetry-api/tests/metrics/test_observation.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from unittest import TestCase
diff --git a/opentelemetry-api/tests/metrics/test_subclass_instantiation.py b/opentelemetry-api/tests/metrics/test_subclass_instantiation.py
index 67001e8..f5f9a93 100644
--- a/opentelemetry-api/tests/metrics/test_subclass_instantiation.py
+++ b/opentelemetry-api/tests/metrics/test_subclass_instantiation.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
# NOTE: The tests in this file are intended to test the semver compatibility of the public API.
@@ -19,7 +8,6 @@
# pylint: disable=useless-parent-delegation,arguments-differ
-from typing import Optional
from opentelemetry.metrics import (
Asynchronous,
@@ -41,8 +29,8 @@ class MeterProviderImplTest(MeterProvider):
def get_meter(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
) -> Meter:
return super().get_meter(name, version, schema_url)
diff --git a/opentelemetry-api/tests/mypysmoke.py b/opentelemetry-api/tests/mypysmoke.py
index ede4af7..ce373f5 100644
--- a/opentelemetry-api/tests/mypysmoke.py
+++ b/opentelemetry-api/tests/mypysmoke.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import opentelemetry.trace
diff --git a/opentelemetry-api/tests/propagators/test__envcarrier.py b/opentelemetry-api/tests/propagators/test__envcarrier.py
index eb2b99c..e3e36f4 100644
--- a/opentelemetry-api/tests/propagators/test__envcarrier.py
+++ b/opentelemetry-api/tests/propagators/test__envcarrier.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
@@ -25,12 +14,35 @@ from opentelemetry.context import Context, get_current
from opentelemetry.propagators._envcarrier import (
EnvironmentGetter,
EnvironmentSetter,
+ _normalize_key,
)
from opentelemetry.trace.propagation.tracecontext import (
TraceContextTextMapPropagator,
)
+class TestNormalizeKey(unittest.TestCase):
+ """Unit tests for _normalize_key."""
+
+ def test_lowercase(self):
+ self.assertEqual(_normalize_key("traceparent"), "TRACEPARENT")
+
+ def test_hyphen_replaced(self):
+ self.assertEqual(_normalize_key("trace-parent"), "TRACE_PARENT")
+
+ def test_non_ascii_replaced(self):
+ self.assertEqual(_normalize_key("héllo"), "H_LLO")
+
+ def test_leading_digit_prefixed(self):
+ self.assertEqual(_normalize_key("1abc"), "_1ABC")
+
+ def test_already_valid(self):
+ self.assertEqual(_normalize_key("ALREADY_VALID"), "ALREADY_VALID")
+
+ def test_empty_string(self):
+ self.assertEqual(_normalize_key(""), "")
+
+
class TestEnvironmentGetter(unittest.TestCase):
"""Unit tests for EnvironmentGetter."""
@@ -78,7 +90,7 @@ class TestEnvironmentGetter(unittest.TestCase):
with patch.dict(os.environ, test_env, clear=True):
getter = EnvironmentGetter()
keys = getter.keys({})
- expected_keys = {"key1", "key2", "key3"}
+ expected_keys = {"KEY1", "KEY2", "KEY3"}
self.assertEqual(set(keys), expected_keys)
def test_keys_empty_environment(self):
@@ -470,9 +482,9 @@ class TestEnvironmentCarrierWithBaggage(unittest.TestCase):
self.assertEqual(baggage1, baggage2)
@patch("opentelemetry.baggage.propagation.get_all")
- @patch("opentelemetry.baggage.propagation._format_baggage")
- def test_fields(self, mock_format_baggage, mock_get_all):
+ def test_fields(self, mock_get_all):
"""Test that propagator.fields matches injected keys."""
+ mock_get_all.return_value = {"key": "value"}
mock_setter = Mock()
self.propagator.inject({}, setter=mock_setter)
diff --git a/opentelemetry-api/tests/propagators/test_composite.py b/opentelemetry-api/tests/propagators/test_composite.py
index 14d1894..786a05e 100644
--- a/opentelemetry-api/tests/propagators/test_composite.py
+++ b/opentelemetry-api/tests/propagators/test_composite.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
diff --git a/opentelemetry-api/tests/propagators/test_global_httptextformat.py b/opentelemetry-api/tests/propagators/test_global_httptextformat.py
index c383ec6..2101894 100644
--- a/opentelemetry-api/tests/propagators/test_global_httptextformat.py
+++ b/opentelemetry-api/tests/propagators/test_global_httptextformat.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
diff --git a/opentelemetry-api/tests/propagators/test_propagators.py b/opentelemetry-api/tests/propagators/test_propagators.py
index 6215b99..a62f8c0 100644
--- a/opentelemetry-api/tests/propagators/test_propagators.py
+++ b/opentelemetry-api/tests/propagators/test_propagators.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
diff --git a/opentelemetry-api/tests/propagators/test_w3cbaggagepropagator.py b/opentelemetry-api/tests/propagators/test_w3cbaggagepropagator.py
index 46db45f..178da65 100644
--- a/opentelemetry-api/tests/propagators/test_w3cbaggagepropagator.py
+++ b/opentelemetry-api/tests/propagators/test_w3cbaggagepropagator.py
@@ -1,28 +1,15 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
#
# type: ignore
from logging import WARNING
from unittest import TestCase
from unittest.mock import Mock, patch
+from urllib.parse import quote_plus
from opentelemetry.baggage import get_all, set_baggage
-from opentelemetry.baggage.propagation import (
- W3CBaggagePropagator,
- _format_baggage,
-)
+from opentelemetry.baggage.propagation import W3CBaggagePropagator
from opentelemetry.context import get_current
@@ -99,15 +86,45 @@ class TestW3CBaggagePropagator(TestCase):
long_value = "s" * (W3CBaggagePropagator._MAX_HEADER_LENGTH + 1)
header = f"key1={long_value}"
expected = {}
- self.assertEqual(self._extract(header), expected)
+ with self.assertLogs(level=WARNING) as warning:
+ self.assertEqual(self._extract(header), expected)
+ self.assertIn(
+ "exceeded the maximum number of bytes per baggage-string",
+ warning.output[0],
+ )
+
+ def test_extract_non_ascii_header(self):
+ """Extract should skip non-ASCII entries but keep valid ones."""
+ header = "key1=val1,key2=test\u00e9"
+ with self.assertLogs(level=WARNING) as warning:
+ result = self._extract(header)
+ self.assertEqual(result, {"key1": "val1"})
+ self.assertIn(
+ "contains non-ASCII characters",
+ warning.output[0],
+ )
+
+ def test_extract_non_ascii_header_exceeds_byte_limit(self):
+ """A header under the char limit but over the byte limit when encoded should be rejected."""
+ # Each \u00e9 encodes to 2 bytes in UTF-8, so we need fewer chars to exceed the byte limit
+ non_ascii_value = "\u00e9" * (W3CBaggagePropagator._MAX_HEADER_LENGTH)
+ header = f"key1={non_ascii_value}"
+ with self.assertLogs(level=WARNING) as warning:
+ result = self._extract(header)
+ self.assertEqual(result, {})
+ self.assertIn(
+ "exceeded the maximum number of bytes per baggage-string",
+ warning.output[0],
+ )
def test_header_contains_too_many_entries(self):
header = ",".join(
[f"key{k}=val" for k in range(W3CBaggagePropagator._MAX_PAIRS + 1)]
)
- self.assertEqual(
- len(self._extract(header)), W3CBaggagePropagator._MAX_PAIRS
- )
+ with self.assertLogs(level=WARNING):
+ self.assertEqual(
+ len(self._extract(header)), W3CBaggagePropagator._MAX_PAIRS
+ )
def test_header_contains_pair_too_long(self):
long_value = "s" * (W3CBaggagePropagator._MAX_PAIR_LENGTH + 1)
@@ -130,6 +147,7 @@ class TestW3CBaggagePropagator(TestCase):
)
def test_header_max_entries_skip_invalid_entry(self):
+ # 181 entries where index 2 is too long: skipping it leaves exactly 180 valid entries
with self.assertLogs(level=WARNING) as warning:
self.assertEqual(
self._extract(
@@ -155,11 +173,17 @@ class TestW3CBaggagePropagator(TestCase):
if index != 2
},
)
- self.assertIn(
- "exceeded the maximum number of list-members",
- warning.output[0],
+ self.assertTrue(
+ any(
+ "exceeded the maximum number of bytes per list-member"
+ in msg
+ for msg in warning.output
+ )
)
+ # 181 entries where index 2 is malformed (no '='): _apply_baggage_limits
+ # accepts the first 180 entries (indices 0-179), then the malformed entry
+ # at index 2 is skipped during parsing
with self.assertLogs(level=WARNING) as warning:
self.assertEqual(
self._extract(
@@ -178,13 +202,20 @@ class TestW3CBaggagePropagator(TestCase):
),
{
f"key{index}": f"value{index}"
- for index in range(W3CBaggagePropagator._MAX_PAIRS + 1)
+ for index in range(W3CBaggagePropagator._MAX_PAIRS)
if index != 2
},
)
- self.assertIn(
- "exceeded the maximum number of list-members",
- warning.output[0],
+ self.assertTrue(
+ any(
+ "exceeded the maximum number of list-members" in msg
+ for msg in warning.output
+ )
+ )
+ self.assertTrue(
+ any(
+ "doesn't match the format" in msg for msg in warning.output
+ )
)
def test_inject_no_baggage_entries(self):
@@ -224,8 +255,8 @@ class TestW3CBaggagePropagator(TestCase):
self.assertIn("key3=123.567", output)
@patch("opentelemetry.baggage.propagation.get_all")
- @patch("opentelemetry.baggage.propagation._format_baggage")
- def test_fields(self, mock_format_baggage, mock_baggage):
+ def test_fields(self, mock_baggage):
+ mock_baggage.return_value = {"key": "value"}
mock_setter = Mock()
self.propagator.inject({}, setter=mock_setter)
@@ -237,7 +268,13 @@ class TestW3CBaggagePropagator(TestCase):
self.assertEqual(inject_fields, self.propagator.fields)
- def test__format_baggage(self):
+ def test_encode_baggage_pairs(self):
+ def _format_baggage(entries):
+ return ",".join(
+ quote_plus(str(k)) + "=" + quote_plus(str(v))
+ for k, v in entries.items()
+ )
+
self.assertEqual(
_format_baggage({"key key": "value value"}), "key+key=value+value"
)
@@ -246,6 +283,81 @@ class TestW3CBaggagePropagator(TestCase):
"key%2Fkey=value%2Fvalue",
)
+ def test_inject_too_many_entries(self):
+ """Inject should drop entries exceeding _MAX_PAIRS."""
+ values = {
+ f"key{i}": f"val{i}"
+ for i in range(self.propagator._MAX_PAIRS + 10)
+ }
+ ctx = get_current()
+ for key, val in values.items():
+ ctx = set_baggage(key, val, context=ctx)
+ output = {}
+ with self.assertLogs(level=WARNING) as warning:
+ self.propagator.inject(output, context=ctx)
+ self.assertIn(
+ "exceeded the maximum number of list-members",
+ warning.output[0],
+ )
+ baggage_str = output.get("baggage", "")
+ pairs = baggage_str.split(",")
+ self.assertEqual(len(pairs), self.propagator._MAX_PAIRS)
+ self.assertNotIn(
+ f"key{self.propagator._MAX_PAIRS}=",
+ baggage_str,
+ )
+
+ def test_inject_entry_too_long(self):
+ """Inject should skip individual entries that exceed _MAX_PAIR_LENGTH."""
+ long_value = "x" * self.propagator._MAX_PAIR_LENGTH
+ values = {"key1": "val1", "big": long_value, "key3": "val3"}
+ ctx = get_current()
+ for key, val in values.items():
+ ctx = set_baggage(key, val, context=ctx)
+ output = {}
+ with self.assertLogs(level=WARNING) as warning:
+ self.propagator.inject(output, context=ctx)
+ self.assertIn(
+ "exceeded the maximum number of bytes per list-member",
+ warning.output[0],
+ )
+ baggage_str = output.get("baggage", "")
+ self.assertIn("key1=val1", baggage_str)
+ self.assertIn("key3=val3", baggage_str)
+ self.assertNotIn("big=", baggage_str)
+
+ def test_inject_total_header_too_long(self):
+ """Inject should stop adding entries when total header would exceed _MAX_HEADER_LENGTH."""
+ # Create entries that individually fit but collectively exceed the max header length
+ # Each entry "kNNN=<value>" with value ~200 chars; 50 of these > 8192
+ value = "v" * 200
+ values = {f"k{i:03d}": value for i in range(50)}
+ ctx = get_current()
+ for key, val in values.items():
+ ctx = set_baggage(key, val, context=ctx)
+ output = {}
+ with self.assertLogs(level=WARNING) as warning:
+ self.propagator.inject(output, context=ctx)
+ self.assertIn(
+ "exceeded the maximum number of bytes per baggage-string",
+ warning.output[0],
+ )
+ baggage_str = output.get("baggage", "")
+ self.assertLessEqual(
+ len(baggage_str), self.propagator._MAX_HEADER_LENGTH
+ )
+
+ def test_inject_empty_after_all_dropped(self):
+ """If all entries are too long, nothing should be injected."""
+ long_value = "x" * self.propagator._MAX_PAIR_LENGTH
+ values = {"big1": long_value, "big2": long_value}
+ ctx = get_current()
+ for key, val in values.items():
+ ctx = set_baggage(key, val, context=ctx)
+ output = {}
+ self.propagator.inject(output, context=ctx)
+ self.assertNotIn("baggage", output)
+
@patch("opentelemetry.baggage._BAGGAGE_KEY", new="abc")
def test_inject_extract(self):
carrier = {}
diff --git a/opentelemetry-api/tests/test_implementation.py b/opentelemetry-api/tests/test_implementation.py
index 769f1ee..1357e4a 100644
--- a/opentelemetry-api/tests/test_implementation.py
+++ b/opentelemetry-api/tests/test_implementation.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
diff --git a/opentelemetry-api/tests/trace/__init__.py b/opentelemetry-api/tests/trace/__init__.py
index b0a6f42..e57cf4a 100644
--- a/opentelemetry-api/tests/trace/__init__.py
+++ b/opentelemetry-api/tests/trace/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/opentelemetry-api/tests/trace/propagation/test_textmap.py b/opentelemetry-api/tests/trace/propagation/test_textmap.py
index 6b22d46..0865e1f 100644
--- a/opentelemetry-api/tests/trace/propagation/test_textmap.py
+++ b/opentelemetry-api/tests/trace/propagation/test_textmap.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
diff --git a/opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py b/opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py
index 4ad9e89..87d410c 100644
--- a/opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py
+++ b/opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py
@@ -1,20 +1,8 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
-import typing
import unittest
from unittest.mock import Mock, patch
@@ -39,7 +27,7 @@ class TestTraceContextFormat(unittest.TestCase):
If no traceparent header is received, the vendor creates a new
trace-id and parent-id that represents the current request.
"""
- output: typing.Dict[str, typing.List[str]] = {}
+ output: dict[str, list[str]] = {}
span = trace.get_current_span(FORMAT.extract(output))
self.assertIsInstance(span.get_span_context(), trace.SpanContext)
@@ -66,7 +54,7 @@ class TestTraceContextFormat(unittest.TestCase):
span_context.trace_state, {"foo": "1", "bar": "2", "baz": "3"}
)
self.assertTrue(span_context.is_remote)
- output: typing.Dict[str, str] = {}
+ output: dict[str, str] = {}
span = trace.NonRecordingSpan(span_context)
ctx = trace.set_span_in_context(span)
@@ -145,7 +133,7 @@ class TestTraceContextFormat(unittest.TestCase):
Empty and whitespace-only list members are allowed. Vendors MUST accept
empty tracestate headers but SHOULD avoid sending them.
"""
- output: typing.Dict[str, str] = {}
+ output: dict[str, str] = {}
span = trace.NonRecordingSpan(
trace.SpanContext(self.TRACE_ID, self.SPAN_ID, is_remote=False)
)
@@ -177,7 +165,7 @@ class TestTraceContextFormat(unittest.TestCase):
def test_propagate_invalid_context(self):
"""Do not propagate invalid trace context."""
- output: typing.Dict[str, str] = {}
+ output: dict[str, str] = {}
ctx = trace.set_span_in_context(trace.INVALID_SPAN)
FORMAT.inject(output, context=ctx)
self.assertFalse("traceparent" in output)
diff --git a/opentelemetry-api/tests/trace/test_defaultspan.py b/opentelemetry-api/tests/trace/test_defaultspan.py
index fbd3c00..af886b9 100644
--- a/opentelemetry-api/tests/trace/test_defaultspan.py
+++ b/opentelemetry-api/tests/trace/test_defaultspan.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
diff --git a/opentelemetry-api/tests/trace/test_globals.py b/opentelemetry-api/tests/trace/test_globals.py
index 920ed4b..c572b02 100644
--- a/opentelemetry-api/tests/trace/test_globals.py
+++ b/opentelemetry-api/tests/trace/test_globals.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
from unittest.mock import Mock, patch
diff --git a/opentelemetry-api/tests/trace/test_immutablespancontext.py b/opentelemetry-api/tests/trace/test_immutablespancontext.py
index 7e98470..1b0bb98 100644
--- a/opentelemetry-api/tests/trace/test_immutablespancontext.py
+++ b/opentelemetry-api/tests/trace/test_immutablespancontext.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
diff --git a/opentelemetry-api/tests/trace/test_proxy.py b/opentelemetry-api/tests/trace/test_proxy.py
index ad969e7..13f380c 100644
--- a/opentelemetry-api/tests/trace/test_proxy.py
+++ b/opentelemetry-api/tests/trace/test_proxy.py
@@ -1,19 +1,7 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=W0212,W0222,W0221
-import typing
import unittest
from opentelemetry import trace
@@ -31,9 +19,9 @@ class TestProvider(trace.NoOpTracerProvider):
def get_tracer(
self,
instrumenting_module_name: str,
- instrumenting_library_version: typing.Optional[str] = None,
- schema_url: typing.Optional[str] = None,
- attributes: typing.Optional[Attributes] = None,
+ instrumenting_library_version: str | None = None,
+ schema_url: str | None = None,
+ attributes: Attributes | None = None,
) -> trace.Tracer:
return TestTracer()
diff --git a/opentelemetry-api/tests/trace/test_span_context.py b/opentelemetry-api/tests/trace/test_span_context.py
index 55abb0f..d7e75b1 100644
--- a/opentelemetry-api/tests/trace/test_span_context.py
+++ b/opentelemetry-api/tests/trace/test_span_context.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import pickle
import unittest
diff --git a/opentelemetry-api/tests/trace/test_status.py b/opentelemetry-api/tests/trace/test_status.py
index d7ea944..a203f02 100644
--- a/opentelemetry-api/tests/trace/test_status.py
+++ b/opentelemetry-api/tests/trace/test_status.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
from logging import WARNING
diff --git a/opentelemetry-api/tests/trace/test_tracer.py b/opentelemetry-api/tests/trace/test_tracer.py
index 632232e..68bec06 100644
--- a/opentelemetry-api/tests/trace/test_tracer.py
+++ b/opentelemetry-api/tests/trace/test_tracer.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
diff --git a/opentelemetry-api/tests/trace/test_tracestate.py b/opentelemetry-api/tests/trace/test_tracestate.py
index 625b260..555cef1 100644
--- a/opentelemetry-api/tests/trace/test_tracestate.py
+++ b/opentelemetry-api/tests/trace/test_tracestate.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=no-member
import unittest
diff --git a/opentelemetry-api/tests/util/test__importlib_metadata.py b/opentelemetry-api/tests/util/test__importlib_metadata.py
index 7855153..f7e5152 100644
--- a/opentelemetry-api/tests/util/test__importlib_metadata.py
+++ b/opentelemetry-api/tests/util/test__importlib_metadata.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from unittest import TestCase
@@ -18,6 +7,7 @@ from opentelemetry.metrics import MeterProvider
from opentelemetry.util._importlib_metadata import (
EntryPoint,
EntryPoints,
+ _as_entry_points,
version,
)
from opentelemetry.util._importlib_metadata import (
@@ -111,3 +101,19 @@ class TestEntryPoints(TestCase):
self.assertEqual(len(entry_points), 0)
self.assertIsInstance(version("opentelemetry-api"), str)
+
+ def test_as_entry_points_selectable_groups_compat(self):
+ """Test that _as_entry_points correctly normalizes dict-like SelectableGroups
+ (returned by importlib.metadata.entry_points() in Python 3.10) into EntryPoints.
+
+ On Python 3.11+, entry_points() returns EntryPoints directly, which is
+ handled by the fast-path in _as_entry_points.
+ """
+ ep1 = EntryPoint(name="foo", value="bar:baz", group="gp")
+ ep2 = EntryPoint(name="foo2", value="bar2:baz2", group="gp")
+ selectable_groups = {"gp": [ep1, ep2]}
+
+ normalized = _as_entry_points(selectable_groups)
+ self.assertIsInstance(normalized, EntryPoints)
+ self.assertEqual(len(normalized), 2)
+ self.assertEqual(list(normalized), [ep1, ep2])
diff --git a/opentelemetry-api/tests/util/test__providers.py b/opentelemetry-api/tests/util/test__providers.py
index 8b2e46b..73deea3 100644
--- a/opentelemetry-api/tests/util/test__providers.py
+++ b/opentelemetry-api/tests/util/test__providers.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from importlib import reload
from os import environ
diff --git a/opentelemetry-api/tests/util/test_contextmanager.py b/opentelemetry-api/tests/util/test_contextmanager.py
index f26882c..692168d 100644
--- a/opentelemetry-api/tests/util/test_contextmanager.py
+++ b/opentelemetry-api/tests/util/test_contextmanager.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
import unittest
-from typing import Callable, Iterator
+from collections.abc import Callable, Iterator
from opentelemetry.util._decorator import _agnosticcontextmanager
diff --git a/opentelemetry-api/tests/util/test_once.py b/opentelemetry-api/tests/util/test_once.py
index 97088f9..d11ae56 100644
--- a/opentelemetry-api/tests/util/test_once.py
+++ b/opentelemetry-api/tests/util/test_once.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.test.concurrency_test import ConcurrencyTestBase, MockFunc
from opentelemetry.util._once import Once
diff --git a/opentelemetry-api/tests/util/test_re.py b/opentelemetry-api/tests/util/test_re.py
index 7c0a2a3..e8f48be 100644
--- a/opentelemetry-api/tests/util/test_re.py
+++ b/opentelemetry-api/tests/util/test_re.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
diff --git a/opentelemetry-proto-json/pyproject.toml b/opentelemetry-proto-json/pyproject.toml
index b161fc4..540567b 100644
--- a/opentelemetry-proto-json/pyproject.toml
+++ b/opentelemetry-proto-json/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenTelemetry Python Json Proto"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -18,7 +18,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
diff --git a/opentelemetry-proto-json/src/opentelemetry/proto_json/_json_codec.py b/opentelemetry-proto-json/src/opentelemetry/proto_json/_json_codec.py
index da1f40b..c4a9702 100644
--- a/opentelemetry-proto-json/src/opentelemetry/proto_json/_json_codec.py
+++ b/opentelemetry-proto-json/src/opentelemetry/proto_json/_json_codec.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
diff --git a/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/logs/v1/logs_service.py b/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/logs/v1/logs_service.py
index f668b94..c385c36 100644
--- a/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/logs/v1/logs_service.py
+++ b/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/logs/v1/logs_service.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# AUTO-GENERATED from "opentelemetry/proto/collector/logs/v1/logs_service.proto"
# DO NOT EDIT MANUALLY
@@ -20,13 +9,9 @@ from __future__ import annotations
import builtins
import dataclasses
import functools
-import sys
import typing
-if sys.version_info >= (3, 10):
- _dataclass = functools.partial(dataclasses.dataclass, slots=True)
-else:
- _dataclass = dataclasses.dataclass
+_dataclass = functools.partial(dataclasses.dataclass, slots=True)
import opentelemetry.proto_json._json_codec
import opentelemetry.proto_json.logs.v1.logs
diff --git a/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/metrics/v1/metrics_service.py b/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/metrics/v1/metrics_service.py
index 0ab6dd8..6478bc0 100644
--- a/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/metrics/v1/metrics_service.py
+++ b/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/metrics/v1/metrics_service.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# AUTO-GENERATED from "opentelemetry/proto/collector/metrics/v1/metrics_service.proto"
# DO NOT EDIT MANUALLY
@@ -20,13 +9,9 @@ from __future__ import annotations
import builtins
import dataclasses
import functools
-import sys
import typing
-if sys.version_info >= (3, 10):
- _dataclass = functools.partial(dataclasses.dataclass, slots=True)
-else:
- _dataclass = dataclasses.dataclass
+_dataclass = functools.partial(dataclasses.dataclass, slots=True)
import opentelemetry.proto_json._json_codec
import opentelemetry.proto_json.metrics.v1.metrics
diff --git a/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/profiles/v1development/profiles_service.py b/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/profiles/v1development/profiles_service.py
index 589b596..6382389 100644
--- a/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/profiles/v1development/profiles_service.py
+++ b/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/profiles/v1development/profiles_service.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# AUTO-GENERATED from "opentelemetry/proto/collector/profiles/v1development/profiles_service.proto"
# DO NOT EDIT MANUALLY
@@ -20,13 +9,9 @@ from __future__ import annotations
import builtins
import dataclasses
import functools
-import sys
import typing
-if sys.version_info >= (3, 10):
- _dataclass = functools.partial(dataclasses.dataclass, slots=True)
-else:
- _dataclass = dataclasses.dataclass
+_dataclass = functools.partial(dataclasses.dataclass, slots=True)
import opentelemetry.proto_json._json_codec
import opentelemetry.proto_json.profiles.v1development.profiles
diff --git a/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/trace/v1/trace_service.py b/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/trace/v1/trace_service.py
index 2c22246..7e93f58 100644
--- a/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/trace/v1/trace_service.py
+++ b/opentelemetry-proto-json/src/opentelemetry/proto_json/collector/trace/v1/trace_service.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# AUTO-GENERATED from "opentelemetry/proto/collector/trace/v1/trace_service.proto"
# DO NOT EDIT MANUALLY
@@ -20,13 +9,9 @@ from __future__ import annotations
import builtins
import dataclasses
import functools
-import sys
import typing
-if sys.version_info >= (3, 10):
- _dataclass = functools.partial(dataclasses.dataclass, slots=True)
-else:
- _dataclass = dataclasses.dataclass
+_dataclass = functools.partial(dataclasses.dataclass, slots=True)
import opentelemetry.proto_json._json_codec
import opentelemetry.proto_json.trace.v1.trace
diff --git a/opentelemetry-proto-json/src/opentelemetry/proto_json/common/v1/common.py b/opentelemetry-proto-json/src/opentelemetry/proto_json/common/v1/common.py
index 784eb3d..85d1e59 100644
--- a/opentelemetry-proto-json/src/opentelemetry/proto_json/common/v1/common.py
+++ b/opentelemetry-proto-json/src/opentelemetry/proto_json/common/v1/common.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# AUTO-GENERATED from "opentelemetry/proto/common/v1/common.proto"
# DO NOT EDIT MANUALLY
@@ -20,13 +9,9 @@ from __future__ import annotations
import builtins
import dataclasses
import functools
-import sys
import typing
-if sys.version_info >= (3, 10):
- _dataclass = functools.partial(dataclasses.dataclass, slots=True)
-else:
- _dataclass = dataclasses.dataclass
+_dataclass = functools.partial(dataclasses.dataclass, slots=True)
import opentelemetry.proto_json._json_codec
diff --git a/opentelemetry-proto-json/src/opentelemetry/proto_json/logs/v1/logs.py b/opentelemetry-proto-json/src/opentelemetry/proto_json/logs/v1/logs.py
index ca2e42c..3a0bdb1 100644
--- a/opentelemetry-proto-json/src/opentelemetry/proto_json/logs/v1/logs.py
+++ b/opentelemetry-proto-json/src/opentelemetry/proto_json/logs/v1/logs.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# AUTO-GENERATED from "opentelemetry/proto/logs/v1/logs.proto"
# DO NOT EDIT MANUALLY
@@ -21,13 +10,9 @@ import builtins
import dataclasses
import enum
import functools
-import sys
import typing
-if sys.version_info >= (3, 10):
- _dataclass = functools.partial(dataclasses.dataclass, slots=True)
-else:
- _dataclass = dataclasses.dataclass
+_dataclass = functools.partial(dataclasses.dataclass, slots=True)
import opentelemetry.proto_json._json_codec
import opentelemetry.proto_json.common.v1.common
diff --git a/opentelemetry-proto-json/src/opentelemetry/proto_json/metrics/v1/metrics.py b/opentelemetry-proto-json/src/opentelemetry/proto_json/metrics/v1/metrics.py
index fa98143..02ebfd6 100644
--- a/opentelemetry-proto-json/src/opentelemetry/proto_json/metrics/v1/metrics.py
+++ b/opentelemetry-proto-json/src/opentelemetry/proto_json/metrics/v1/metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# AUTO-GENERATED from "opentelemetry/proto/metrics/v1/metrics.proto"
# DO NOT EDIT MANUALLY
@@ -21,13 +10,9 @@ import builtins
import dataclasses
import enum
import functools
-import sys
import typing
-if sys.version_info >= (3, 10):
- _dataclass = functools.partial(dataclasses.dataclass, slots=True)
-else:
- _dataclass = dataclasses.dataclass
+_dataclass = functools.partial(dataclasses.dataclass, slots=True)
import opentelemetry.proto_json._json_codec
import opentelemetry.proto_json.common.v1.common
diff --git a/opentelemetry-proto-json/src/opentelemetry/proto_json/profiles/v1development/profiles.py b/opentelemetry-proto-json/src/opentelemetry/proto_json/profiles/v1development/profiles.py
index 6ad4eb5..a5411b0 100644
--- a/opentelemetry-proto-json/src/opentelemetry/proto_json/profiles/v1development/profiles.py
+++ b/opentelemetry-proto-json/src/opentelemetry/proto_json/profiles/v1development/profiles.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# AUTO-GENERATED from "opentelemetry/proto/profiles/v1development/profiles.proto"
# DO NOT EDIT MANUALLY
@@ -20,13 +9,9 @@ from __future__ import annotations
import builtins
import dataclasses
import functools
-import sys
import typing
-if sys.version_info >= (3, 10):
- _dataclass = functools.partial(dataclasses.dataclass, slots=True)
-else:
- _dataclass = dataclasses.dataclass
+_dataclass = functools.partial(dataclasses.dataclass, slots=True)
import opentelemetry.proto_json._json_codec
import opentelemetry.proto_json.common.v1.common
diff --git a/opentelemetry-proto-json/src/opentelemetry/proto_json/resource/v1/resource.py b/opentelemetry-proto-json/src/opentelemetry/proto_json/resource/v1/resource.py
index 3525242..664a42e 100644
--- a/opentelemetry-proto-json/src/opentelemetry/proto_json/resource/v1/resource.py
+++ b/opentelemetry-proto-json/src/opentelemetry/proto_json/resource/v1/resource.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# AUTO-GENERATED from "opentelemetry/proto/resource/v1/resource.proto"
# DO NOT EDIT MANUALLY
@@ -20,13 +9,9 @@ from __future__ import annotations
import builtins
import dataclasses
import functools
-import sys
import typing
-if sys.version_info >= (3, 10):
- _dataclass = functools.partial(dataclasses.dataclass, slots=True)
-else:
- _dataclass = dataclasses.dataclass
+_dataclass = functools.partial(dataclasses.dataclass, slots=True)
import opentelemetry.proto_json._json_codec
import opentelemetry.proto_json.common.v1.common
diff --git a/opentelemetry-proto-json/src/opentelemetry/proto_json/trace/v1/trace.py b/opentelemetry-proto-json/src/opentelemetry/proto_json/trace/v1/trace.py
index 96b0867..5afa5a6 100644
--- a/opentelemetry-proto-json/src/opentelemetry/proto_json/trace/v1/trace.py
+++ b/opentelemetry-proto-json/src/opentelemetry/proto_json/trace/v1/trace.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# AUTO-GENERATED from "opentelemetry/proto/trace/v1/trace.proto"
# DO NOT EDIT MANUALLY
@@ -21,13 +10,9 @@ import builtins
import dataclasses
import enum
import functools
-import sys
import typing
-if sys.version_info >= (3, 10):
- _dataclass = functools.partial(dataclasses.dataclass, slots=True)
-else:
- _dataclass = dataclasses.dataclass
+_dataclass = functools.partial(dataclasses.dataclass, slots=True)
import opentelemetry.proto_json._json_codec
import opentelemetry.proto_json.common.v1.common
diff --git a/opentelemetry-proto-json/src/opentelemetry/proto_json/version/__init__.py b/opentelemetry-proto-json/src/opentelemetry/proto_json/version/__init__.py
index 766799c..23888c2 100644
--- a/opentelemetry-proto-json/src/opentelemetry/proto_json/version/__init__.py
+++ b/opentelemetry-proto-json/src/opentelemetry/proto_json/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "0.62b0"
+__version__ = "0.63b1"
diff --git a/opentelemetry-proto-json/test-requirements.latest.txt b/opentelemetry-proto-json/test-requirements.latest.txt
index 403b955..8f7f168 100644
--- a/opentelemetry-proto-json/test-requirements.latest.txt
+++ b/opentelemetry-proto-json/test-requirements.latest.txt
@@ -1,5 +1,5 @@
# This file was autogenerated by uv via the following command:
-# uv pip compile --python 3.9 --universal -c dev-requirements.txt opentelemetry-proto-json/test-requirements.in -o opentelemetry-proto-json/test-requirements.latest.txt
+# uv pip compile --python 3.10 --universal -c dev-requirements.txt opentelemetry-proto-json/test-requirements.in -o opentelemetry-proto-json/test-requirements.latest.txt
-e opentelemetry-proto-json
# via -r opentelemetry-proto-json/test-requirements.in
colorama==0.4.6
@@ -8,7 +8,7 @@ colorama==0.4.6
# pytest
exceptiongroup==1.3.1 ; python_full_version < '3.11'
# via pytest
-iniconfig==2.1.0
+iniconfig==2.3.0
# via
# -r opentelemetry-proto-json/test-requirements.in
# pytest
@@ -17,12 +17,14 @@ packaging==26.0
# -r opentelemetry-proto-json/test-requirements.in
# pytest
pluggy==1.6.0
- # via pytest
+ # via
+ # -r opentelemetry-proto-json/test-requirements.in
+ # pytest
pytest==7.4.4
# via
# -c dev-requirements.txt
# -r opentelemetry-proto-json/test-requirements.in
-tomli==2.4.0 ; python_full_version < '3.11'
+tomli==2.4.1 ; python_full_version < '3.11'
# via pytest
typing-extensions==4.15.0 ; python_full_version < '3.11'
# via exceptiongroup
diff --git a/opentelemetry-proto-json/test-requirements.oldest.txt b/opentelemetry-proto-json/test-requirements.oldest.txt
index 730c9c0..af7be01 100644
--- a/opentelemetry-proto-json/test-requirements.oldest.txt
+++ b/opentelemetry-proto-json/test-requirements.oldest.txt
@@ -1,5 +1,5 @@
# This file was autogenerated by uv via the following command:
-# uv pip compile --python 3.9 --universal --resolution lowest -c dev-requirements.txt opentelemetry-proto-json/test-requirements.in -o opentelemetry-proto-json/test-requirements.oldest.txt
+# uv pip compile --python 3.10 --universal --resolution lowest -c dev-requirements.txt opentelemetry-proto-json/test-requirements.in -o opentelemetry-proto-json/test-requirements.oldest.txt
-e opentelemetry-proto-json
# via -r opentelemetry-proto-json/test-requirements.in
colorama==0.4.6
diff --git a/opentelemetry-proto-json/tests/test_proto.py b/opentelemetry-proto-json/tests/test_proto.py
index f99d4ab..15c4aa7 100644
--- a/opentelemetry-proto-json/tests/test_proto.py
+++ b/opentelemetry-proto-json/tests/test_proto.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
from importlib.util import find_spec
diff --git a/opentelemetry-proto/pyproject.toml b/opentelemetry-proto/pyproject.toml
index bf76152..28bce6b 100644
--- a/opentelemetry-proto/pyproject.toml
+++ b/opentelemetry-proto/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenTelemetry Python Proto"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -18,7 +18,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
diff --git a/opentelemetry-proto/src/opentelemetry/proto/version/__init__.py b/opentelemetry-proto/src/opentelemetry/proto/version/__init__.py
index 49e1254..a400b9b 100644
--- a/opentelemetry-proto/src/opentelemetry/proto/version/__init__.py
+++ b/opentelemetry-proto/src/opentelemetry/proto/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.0"
+__version__ = "1.42.1"
diff --git a/opentelemetry-proto/test-requirements.in b/opentelemetry-proto/test-requirements.in
index 897bf36..9246922 100644
--- a/opentelemetry-proto/test-requirements.in
+++ b/opentelemetry-proto/test-requirements.in
@@ -1,6 +1,6 @@
colorama>=0.4.6
iniconfig>=2.0.0
packaging>=24.0
-protobuf>=5.29.5
+protobuf>=5.29.6
pytest>=7.4.4
-e opentelemetry-proto
diff --git a/opentelemetry-proto/test-requirements.latest.txt b/opentelemetry-proto/test-requirements.latest.txt
index 8605252..f62c1b2 100644
--- a/opentelemetry-proto/test-requirements.latest.txt
+++ b/opentelemetry-proto/test-requirements.latest.txt
@@ -1,24 +1,24 @@
# This file was autogenerated by uv via the following command:
-# uv pip compile --python 3.9 --universal -c dev-requirements.txt opentelemetry-proto/test-requirements.in -o opentelemetry-proto/test-requirements.latest.txt
+# uv pip compile --python 3.10 --universal -c dev-requirements.txt opentelemetry-proto/test-requirements.in -o opentelemetry-proto/test-requirements.latest.txt
-e opentelemetry-proto
# via -r opentelemetry-proto/test-requirements.in
colorama==0.4.6
# via
# -r opentelemetry-proto/test-requirements.in
# pytest
-exceptiongroup==1.3.0 ; python_full_version < '3.11'
+exceptiongroup==1.3.1 ; python_full_version < '3.11'
# via pytest
-iniconfig==2.1.0
+iniconfig==2.3.0
# via
# -r opentelemetry-proto/test-requirements.in
# pytest
-packaging==25.0
+packaging==26.0
# via
# -r opentelemetry-proto/test-requirements.in
# pytest
pluggy==1.6.0
# via pytest
-protobuf==6.33.5
+protobuf==6.33.6
# via
# -r opentelemetry-proto/test-requirements.in
# opentelemetry-proto
@@ -26,7 +26,7 @@ pytest==7.4.4
# via
# -c dev-requirements.txt
# -r opentelemetry-proto/test-requirements.in
-tomli==2.2.1 ; python_full_version < '3.11'
+tomli==2.4.1 ; python_full_version < '3.11'
# via pytest
-typing-extensions==4.14.0 ; python_full_version < '3.11'
+typing-extensions==4.15.0 ; python_full_version < '3.11'
# via exceptiongroup
diff --git a/opentelemetry-proto/test-requirements.oldest.txt b/opentelemetry-proto/test-requirements.oldest.txt
index f6bf702..c925a09 100644
--- a/opentelemetry-proto/test-requirements.oldest.txt
+++ b/opentelemetry-proto/test-requirements.oldest.txt
@@ -1,18 +1,18 @@
# This file was autogenerated by uv via the following command:
-# uv pip compile --python 3.9 --universal --resolution lowest -c dev-requirements.txt opentelemetry-proto/test-requirements.in -o opentelemetry-proto/test-requirements.oldest.txt
+# uv pip compile --python 3.10 --universal --resolution lowest -c dev-requirements.txt opentelemetry-proto/test-requirements.in -o opentelemetry-proto/test-requirements.oldest.txt
-e opentelemetry-proto
# via -r opentelemetry-proto/test-requirements.in
colorama==0.4.6
# via
# -r opentelemetry-proto/test-requirements.in
# pytest
-exceptiongroup==1.3.0 ; python_full_version < '3.11'
+exceptiongroup==1.0.0 ; python_full_version < '3.11'
# via pytest
-iniconfig==2.1.0
+iniconfig==2.0.0
# via
# -r opentelemetry-proto/test-requirements.in
# pytest
-packaging==25.0
+packaging==24.0
# via
# -r opentelemetry-proto/test-requirements.in
# pytest
@@ -26,7 +26,5 @@ pytest==7.4.4
# via
# -c dev-requirements.txt
# -r opentelemetry-proto/test-requirements.in
-tomli==2.2.1 ; python_full_version < '3.11'
+tomli==1.0.0 ; python_full_version < '3.11'
# via pytest
-typing-extensions==4.14.0 ; python_full_version < '3.11'
- # via exceptiongroup
diff --git a/opentelemetry-proto/tests/test_proto.py b/opentelemetry-proto/tests/test_proto.py
index 5c04116..2120c0a 100644
--- a/opentelemetry-proto/tests/test_proto.py
+++ b/opentelemetry-proto/tests/test_proto.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
from importlib.util import find_spec
diff --git a/opentelemetry-sdk/benchmarks/logs/test_benchmark_logging_handler.py b/opentelemetry-sdk/benchmarks/logs/test_benchmark_logging_handler.py
index d1b9cf5..ca32619 100644
--- a/opentelemetry-sdk/benchmarks/logs/test_benchmark_logging_handler.py
+++ b/opentelemetry-sdk/benchmarks/logs/test_benchmark_logging_handler.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import logging
diff --git a/opentelemetry-sdk/benchmarks/logs/test_benchmark_logs.py b/opentelemetry-sdk/benchmarks/logs/test_benchmark_logs.py
index f1578af..73952bb 100644
--- a/opentelemetry-sdk/benchmarks/logs/test_benchmark_logs.py
+++ b/opentelemetry-sdk/benchmarks/logs/test_benchmark_logs.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import pytest
diff --git a/opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics.py b/opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics.py
index 007481a..34317ce 100644
--- a/opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics.py
+++ b/opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import pytest
from opentelemetry.sdk.metrics import Counter, MeterProvider
diff --git a/opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics_histogram.py b/opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics_histogram.py
index 1c7cdf2..440a37f 100644
--- a/opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics_histogram.py
+++ b/opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics_histogram.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=invalid-name
import random
diff --git a/opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics_histogram_steady.py b/opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics_histogram_steady.py
index 163edcf..70bea3e 100644
--- a/opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics_histogram_steady.py
+++ b/opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics_histogram_steady.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=invalid-name
import itertools
diff --git a/opentelemetry-sdk/benchmarks/test_baggage.py b/opentelemetry-sdk/benchmarks/test_baggage.py
index 4ec331a..7d67fab 100644
--- a/opentelemetry-sdk/benchmarks/test_baggage.py
+++ b/opentelemetry-sdk/benchmarks/test_baggage.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
# pylint: disable=redefined-outer-name, invalid-name
import pytest
diff --git a/opentelemetry-sdk/benchmarks/trace/test_benchmark_trace.py b/opentelemetry-sdk/benchmarks/trace/test_benchmark_trace.py
index 51a3214..3ab23e2 100644
--- a/opentelemetry-sdk/benchmarks/trace/test_benchmark_trace.py
+++ b/opentelemetry-sdk/benchmarks/trace/test_benchmark_trace.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from functools import lru_cache
@@ -80,7 +69,7 @@ def test_simple_start_span_with_tracer_configurator_rules(
for i in range(num_tracer_configurator_rules)
],
default_config=_TracerConfig(is_enabled=True),
- )(tracer_scope=tracer_scope)
+ )(tracer_scope)
tracer_provider._set_tracer_configurator(
tracer_configurator=tracer_configurator
diff --git a/opentelemetry-sdk/codegen/README.md b/opentelemetry-sdk/codegen/README.md
new file mode 100644
index 0000000..d28e52b
--- /dev/null
+++ b/opentelemetry-sdk/codegen/README.md
@@ -0,0 +1,24 @@
+# Code Generation Templates
+
+Custom [datamodel-code-generator](https://github.com/koxudaxi/datamodel-code-generator) templates used when generating `models.py` from the OpenTelemetry configuration JSON schema.
+
+## `dataclass.jinja2`
+
+Extends the default dataclass template to support `additionalProperties` from the JSON Schema. Schema types that allow additional properties (e.g. `Sampler`, `SpanExporter`, `TextMapPropagator`) get:
+
+- `@_additional_properties` decorator — captures unknown constructor kwargs
+- `additional_properties: ClassVar[dict[str, Any]]` annotation — satisfies type checkers without creating a dataclass field
+
+This enables plugin/custom component names to flow through typed dataclasses without a post-processing step.
+
+## Usage
+
+Templates are applied automatically when regenerating models:
+
+```sh
+tox -e generate-config-from-jsonschema
+```
+
+The template directory is configured in `pyproject.toml` under `[tool.datamodel-codegen]`.
+
+See `opentelemetry-sdk/src/opentelemetry/sdk/_configuration/README.md` for the full schema update workflow.
diff --git a/opentelemetry-sdk/codegen/dataclass.jinja2 b/opentelemetry-sdk/codegen/dataclass.jinja2
new file mode 100644
index 0000000..f584357
--- /dev/null
+++ b/opentelemetry-sdk/codegen/dataclass.jinja2
@@ -0,0 +1,73 @@
+{# Custom dataclass template for OpenTelemetry configuration models.
+ Extends the default datamodel-codegen dataclass template to support
+ JSON Schema additionalProperties. When a schema type allows additional
+ properties (e.g. Sampler, SpanExporter), this template adds:
+ - @_additional_properties decorator (captures unknown kwargs)
+ - additional_properties: ClassVar[dict[str, Any]] annotation (for type checkers)
+
+ The template checks two context variables set by datamodel-codegen:
+ - additionalProperties: set when the schema value is a boolean (true/false)
+ - additionalPropertiesType: set when the schema value is a type object
+ (e.g. {"type": ["object", "null"]}), which is how the OTel config
+ schema defines it for plugin-capable types.
+
+ See TestGeneratedModelsHaveAdditionalProperties in test_common.py for
+ regression tests that guard against changes in these context variables.
+-#}
+{% for decorator in decorators -%}
+{{ decorator }}
+{% endfor -%}
+{%- set args = [] %}
+{%- for k, v in (dataclass_arguments or {}).items() %}
+ {%- if v is not none and v is not false %}
+ {%- set _ = args.append(k ~ '=' ~ (v|pprint)) %}
+ {%- endif %}
+{%- endfor %}
+{%- set has_additional = (additionalProperties is defined and additionalProperties != false) or (additionalPropertiesType is defined) %}
+{%- if has_additional %}
+@_additional_properties
+{%- endif %}
+{%- if args %}
+@dataclass({{ args | join(', ') }})
+{%- else %}
+@dataclass
+{%- endif %}
+{%- if base_class %}
+class {{ class_name }}({{ base_class }}):
+{%- else %}
+class {{ class_name }}:
+{%- endif %}
+{%- if description %}
+ """
+ {{ description | escape_docstring | indent(4) }}
+ """
+{%- endif %}
+{%- if not fields and not description and not has_additional %}
+ pass
+{%- endif %}
+{%- for field in fields -%}
+ {%- if field.field %}
+ {{ field.name }}: {{ field.type_hint }} = {{ field.field }}
+ {%- else %}
+ {{ field.name }}: {{ field.type_hint }}
+ {%- if not (field.required or (field.represented_default == 'None' and field.strip_default_none))
+ %} = {{ field.represented_default }}
+ {%- endif -%}
+ {%- endif %}
+ {%- if field.docstring %}
+ """
+ {{ field.docstring | escape_docstring | indent(4) }}
+ """
+{%- if field.use_inline_field_description and not loop.last %}
+
+{% endif %}
+ {%- elif field.inline_field_docstring %}
+ {{ field.inline_field_docstring }}
+{%- if not loop.last %}
+
+{% endif %}
+ {%- endif %}
+{%- endfor -%}
+{%- if has_additional %}
+ additional_properties: ClassVar[dict[str, Any]]
+{%- endif -%}
diff --git a/opentelemetry-sdk/pyproject.toml b/opentelemetry-sdk/pyproject.toml
index 6e441ab..f5a4816 100644
--- a/opentelemetry-sdk/pyproject.toml
+++ b/opentelemetry-sdk/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenTelemetry Python SDK"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -18,7 +18,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -27,8 +26,8 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
- "opentelemetry-api == 1.41.0",
- "opentelemetry-semantic-conventions == 0.62b0",
+ "opentelemetry-api == 1.42.1",
+ "opentelemetry-semantic-conventions == 0.63b1",
"typing-extensions >= 4.5.0",
]
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/README.md b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/README.md
index 5911d5f..e7cf918 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/README.md
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/README.md
@@ -5,7 +5,7 @@ This package implements [OpenTelemetry file-based configuration](https://opentel
## Files
- `schema.json` — vendored copy of the [OpenTelemetry configuration JSON schema](https://github.com/open-telemetry/opentelemetry-configuration)
-- `models.py` — Python dataclasses generated from `schema.json` by [datamodel-code-generator](https://github.com/koxudaxi/datamodel-code-generator)
+- `models.py` — Python dataclasses generated from `schema.json` by [datamodel-code-generator](https://github.com/koxudaxi/datamodel-code-generator), using a custom template from `opentelemetry-sdk/codegen/` (see that directory's README for details)
## Updating the schema
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py
index 4c6b533..3b6e2ba 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
#
"""
@@ -24,10 +13,9 @@ import logging.config
import os
import warnings
from abc import ABC, abstractmethod
+from collections.abc import Callable, Mapping, Sequence
from os import environ
-from typing import Any, Callable, Mapping, Protocol, Sequence, Type, Union
-
-from typing_extensions import Literal
+from typing import Any, Literal, Protocol
from opentelemetry._logs import set_logger_provider
from opentelemetry.environment_variables import (
@@ -108,12 +96,10 @@ _OTEL_SAMPLER_ENTRY_POINT_GROUP = "opentelemetry_traces_sampler"
_logger = logging.getLogger(__name__)
ExporterArgsMap = Mapping[
- Union[
- Type[SpanExporter],
- Type[MetricExporter],
- Type[MetricReader],
- Type[LogRecordExporter],
- ],
+ type[SpanExporter]
+ | type[MetricExporter]
+ | type[MetricReader]
+ | type[LogRecordExporter],
Mapping[str, Any],
]
@@ -132,7 +118,7 @@ class _ConfigurationExporterLogRecordProcessorT(Protocol):
def _import_config_components(
selected_components: Sequence[str], entry_point_name: str
-) -> list[tuple[str, Type]]:
+) -> list[tuple[str, type]]:
component_implementations = []
for selected_component in selected_components:
@@ -242,7 +228,7 @@ def _get_exporter_names(
def _init_tracing(
- exporters: dict[str, Type[SpanExporter]],
+ exporters: dict[str, type[SpanExporter]],
id_generator: IdGenerator | None = None,
sampler: Sampler | None = None,
resource: Resource | None = None,
@@ -274,9 +260,7 @@ def _init_tracing(
def _init_metrics(
- exporters_or_readers: dict[
- str, Union[Type[MetricExporter], Type[MetricReader]]
- ],
+ exporters_or_readers: dict[str, type[MetricExporter] | type[MetricReader]],
resource: Resource | None = None,
exporter_args_map: ExporterArgsMap | None = None,
meter_configurator: _MeterConfiguratorT | None = None,
@@ -305,7 +289,7 @@ def _init_metrics(
# pylint: disable-next=too-many-locals
def _init_logging(
- exporters: dict[str, Type[LogRecordExporter]],
+ exporters: dict[str, type[LogRecordExporter]],
resource: Resource | None = None,
setup_logging_handler: bool = True,
exporter_args_map: ExporterArgsMap | None = None,
@@ -455,9 +439,9 @@ def _import_exporters(
metric_exporter_names: Sequence[str],
log_exporter_names: Sequence[str],
) -> tuple[
- dict[str, Type[SpanExporter]],
- dict[str, Union[Type[MetricExporter], Type[MetricReader]]],
- dict[str, Type[LogRecordExporter]],
+ dict[str, type[SpanExporter]],
+ dict[str, type[MetricExporter] | type[MetricReader]],
+ dict[str, type[LogRecordExporter]],
]:
trace_exporters = {}
metric_exporters = {}
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_common.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_common.py
index 152be1e..5d709fb 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_common.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_common.py
@@ -1,29 +1,81 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
+import dataclasses
+import inspect
import logging
-from typing import Optional
+
+from opentelemetry.sdk._configuration._exceptions import ConfigurationError
+from opentelemetry.util._importlib_metadata import entry_points
_logger = logging.getLogger(__name__)
+def _additional_properties(cls):
+ """Decorator for dataclasses whose JSON Schema sets additionalProperties.
+
+ Wraps the dataclass-generated ``__init__`` so that unknown keyword
+ arguments are captured into an ``additional_properties`` instance
+ attribute instead of raising ``TypeError``. This lets plugin/custom
+ component names flow through the config pipeline without modifying
+ the codegen output for known fields.
+
+ Applied automatically by the custom template in ``opentelemetry-sdk/codegen/``
+ when ``additionalPropertiesType`` is present in the template context
+ (set by ``datamodel-codegen`` for schema types with ``additionalProperties``).
+ """
+ original_init = cls.__init__
+ original_sig = inspect.signature(original_init)
+ known_fields = frozenset(f.name for f in dataclasses.fields(cls))
+
+ def _init(self, **kwargs):
+ known = {k: v for k, v in kwargs.items() if k in known_fields}
+ extra = {k: v for k, v in kwargs.items() if k not in known_fields}
+ original_init(self, **known)
+ self.additional_properties = extra
+
+ # Preserve the original parameter list for IDE autocompletion and
+ # inspect.signature(), adding **kwargs to signal extras are accepted.
+ # setattr used because pyright rejects direct __signature__ assignment.
+ params = list(original_sig.parameters.values())
+ params.append(inspect.Parameter("kwargs", inspect.Parameter.VAR_KEYWORD))
+ setattr(_init, "__signature__", original_sig.replace(parameters=params)) # noqa: B010
+
+ cls.__init__ = _init
+ return cls
+
+
+def load_entry_point(group: str, name: str) -> type:
+ """Load a plugin class from an entry point group by name.
+
+ Returns the loaded class — callers are responsible for instantiation
+ with whatever arguments their config requires.
+
+ Raises:
+ ConfigurationError: If the entry point is not found or fails to load.
+ """
+ try:
+ ep = next(iter(entry_points(group=group, name=name)), None)
+ if ep is None:
+ raise ConfigurationError(
+ f"Plugin '{name}' not found in group '{group}'. "
+ "Make sure the package providing this plugin is installed."
+ )
+ return ep.load()
+ except ConfigurationError:
+ raise
+ except Exception as exc:
+ raise ConfigurationError(
+ f"Failed to load plugin '{name}' from group '{group}': {exc}"
+ ) from exc
+
+
def _parse_headers(
- headers: Optional[list],
- headers_list: Optional[str],
-) -> Optional[dict[str, str]]:
+ headers: list | None,
+ headers_list: str | None,
+) -> dict[str, str] | None:
"""Merge headers struct and headers_list into a dict.
Returns None if neither is set, letting the exporter read env vars.
@@ -45,5 +97,38 @@ def _parse_headers(
)
if headers:
for pair in headers:
- result[pair.name] = pair.value or ""
+ if isinstance(pair, dict):
+ result[pair["name"]] = pair.get("value") or ""
+ else:
+ result[pair.name] = pair.value or ""
return result
+
+
+def _map_compression(
+ value: str | None,
+ compression_enum: type,
+ *,
+ allow_deflate: bool = False,
+) -> object | None:
+ """Map a compression string to the given Compression enum value."""
+ if value is None:
+ return None
+
+ value_lower = value.lower()
+ supports_deflate = hasattr(compression_enum, "Deflate")
+
+ if value_lower == "none":
+ return None
+ if value_lower == "gzip":
+ return compression_enum.Gzip # type: ignore[attr-defined]
+ if value_lower == "deflate" and allow_deflate and supports_deflate:
+ return compression_enum.Deflate # type: ignore[attr-defined]
+
+ supported_values = ["'gzip'", "'none'"]
+ if allow_deflate and supports_deflate:
+ supported_values.insert(1, "'deflate'")
+
+ raise ConfigurationError(
+ f"Unsupported compression value '{value}'. Supported values: "
+ f"{', '.join(supported_values)}."
+ )
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_exceptions.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_exceptions.py
index 9b90dbd..d85b3f8 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_exceptions.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_exceptions.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
class ConfigurationError(Exception):
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_logger_provider.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_logger_provider.py
new file mode 100644
index 0000000..d3ed82c
--- /dev/null
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_logger_provider.py
@@ -0,0 +1,266 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+from __future__ import annotations
+
+import logging
+
+from opentelemetry._logs import set_logger_provider
+from opentelemetry.sdk._configuration._common import (
+ _map_compression,
+ _parse_headers,
+ load_entry_point,
+)
+from opentelemetry.sdk._configuration._exceptions import ConfigurationError
+from opentelemetry.sdk._configuration.models import (
+ BatchLogRecordProcessor as BatchLogRecordProcessorConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ LoggerProvider as LoggerProviderConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ LogRecordExporter as LogRecordExporterConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ LogRecordProcessor as LogRecordProcessorConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ OtlpGrpcExporter as OtlpGrpcExporterConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ OtlpHttpExporter as OtlpHttpExporterConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ SimpleLogRecordProcessor as SimpleLogRecordProcessorConfig,
+)
+from opentelemetry.sdk._logs import LoggerProvider
+from opentelemetry.sdk._logs._internal.export import (
+ BatchLogRecordProcessor,
+ ConsoleLogRecordExporter,
+ LogRecordExporter,
+ SimpleLogRecordProcessor,
+)
+from opentelemetry.sdk.resources import Resource
+
+_logger = logging.getLogger(__name__)
+
+# BatchLogRecordProcessor defaults per OTel spec (milliseconds).
+_DEFAULT_SCHEDULE_DELAY_MILLIS = 1000
+_DEFAULT_EXPORT_TIMEOUT_MILLIS = 30000
+_DEFAULT_MAX_QUEUE_SIZE = 2048
+_DEFAULT_MAX_EXPORT_BATCH_SIZE = 512
+
+
+def _create_console_log_exporter() -> ConsoleLogRecordExporter:
+ """Create a ConsoleLogRecordExporter."""
+ return ConsoleLogRecordExporter()
+
+
+def _create_otlp_http_log_exporter(
+ config: OtlpHttpExporterConfig,
+) -> LogRecordExporter:
+ """Create an OTLP HTTP log exporter from config."""
+ try:
+ # pylint: disable=import-outside-toplevel,no-name-in-module
+ from opentelemetry.exporter.otlp.proto.http import ( # type: ignore[import-untyped] # noqa: PLC0415
+ Compression,
+ )
+ from opentelemetry.exporter.otlp.proto.http._log_exporter import ( # type: ignore[import-untyped] # noqa: PLC0415
+ OTLPLogExporter,
+ )
+ except ImportError as exc:
+ raise ConfigurationError(
+ "otlp_http log exporter requires 'opentelemetry-exporter-otlp-proto-http'. "
+ "Install it with: pip install opentelemetry-exporter-otlp-proto-http"
+ ) from exc
+
+ compression = _map_compression(
+ config.compression, Compression, allow_deflate=True
+ )
+ headers = _parse_headers(config.headers, config.headers_list)
+ timeout = (config.timeout / 1000.0) if config.timeout is not None else None
+
+ return OTLPLogExporter( # type: ignore[return-value]
+ endpoint=config.endpoint,
+ headers=headers,
+ timeout=timeout,
+ compression=compression, # type: ignore[arg-type]
+ )
+
+
+def _create_otlp_grpc_log_exporter(
+ config: OtlpGrpcExporterConfig,
+) -> LogRecordExporter:
+ """Create an OTLP gRPC log exporter from config."""
+ try:
+ # pylint: disable=import-outside-toplevel,no-name-in-module
+ import grpc # type: ignore[import-untyped] # noqa: PLC0415
+
+ from opentelemetry.exporter.otlp.proto.grpc._log_exporter import ( # type: ignore[import-untyped] # noqa: PLC0415
+ OTLPLogExporter,
+ )
+ except ImportError as exc:
+ raise ConfigurationError(
+ "otlp_grpc log exporter requires 'opentelemetry-exporter-otlp-proto-grpc'. "
+ "Install it with: pip install opentelemetry-exporter-otlp-proto-grpc"
+ ) from exc
+
+ compression = _map_compression(config.compression, grpc.Compression)
+ headers = _parse_headers(config.headers, config.headers_list)
+ timeout = (config.timeout / 1000.0) if config.timeout is not None else None
+
+ return OTLPLogExporter( # type: ignore[return-value]
+ endpoint=config.endpoint,
+ headers=headers,
+ timeout=timeout,
+ compression=compression, # type: ignore[arg-type]
+ )
+
+
+_LOG_EXPORTER_REGISTRY: dict = {
+ "otlp_http": _create_otlp_http_log_exporter,
+ "otlp_grpc": _create_otlp_grpc_log_exporter,
+ "console": lambda _: ConsoleLogRecordExporter(),
+}
+
+
+def _create_log_record_exporter(
+ config: LogRecordExporterConfig,
+) -> LogRecordExporter:
+ """Create a log record exporter from config.
+
+ Known exporter types are checked via typed fields on the LogRecordExporter
+ dataclass. Unknown exporter names captured in additional_properties
+ by the @_additional_properties decorator are loaded via the
+ ``opentelemetry_logs_exporter`` entry point group.
+ """
+ if config.otlp_file_development is not None:
+ raise ConfigurationError(
+ "otlp_file_development log exporter is experimental "
+ "and not yet supported."
+ )
+ for name, factory in _LOG_EXPORTER_REGISTRY.items():
+ value = getattr(config, name, None)
+ if value is not None:
+ return factory(value)
+ if config.additional_properties:
+ name, plugin_config = next(iter(config.additional_properties.items()))
+ return load_entry_point("opentelemetry_logs_exporter", name)(
+ **(plugin_config or {})
+ )
+ raise ConfigurationError(
+ "No exporter type specified in log record exporter config. "
+ "Supported types: console, otlp_http, otlp_grpc."
+ )
+
+
+def _create_batch_log_record_processor(
+ config: BatchLogRecordProcessorConfig,
+) -> BatchLogRecordProcessor:
+ """Create a BatchLogRecordProcessor from config.
+
+ Passes explicit defaults to suppress OTEL_BLRP_* env var reading.
+ """
+ exporter = _create_log_record_exporter(config.exporter)
+ schedule_delay = (
+ config.schedule_delay
+ if config.schedule_delay is not None
+ else _DEFAULT_SCHEDULE_DELAY_MILLIS
+ )
+ export_timeout = (
+ config.export_timeout
+ if config.export_timeout is not None
+ else _DEFAULT_EXPORT_TIMEOUT_MILLIS
+ )
+ max_queue_size = (
+ config.max_queue_size
+ if config.max_queue_size is not None
+ else _DEFAULT_MAX_QUEUE_SIZE
+ )
+ max_export_batch_size = (
+ config.max_export_batch_size
+ if config.max_export_batch_size is not None
+ else _DEFAULT_MAX_EXPORT_BATCH_SIZE
+ )
+ return BatchLogRecordProcessor(
+ exporter=exporter,
+ schedule_delay_millis=float(schedule_delay),
+ export_timeout_millis=float(export_timeout),
+ max_queue_size=max_queue_size,
+ max_export_batch_size=max_export_batch_size,
+ )
+
+
+def _create_simple_log_record_processor(
+ config: SimpleLogRecordProcessorConfig,
+) -> SimpleLogRecordProcessor:
+ """Create a SimpleLogRecordProcessor from config."""
+ exporter = _create_log_record_exporter(config.exporter)
+ return SimpleLogRecordProcessor(exporter)
+
+
+def _create_log_record_processor(
+ config: LogRecordProcessorConfig,
+) -> BatchLogRecordProcessor | SimpleLogRecordProcessor:
+ """Create a log record processor from config."""
+ if config.batch is not None:
+ return _create_batch_log_record_processor(config.batch)
+ if config.simple is not None:
+ return _create_simple_log_record_processor(config.simple)
+ raise ConfigurationError(
+ "No processor type specified in log record processor config. "
+ "Supported types: batch, simple."
+ )
+
+
+def create_logger_provider(
+ config: LoggerProviderConfig | None,
+ resource: Resource | None = None,
+) -> LoggerProvider:
+ """Create an SDK LoggerProvider from declarative config.
+
+ Does NOT read OTEL_BLRP_* or other env vars for values explicitly
+ controlled by the config. Absent config values use OTel spec defaults.
+
+ Args:
+ config: LoggerProvider config from the parsed config file, or None.
+ resource: Resource to attach to the provider.
+
+ Returns:
+ A configured LoggerProvider.
+ """
+ provider = LoggerProvider(resource=resource)
+
+ if config is None:
+ return provider
+
+ if config.limits is not None:
+ _logger.warning(
+ "log_record_limits are specified in config but are not supported "
+ "by the Python SDK LoggerProvider constructor; limits will be ignored."
+ )
+
+ for processor_config in config.processors:
+ provider.add_log_record_processor(
+ _create_log_record_processor(processor_config)
+ )
+
+ return provider
+
+
+def configure_logger_provider(
+ config: LoggerProviderConfig | None,
+ resource: Resource | None = None,
+) -> None:
+ """Configure the global LoggerProvider from declarative config.
+
+ When config is None (logger_provider section absent from config file),
+ the global is not set — matching Java/JS SDK behavior.
+
+ Args:
+ config: LoggerProvider config from the parsed config file, or None.
+ resource: Resource to attach to the provider.
+ """
+ if config is None:
+ return
+ set_logger_provider(create_logger_provider(config, resource))
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_meter_provider.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_meter_provider.py
index 2573511..5cb42eb 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_meter_provider.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_meter_provider.py
@@ -1,24 +1,16 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
import logging
-from typing import Optional, Set, Type
from opentelemetry import metrics
-from opentelemetry.sdk._configuration._common import _parse_headers
+from opentelemetry.sdk._configuration._common import (
+ _map_compression,
+ _parse_headers,
+ load_entry_point,
+)
from opentelemetry.sdk._configuration._exceptions import ConfigurationError
from opentelemetry.sdk._configuration.models import (
Aggregation as AggregationConfig,
@@ -95,7 +87,7 @@ _DEFAULT_EXPORT_INTERVAL_MILLIS = 60000
_DEFAULT_EXPORT_TIMEOUT_MILLIS = 30000
# Instrument type → SDK instrument class mapping (for View selectors).
-_INSTRUMENT_TYPE_MAP: dict[InstrumentType, Type] = {
+_INSTRUMENT_TYPE_MAP: dict[InstrumentType, type] = {
InstrumentType.counter: Counter,
InstrumentType.up_down_counter: UpDownCounter,
InstrumentType.histogram: Histogram,
@@ -107,7 +99,7 @@ _INSTRUMENT_TYPE_MAP: dict[InstrumentType, Type] = {
def _map_temporality(
- pref: Optional[ExporterTemporalityPreference],
+ pref: ExporterTemporalityPreference | None,
) -> dict[type, AggregationTemporality]:
"""Map a temporality preference to an explicit preferred_temporality dict.
@@ -148,7 +140,7 @@ def _map_temporality(
def _map_histogram_aggregation(
- pref: Optional[ExporterDefaultHistogramAggregation],
+ pref: ExporterDefaultHistogramAggregation | None,
) -> dict[type, Aggregation]:
"""Map a histogram aggregation preference to an explicit preferred_aggregation dict.
@@ -223,7 +215,7 @@ def _create_view(config: ViewConfig) -> View:
f"Unknown instrument type: {selector.instrument_type!r}"
)
- attribute_keys: Optional[Set[str]] = None
+ attribute_keys: set[str] | None = None
if stream.attribute_keys is not None:
if stream.attribute_keys.excluded:
_logger.warning(
@@ -265,19 +257,6 @@ def _create_console_metric_exporter(
)
-def _map_compression_metric(
- value: Optional[str], compression_enum: type
-) -> Optional[object]:
- """Map a compression string to the given Compression enum value."""
- if value is None or value.lower() == "none":
- return None
- if value.lower() == "gzip":
- return compression_enum.Gzip # type: ignore[attr-defined]
- raise ConfigurationError(
- f"Unsupported compression value '{value}'. Supported values: 'gzip', 'none'."
- )
-
-
def _create_otlp_http_metric_exporter(
config: OtlpHttpMetricExporterConfig,
) -> MetricExporter:
@@ -296,7 +275,9 @@ def _create_otlp_http_metric_exporter(
"Install it with: pip install opentelemetry-exporter-otlp-proto-http"
) from exc
- compression = _map_compression_metric(config.compression, Compression)
+ compression = _map_compression(
+ config.compression, Compression, allow_deflate=True
+ )
headers = _parse_headers(config.headers, config.headers_list)
timeout = (config.timeout / 1000.0) if config.timeout is not None else None
preferred_temporality = _map_temporality(config.temporality_preference)
@@ -331,7 +312,7 @@ def _create_otlp_grpc_metric_exporter(
"Install it with: pip install opentelemetry-exporter-otlp-proto-grpc"
) from exc
- compression = _map_compression_metric(config.compression, grpc.Compression)
+ compression = _map_compression(config.compression, grpc.Compression)
headers = _parse_headers(config.headers, config.headers_list)
timeout = (config.timeout / 1000.0) if config.timeout is not None else None
preferred_temporality = _map_temporality(config.temporality_preference)
@@ -349,19 +330,36 @@ def _create_otlp_grpc_metric_exporter(
)
+_METRIC_EXPORTER_REGISTRY: dict = {
+ "otlp_http": _create_otlp_http_metric_exporter,
+ "otlp_grpc": _create_otlp_grpc_metric_exporter,
+ "console": _create_console_metric_exporter,
+}
+
+
def _create_push_metric_exporter(
config: PushMetricExporterConfig,
) -> MetricExporter:
- """Create a push metric exporter from config."""
- if config.console is not None:
- return _create_console_metric_exporter(config.console)
- if config.otlp_http is not None:
- return _create_otlp_http_metric_exporter(config.otlp_http)
- if config.otlp_grpc is not None:
- return _create_otlp_grpc_metric_exporter(config.otlp_grpc)
+ """Create a push metric exporter from config.
+
+ Known exporter types are checked via typed fields on the PushMetricExporter
+ dataclass. Unknown exporter names captured in additional_properties
+ by the @_additional_properties decorator are loaded via the
+ ``opentelemetry_metrics_exporter`` entry point group.
+ """
if config.otlp_file_development is not None:
raise ConfigurationError(
- "otlp_file_development metric exporter is experimental and not yet supported."
+ "otlp_file_development metric exporter is experimental "
+ "and not yet supported."
+ )
+ for name, factory in _METRIC_EXPORTER_REGISTRY.items():
+ value = getattr(config, name, None)
+ if value is not None:
+ return factory(value)
+ if config.additional_properties:
+ name, plugin_config = next(iter(config.additional_properties.items()))
+ return load_entry_point("opentelemetry_metrics_exporter", name)(
+ **(plugin_config or {})
)
raise ConfigurationError(
"No exporter type specified in push metric exporter config. "
@@ -426,8 +424,8 @@ def _create_exemplar_filter(
def create_meter_provider(
- config: Optional[MeterProviderConfig],
- resource: Optional[Resource] = None,
+ config: MeterProviderConfig | None,
+ resource: Resource | None = None,
) -> MeterProvider:
"""Create an SDK MeterProvider from declarative config.
@@ -467,8 +465,8 @@ def create_meter_provider(
def configure_meter_provider(
- config: Optional[MeterProviderConfig],
- resource: Optional[Resource] = None,
+ config: MeterProviderConfig | None,
+ resource: Resource | None = None,
) -> None:
"""Configure the global MeterProvider from declarative config.
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_propagator.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_propagator.py
index 3c6372b..8008cf7 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_propagator.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_propagator.py
@@ -1,26 +1,13 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
-from typing import Optional
-
from opentelemetry.baggage.propagation import W3CBaggagePropagator
from opentelemetry.propagate import set_global_textmap
from opentelemetry.propagators.composite import CompositePropagator
from opentelemetry.propagators.textmap import TextMapPropagator
-from opentelemetry.sdk._configuration._exceptions import ConfigurationError
+from opentelemetry.sdk._configuration._common import load_entry_point
from opentelemetry.sdk._configuration.models import (
Propagator as PropagatorConfig,
)
@@ -30,48 +17,47 @@ from opentelemetry.sdk._configuration.models import (
from opentelemetry.trace.propagation.tracecontext import (
TraceContextTextMapPropagator,
)
-from opentelemetry.util._importlib_metadata import entry_points
-
-def _load_entry_point_propagator(name: str) -> TextMapPropagator:
- """Load a propagator by name from the opentelemetry_propagator entry point group."""
- try:
- ep = next(
- iter(entry_points(group="opentelemetry_propagator", name=name)),
- None,
- )
- if not ep:
- raise ConfigurationError(
- f"Propagator '{name}' not found. "
- "It may not be installed or may be misspelled."
- )
- return ep.load()()
- except ConfigurationError:
- raise
- except Exception as exc:
- raise ConfigurationError(
- f"Failed to load propagator '{name}': {exc}"
- ) from exc
+# Propagators bundled with the SDK — no entry point lookup needed.
+_PROPAGATOR_REGISTRY: dict[str, type[TextMapPropagator]] = {
+ "tracecontext": TraceContextTextMapPropagator,
+ "baggage": W3CBaggagePropagator,
+}
def _propagators_from_textmap_config(
config: TextMapPropagatorConfig,
) -> list[TextMapPropagator]:
- """Resolve a single TextMapPropagator config entry to a list of propagators."""
+ """Resolve a TextMapPropagator config to a list of propagators.
+
+ Known names (tracecontext, baggage) are bootstrapped directly via
+ _PROPAGATOR_REGISTRY. Known schema fields not in the registry (b3, b3multi)
+ and unknown plugin names from additional_properties are loaded via the
+ ``opentelemetry_propagator`` entry point group.
+ """
result: list[TextMapPropagator] = []
- if config.tracecontext is not None:
- result.append(TraceContextTextMapPropagator())
- if config.baggage is not None:
- result.append(W3CBaggagePropagator())
- if config.b3 is not None:
- result.append(_load_entry_point_propagator("b3"))
- if config.b3multi is not None:
- result.append(_load_entry_point_propagator("b3multi"))
+ for name, cls in _PROPAGATOR_REGISTRY.items():
+ if getattr(config, name, None) is not None:
+ result.append(cls())
+
+ # Known schema fields not in registry (b3, b3multi) — loaded via entry point
+ for name in ("b3", "b3multi"):
+ if getattr(config, name, None) is not None:
+ result.append(load_entry_point("opentelemetry_propagator", name)())
+
+ # Plugin propagators from additional_properties
+ for name, plugin_config in config.additional_properties.items():
+ result.append(
+ load_entry_point("opentelemetry_propagator", name)(
+ **(plugin_config or {})
+ )
+ )
+
return result
def create_propagator(
- config: Optional[PropagatorConfig],
+ config: PropagatorConfig | None,
) -> CompositePropagator:
"""Create a CompositePropagator from declarative config.
@@ -102,13 +88,13 @@ def create_propagator(
name = name.strip()
if not name or name.lower() == "none":
continue
- propagator = _load_entry_point_propagator(name)
+ propagator = load_entry_point("opentelemetry_propagator", name)()
propagators.setdefault(type(propagator), propagator)
return CompositePropagator(list(propagators.values()))
-def configure_propagator(config: Optional[PropagatorConfig]) -> None:
+def configure_propagator(config: PropagatorConfig | None) -> None:
"""Configure the global text map propagator from declarative config.
Always calls set_global_textmap to override any defaults (including the
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_resource.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_resource.py
index ec68b15..15d9e94 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_resource.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_resource.py
@@ -1,24 +1,18 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
+import dataclasses
import fnmatch
import logging
-from typing import Callable, Optional
+import os
+import uuid
+from collections.abc import Callable
+from typing import Any
from urllib import parse
+from opentelemetry.sdk._configuration._common import load_entry_point
from opentelemetry.sdk._configuration.models import (
AttributeNameValue,
AttributeType,
@@ -28,12 +22,14 @@ from opentelemetry.sdk._configuration.models import (
from opentelemetry.sdk._configuration.models import Resource as ResourceConfig
from opentelemetry.sdk.resources import (
_DEFAULT_RESOURCE,
+ OTEL_SERVICE_NAME,
+ SERVICE_INSTANCE_ID,
SERVICE_NAME,
ProcessResourceDetector,
Resource,
_HostResourceDetector,
)
-from opentelemetry.util._importlib_metadata import entry_points
+from opentelemetry.util.types import AttributeValue
_logger = logging.getLogger(__name__)
@@ -89,7 +85,7 @@ def _parse_attributes_list(attributes_list: str) -> dict[str, str]:
return result
-def create_resource(config: Optional[ResourceConfig]) -> Resource:
+def create_resource(config: ResourceConfig | None) -> Resource:
"""Create an SDK Resource from declarative config.
Does NOT read OTEL_RESOURCE_ATTRIBUTES. Resource detectors are only run
@@ -142,50 +138,61 @@ def create_resource(config: Optional[ResourceConfig]) -> Resource:
return result.merge(config_resource)
+def _detect_service(_config: Any) -> dict[str, AttributeValue]:
+ """Service detector: generates instance ID and reads OTEL_SERVICE_NAME."""
+ attrs: dict[str, AttributeValue] = {
+ SERVICE_INSTANCE_ID: str(uuid.uuid4()),
+ }
+ if service_name := os.environ.get(OTEL_SERVICE_NAME):
+ attrs[SERVICE_NAME] = service_name
+ return attrs
+
+
+_RESOURCE_DETECTOR_REGISTRY: dict[
+ str, Callable[[Any], dict[str, AttributeValue]]
+] = {
+ "service": _detect_service,
+ "host": lambda _: dict(_HostResourceDetector().detect().attributes),
+ "process": lambda _: dict(ProcessResourceDetector().detect().attributes),
+}
+
+
def _run_detectors(
detector_config: ExperimentalResourceDetector,
detected_attrs: dict[str, object],
) -> None:
- """Run any detectors present in a single detector config entry.
+ """Run detectors present in a single detector config entry.
- Each detector PR adds its own branch here. The detected_attrs dict
- is updated in-place; later detectors overwrite earlier ones for the
- same key.
+ Known detectors (service, host, process) are handled directly via
+ _RESOURCE_DETECTOR_REGISTRY. All other detectors — including known
+ schema fields like container that require contrib packages, and
+ unknown plugin detectors captured in additional_properties — are
+ loaded via the ``opentelemetry_resource_detector`` entry point group.
+
+ The detected_attrs dict is updated in-place; later detectors overwrite
+ earlier ones for the same key.
"""
- if detector_config.host is not None:
- detected_attrs.update(_HostResourceDetector().detect().attributes)
-
- if detector_config.container is not None:
- # The container detector is not part of the core SDK. It is provided
- # by the opentelemetry-resource-detector-containerid contrib package,
- # which registers itself under the opentelemetry_resource_detector
- # entry point group as "container". Loading via entry point matches
- # the env-var config counterpart (OTEL_EXPERIMENTAL_RESOURCE_DETECTORS)
- # and avoids a hard import dependency on contrib. See also:
- # https://github.com/open-telemetry/opentelemetry-configuration/issues/570
- ep = next(
- iter(
- entry_points(
- group="opentelemetry_resource_detector", name="container"
- )
- ),
- None,
- )
- if ep is None:
- _logger.warning(
- "container resource detector requested but "
- "'opentelemetry-resource-detector-containerid' is not "
- "installed; install it to enable container detection"
+ for name in dataclasses.fields(detector_config):
+ value = getattr(detector_config, name.name, None)
+ if value is None:
+ continue
+ if name.name in _RESOURCE_DETECTOR_REGISTRY:
+ detected_attrs.update(
+ _RESOURCE_DETECTOR_REGISTRY[name.name](value)
)
else:
- detected_attrs.update(ep.load()().detect().attributes)
+ cls = load_entry_point(
+ "opentelemetry_resource_detector", name.name
+ )
+ detected_attrs.update(cls(**(value or {})).detect().attributes)
- if detector_config.process is not None:
- detected_attrs.update(ProcessResourceDetector().detect().attributes)
+ for name, plugin_config in detector_config.additional_properties.items():
+ cls = load_entry_point("opentelemetry_resource_detector", name)
+ detected_attrs.update(cls(**(plugin_config or {})).detect().attributes)
def _filter_attributes(
- attrs: dict[str, object], filter_config: Optional[IncludeExclude]
+ attrs: dict[str, object], filter_config: IncludeExclude | None
) -> dict[str, object]:
"""Filter detected attribute keys using include/exclude glob patterns.
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_tracer_provider.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_tracer_provider.py
index 32dfd96..ab19145 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_tracer_provider.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_tracer_provider.py
@@ -1,24 +1,16 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
import logging
-from typing import Optional
from opentelemetry import trace
-from opentelemetry.sdk._configuration._common import _parse_headers
+from opentelemetry.sdk._configuration._common import (
+ _map_compression,
+ _parse_headers,
+ load_entry_point,
+)
from opentelemetry.sdk._configuration._exceptions import ConfigurationError
from opentelemetry.sdk._configuration.models import (
OtlpGrpcExporter as OtlpGrpcExporterConfig,
@@ -92,7 +84,9 @@ def _create_otlp_http_span_exporter(
"Install it with: pip install opentelemetry-exporter-otlp-proto-http"
) from exc
- compression = _map_compression(config.compression, Compression)
+ compression = _map_compression(
+ config.compression, Compression, allow_deflate=True
+ )
headers = _parse_headers(config.headers, config.headers_list)
timeout = (config.timeout / 1000.0) if config.timeout is not None else None
@@ -104,19 +98,6 @@ def _create_otlp_http_span_exporter(
)
-def _map_compression(
- value: Optional[str], compression_enum: type
-) -> Optional[object]:
- """Map a compression string to the given Compression enum value."""
- if value is None or value.lower() == "none":
- return None
- if value.lower() == "gzip":
- return compression_enum.Gzip # type: ignore[attr-defined]
- raise ConfigurationError(
- f"Unsupported compression value '{value}'. Supported values: 'gzip', 'none'."
- )
-
-
def _create_otlp_grpc_span_exporter(
config: OtlpGrpcExporterConfig,
) -> SpanExporter:
@@ -146,14 +127,35 @@ def _create_otlp_grpc_span_exporter(
)
+_SPAN_EXPORTER_REGISTRY: dict = {
+ "otlp_http": _create_otlp_http_span_exporter,
+ "otlp_grpc": _create_otlp_grpc_span_exporter,
+ "console": lambda _: ConsoleSpanExporter(),
+}
+
+
def _create_span_exporter(config: SpanExporterConfig) -> SpanExporter:
- """Create a span exporter from config."""
- if config.otlp_http is not None:
- return _create_otlp_http_span_exporter(config.otlp_http)
- if config.otlp_grpc is not None:
- return _create_otlp_grpc_span_exporter(config.otlp_grpc)
- if config.console is not None:
- return ConsoleSpanExporter()
+ """Create a span exporter from config.
+
+ Known exporter types are checked via typed fields on the SpanExporter
+ dataclass. Unknown exporter names captured in additional_properties
+ by the @_additional_properties decorator are loaded via the
+ ``opentelemetry_traces_exporter`` entry point group.
+ """
+ if config.otlp_file_development is not None:
+ raise ConfigurationError(
+ "otlp_file_development span exporter is experimental "
+ "and not yet supported."
+ )
+ for name, factory in _SPAN_EXPORTER_REGISTRY.items():
+ value = getattr(config, name, None)
+ if value is not None:
+ return factory(value)
+ if config.additional_properties:
+ name, plugin_config = next(iter(config.additional_properties.items()))
+ return load_entry_point("opentelemetry_traces_exporter", name)(
+ **(plugin_config or {})
+ )
raise ConfigurationError(
"No exporter type specified in span exporter config. "
"Supported types: otlp_http, otlp_grpc, console."
@@ -184,7 +186,13 @@ def _create_span_processor(
def _create_sampler(config: SamplerConfig) -> Sampler:
- """Create a sampler from config."""
+ """Create a sampler from config.
+
+ Known sampler types are checked via typed fields on the Sampler
+ dataclass. Unknown sampler names captured in additional_properties
+ by the @_additional_properties decorator are loaded via the
+ ``opentelemetry_sampler`` entry point group.
+ """
if config.always_on is not None:
return ALWAYS_ON
if config.always_off is not None:
@@ -194,6 +202,9 @@ def _create_sampler(config: SamplerConfig) -> Sampler:
return TraceIdRatioBased(ratio if ratio is not None else 1.0)
if config.parent_based is not None:
return _create_parent_based_sampler(config.parent_based)
+ if config.additional_properties:
+ name = next(iter(config.additional_properties))
+ return load_entry_point("opentelemetry_sampler", name)()
raise ConfigurationError(
f"Unknown or unsupported sampler type in config: {config!r}. "
"Supported types: always_on, always_off, trace_id_ratio_based, parent_based."
@@ -262,8 +273,8 @@ def _create_span_limits(config: SpanLimitsConfig) -> SpanLimits:
def create_tracer_provider(
- config: Optional[TracerProviderConfig],
- resource: Optional[Resource] = None,
+ config: TracerProviderConfig | None,
+ resource: Resource | None = None,
) -> TracerProvider:
"""Create an SDK TracerProvider from declarative config.
@@ -309,8 +320,8 @@ def create_tracer_provider(
def configure_tracer_provider(
- config: Optional[TracerProviderConfig],
- resource: Optional[Resource] = None,
+ config: TracerProviderConfig | None,
+ resource: Resource | None = None,
) -> None:
"""Configure the global TracerProvider from declarative config.
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/__init__.py
index 8d27b68..cb1e9ec 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""OpenTelemetry SDK File Configuration.
@@ -25,6 +14,10 @@ Example:
"""
from opentelemetry.sdk._configuration._exceptions import ConfigurationError
+from opentelemetry.sdk._configuration._logger_provider import (
+ configure_logger_provider,
+ create_logger_provider,
+)
from opentelemetry.sdk._configuration._meter_provider import (
configure_meter_provider,
create_meter_provider,
@@ -52,6 +45,8 @@ __all__ = [
"create_resource",
"create_propagator",
"configure_propagator",
+ "create_logger_provider",
+ "configure_logger_provider",
"create_tracer_provider",
"configure_tracer_provider",
"create_meter_provider",
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/_env_substitution.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/_env_substitution.py
index 0a42809..9d3a1b8 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/_env_substitution.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/_env_substitution.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""Environment variable substitution for configuration files."""
@@ -81,6 +70,23 @@ def substitute_env_vars(text: str) -> str:
f"Environment variable '{var_name}' not found and no default provided"
)
+ # Per spec: "It MUST NOT be possible to inject YAML structures by
+ # environment variables." Newlines are the primary injection vector —
+ # a value like "legit\nmalicious_key: val" would create extra YAML
+ # keys if substituted verbatim. Wrap such values in a YAML
+ # double-quoted scalar so the newline is treated as literal text.
+ # Simple values (no newlines) are returned as-is so that YAML type
+ # coercion still applies per spec ("Node types MUST be interpreted
+ # after environment variable substitution takes place").
+ if "\n" in value or "\r" in value:
+ escaped = (
+ value.replace("\\", "\\\\")
+ .replace('"', '\\"')
+ .replace("\n", "\\n")
+ .replace("\r", "\\r")
+ .replace("\t", "\\t")
+ )
+ return f'"{escaped}"'
return value
return re.sub(pattern, replace_var, text)
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/_loader.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/_loader.py
index eeab3f2..d56ce34 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/_loader.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/file/_loader.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""Configuration file loading and parsing."""
@@ -95,7 +84,7 @@ def load_config_file(file_path: str) -> OpenTelemetryConfiguration:
try:
with open(path, encoding="utf-8") as config_file:
content = config_file.read()
- except (OSError, IOError) as exc:
+ except OSError as exc:
_logger.exception("Failed to read configuration file: %s", file_path)
raise ConfigurationError(
f"Failed to read configuration file: {file_path}"
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py
index 41a0f6a..1c97e35 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py
@@ -1,25 +1,25 @@
# generated by datamodel-codegen:
# filename: schema.json
-# timestamp: 2026-03-11T13:56:48+00:00
+# timestamp: 2026-04-23T13:18:12+00:00
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
-from typing import Any, Optional, Union
+from typing import Any, ClassVar, TypeAlias
-from typing_extensions import TypeAlias
+from opentelemetry.sdk._configuration._common import _additional_properties
-AlwaysOffSampler: TypeAlias = Optional[dict[str, Any]]
+AlwaysOffSampler: TypeAlias = dict[str, Any] | None
-AlwaysOnSampler: TypeAlias = Optional[dict[str, Any]]
+AlwaysOnSampler: TypeAlias = dict[str, Any] | None
@dataclass
class AttributeLimits:
- attribute_value_length_limit: Optional[int] = None
- attribute_count_limit: Optional[int] = None
+ attribute_value_length_limit: int | None = None
+ attribute_count_limit: int | None = None
Value: TypeAlias = list[str]
@@ -42,44 +42,44 @@ class AttributeType(Enum):
double_array = "double_array"
-B3MultiPropagator: TypeAlias = Optional[dict[str, Any]]
+B3MultiPropagator: TypeAlias = dict[str, Any] | None
-B3Propagator: TypeAlias = Optional[dict[str, Any]]
+B3Propagator: TypeAlias = dict[str, Any] | None
-BaggagePropagator: TypeAlias = Optional[dict[str, Any]]
+BaggagePropagator: TypeAlias = dict[str, Any] | None
@dataclass
class Base2ExponentialBucketHistogramAggregation:
- max_scale: Optional[int] = None
- max_size: Optional[int] = None
- record_min_max: Optional[bool] = None
+ max_scale: int | None = None
+ max_size: int | None = None
+ record_min_max: bool | None = None
@dataclass
class CardinalityLimits:
- default: Optional[int] = None
- counter: Optional[int] = None
- gauge: Optional[int] = None
- histogram: Optional[int] = None
- observable_counter: Optional[int] = None
- observable_gauge: Optional[int] = None
- observable_up_down_counter: Optional[int] = None
- up_down_counter: Optional[int] = None
+ default: int | None = None
+ counter: int | None = None
+ gauge: int | None = None
+ histogram: int | None = None
+ observable_counter: int | None = None
+ observable_gauge: int | None = None
+ observable_up_down_counter: int | None = None
+ up_down_counter: int | None = None
-ConsoleExporter: TypeAlias = Optional[dict[str, Any]]
+ConsoleExporter: TypeAlias = dict[str, Any] | None
-DefaultAggregation: TypeAlias = Optional[dict[str, Any]]
+DefaultAggregation: TypeAlias = dict[str, Any] | None
Distribution: TypeAlias = dict[str, dict[str, Any]]
-DropAggregation: TypeAlias = Optional[dict[str, Any]]
+DropAggregation: TypeAlias = dict[str, Any] | None
class ExemplarFilter(Enum):
@@ -88,22 +88,22 @@ class ExemplarFilter(Enum):
trace_based = "trace_based"
-ExperimentalComposableAlwaysOffSampler: TypeAlias = Optional[dict[str, Any]]
+ExperimentalComposableAlwaysOffSampler: TypeAlias = dict[str, Any] | None
-ExperimentalComposableAlwaysOnSampler: TypeAlias = Optional[dict[str, Any]]
+ExperimentalComposableAlwaysOnSampler: TypeAlias = dict[str, Any] | None
@dataclass
class ExperimentalComposableProbabilitySampler:
- ratio: Optional[float] = None
+ ratio: float | None = None
@dataclass
class ExperimentalComposableRuleBasedSamplerRuleAttributePatterns:
key: str
- included: Optional[list[str]] = None
- excluded: Optional[list[str]] = None
+ included: list[str] | None = None
+ excluded: list[str] | None = None
@dataclass
@@ -112,24 +112,24 @@ class ExperimentalComposableRuleBasedSamplerRuleAttributeValues:
values: list[str]
-ExperimentalContainerResourceDetector: TypeAlias = Optional[dict[str, Any]]
+ExperimentalContainerResourceDetector: TypeAlias = dict[str, Any] | None
-ExperimentalHostResourceDetector: TypeAlias = Optional[dict[str, Any]]
+ExperimentalHostResourceDetector: TypeAlias = dict[str, Any] | None
@dataclass
class ExperimentalHttpClientInstrumentation:
- request_captured_headers: Optional[list[str]] = None
- response_captured_headers: Optional[list[str]] = None
- known_methods: Optional[list[str]] = None
+ request_captured_headers: list[str] | None = None
+ response_captured_headers: list[str] | None = None
+ known_methods: list[str] | None = None
@dataclass
class ExperimentalHttpServerInstrumentation:
- request_captured_headers: Optional[list[str]] = None
- response_captured_headers: Optional[list[str]] = None
- known_methods: Optional[list[str]] = None
+ request_captured_headers: list[str] | None = None
+ response_captured_headers: list[str] | None = None
+ known_methods: list[str] | None = None
ExperimentalLanguageSpecificInstrumentation: TypeAlias = dict[
@@ -139,7 +139,7 @@ ExperimentalLanguageSpecificInstrumentation: TypeAlias = dict[
@dataclass
class ExperimentalMeterConfig:
- enabled: Optional[bool] = None
+ enabled: bool | None = None
@dataclass
@@ -150,15 +150,15 @@ class ExperimentalMeterMatcherAndConfig:
@dataclass
class ExperimentalOtlpFileExporter:
- output_stream: Optional[str] = None
+ output_stream: str | None = None
@dataclass
class ExperimentalProbabilitySampler:
- ratio: Optional[float] = None
+ ratio: float | None = None
-ExperimentalProcessResourceDetector: TypeAlias = Optional[dict[str, Any]]
+ExperimentalProcessResourceDetector: TypeAlias = dict[str, Any] | None
class ExperimentalPrometheusTranslationStrategy(Enum):
@@ -174,12 +174,12 @@ class ExperimentalPrometheusTranslationStrategy(Enum):
@dataclass
class ExperimentalSemconvConfig:
- version: Optional[int] = None
- experimental: Optional[bool] = None
- dual_emit: Optional[bool] = None
+ version: int | None = None
+ experimental: bool | None = None
+ dual_emit: bool | None = None
-ExperimentalServiceResourceDetector: TypeAlias = Optional[dict[str, Any]]
+ExperimentalServiceResourceDetector: TypeAlias = dict[str, Any] | None
class ExperimentalSpanParent(Enum):
@@ -190,7 +190,7 @@ class ExperimentalSpanParent(Enum):
@dataclass
class ExperimentalTracerConfig:
- enabled: Optional[bool] = None
+ enabled: bool | None = None
@dataclass
@@ -201,13 +201,13 @@ class ExperimentalTracerMatcherAndConfig:
@dataclass
class ExperimentalUrlSanitization:
- sensitive_query_parameters: Optional[list[str]] = None
+ sensitive_query_parameters: list[str] | None = None
@dataclass
class ExplicitBucketHistogramAggregation:
- boundaries: Optional[list[float]] = None
- record_min_max: Optional[bool] = None
+ boundaries: list[float] | None = None
+ record_min_max: bool | None = None
class ExporterDefaultHistogramAggregation(Enum):
@@ -223,23 +223,23 @@ class ExporterTemporalityPreference(Enum):
@dataclass
class GrpcTls:
- ca_file: Optional[str] = None
- key_file: Optional[str] = None
- cert_file: Optional[str] = None
- insecure: Optional[bool] = None
+ ca_file: str | None = None
+ key_file: str | None = None
+ cert_file: str | None = None
+ insecure: bool | None = None
@dataclass
class HttpTls:
- ca_file: Optional[str] = None
- key_file: Optional[str] = None
- cert_file: Optional[str] = None
+ ca_file: str | None = None
+ key_file: str | None = None
+ cert_file: str | None = None
@dataclass
class IncludeExclude:
- included: Optional[list[str]] = None
- excluded: Optional[list[str]] = None
+ included: list[str] | None = None
+ excluded: list[str] | None = None
class InstrumentType(Enum):
@@ -252,46 +252,46 @@ class InstrumentType(Enum):
up_down_counter = "up_down_counter"
-LastValueAggregation: TypeAlias = Optional[dict[str, Any]]
+LastValueAggregation: TypeAlias = dict[str, Any] | None
@dataclass
class LogRecordLimits:
- attribute_value_length_limit: Optional[int] = None
- attribute_count_limit: Optional[int] = None
+ attribute_value_length_limit: int | None = None
+ attribute_count_limit: int | None = None
@dataclass
class NameStringValuePair:
name: str
- value: Optional[str]
+ value: str | None
-OpenCensusMetricProducer: TypeAlias = Optional[dict[str, Any]]
+OpenCensusMetricProducer: TypeAlias = dict[str, Any] | None
@dataclass
class OtlpGrpcExporter:
- endpoint: Optional[str] = None
- tls: Optional[GrpcTls] = None
- headers: Optional[list[NameStringValuePair]] = None
- headers_list: Optional[str] = None
- compression: Optional[str] = None
- timeout: Optional[int] = None
+ endpoint: str | None = None
+ tls: GrpcTls | None = None
+ headers: list[NameStringValuePair] | None = None
+ headers_list: str | None = None
+ compression: str | None = None
+ timeout: int | None = None
@dataclass
class OtlpGrpcMetricExporter:
- endpoint: Optional[str] = None
- tls: Optional[GrpcTls] = None
- headers: Optional[list[NameStringValuePair]] = None
- headers_list: Optional[str] = None
- compression: Optional[str] = None
- timeout: Optional[int] = None
- temporality_preference: Optional[ExporterTemporalityPreference] = None
- default_histogram_aggregation: Optional[
- ExporterDefaultHistogramAggregation
- ] = None
+ endpoint: str | None = None
+ tls: GrpcTls | None = None
+ headers: list[NameStringValuePair] | None = None
+ headers_list: str | None = None
+ compression: str | None = None
+ timeout: int | None = None
+ temporality_preference: ExporterTemporalityPreference | None = None
+ default_histogram_aggregation: (
+ ExporterDefaultHistogramAggregation | None
+ ) = None
class OtlpHttpEncoding(Enum):
@@ -301,28 +301,28 @@ class OtlpHttpEncoding(Enum):
@dataclass
class OtlpHttpExporter:
- endpoint: Optional[str] = None
- tls: Optional[HttpTls] = None
- headers: Optional[list[NameStringValuePair]] = None
- headers_list: Optional[str] = None
- compression: Optional[str] = None
- timeout: Optional[int] = None
- encoding: Optional[OtlpHttpEncoding] = None
+ endpoint: str | None = None
+ tls: HttpTls | None = None
+ headers: list[NameStringValuePair] | None = None
+ headers_list: str | None = None
+ compression: str | None = None
+ timeout: int | None = None
+ encoding: OtlpHttpEncoding | None = None
@dataclass
class OtlpHttpMetricExporter:
- endpoint: Optional[str] = None
- tls: Optional[HttpTls] = None
- headers: Optional[list[NameStringValuePair]] = None
- headers_list: Optional[str] = None
- compression: Optional[str] = None
- timeout: Optional[int] = None
- encoding: Optional[OtlpHttpEncoding] = None
- temporality_preference: Optional[ExporterTemporalityPreference] = None
- default_histogram_aggregation: Optional[
- ExporterDefaultHistogramAggregation
- ] = None
+ endpoint: str | None = None
+ tls: HttpTls | None = None
+ headers: list[NameStringValuePair] | None = None
+ headers_list: str | None = None
+ compression: str | None = None
+ timeout: int | None = None
+ encoding: OtlpHttpEncoding | None = None
+ temporality_preference: ExporterTemporalityPreference | None = None
+ default_histogram_aggregation: (
+ ExporterDefaultHistogramAggregation | None
+ ) = None
class SeverityNumber(Enum):
@@ -352,12 +352,14 @@ class SeverityNumber(Enum):
fatal4 = "fatal4"
+@_additional_properties
@dataclass
class SpanExporter:
- otlp_http: Optional[OtlpHttpExporter] = None
- otlp_grpc: Optional[OtlpGrpcExporter] = None
- otlp_file_development: Optional[ExperimentalOtlpFileExporter] = None
- console: Optional[ConsoleExporter] = None
+ otlp_http: OtlpHttpExporter | None = None
+ otlp_grpc: OtlpGrpcExporter | None = None
+ otlp_file_development: ExperimentalOtlpFileExporter | None = None
+ console: ConsoleExporter | None = None
+ additional_properties: ClassVar[dict[str, Any]]
class SpanKind(Enum):
@@ -370,100 +372,98 @@ class SpanKind(Enum):
@dataclass
class SpanLimits:
- attribute_value_length_limit: Optional[int] = None
- attribute_count_limit: Optional[int] = None
- event_count_limit: Optional[int] = None
- link_count_limit: Optional[int] = None
- event_attribute_count_limit: Optional[int] = None
- link_attribute_count_limit: Optional[int] = None
+ attribute_value_length_limit: int | None = None
+ attribute_count_limit: int | None = None
+ event_count_limit: int | None = None
+ link_count_limit: int | None = None
+ event_attribute_count_limit: int | None = None
+ link_attribute_count_limit: int | None = None
-SumAggregation: TypeAlias = Optional[dict[str, Any]]
+SumAggregation: TypeAlias = dict[str, Any] | None
-TraceContextPropagator: TypeAlias = Optional[dict[str, Any]]
+TraceContextPropagator: TypeAlias = dict[str, Any] | None
@dataclass
class TraceIdRatioBasedSampler:
- ratio: Optional[float] = None
+ ratio: float | None = None
@dataclass
class ViewSelector:
- instrument_name: Optional[str] = None
- instrument_type: Optional[InstrumentType] = None
- unit: Optional[str] = None
- meter_name: Optional[str] = None
- meter_version: Optional[str] = None
- meter_schema_url: Optional[str] = None
+ instrument_name: str | None = None
+ instrument_type: InstrumentType | None = None
+ unit: str | None = None
+ meter_name: str | None = None
+ meter_version: str | None = None
+ meter_schema_url: str | None = None
@dataclass
class Aggregation:
- default: Optional[DefaultAggregation] = None
- drop: Optional[DropAggregation] = None
- explicit_bucket_histogram: Optional[ExplicitBucketHistogramAggregation] = (
- None
- )
- base2_exponential_bucket_histogram: Optional[
- Base2ExponentialBucketHistogramAggregation
- ] = None
- last_value: Optional[LastValueAggregation] = None
- sum: Optional[SumAggregation] = None
+ default: DefaultAggregation | None = None
+ drop: DropAggregation | None = None
+ explicit_bucket_histogram: ExplicitBucketHistogramAggregation | None = None
+ base2_exponential_bucket_histogram: (
+ Base2ExponentialBucketHistogramAggregation | None
+ ) = None
+ last_value: LastValueAggregation | None = None
+ sum: SumAggregation | None = None
@dataclass
class AttributeNameValue:
name: str
- value: Optional[Union[str, float, bool, Value, Value1, Value2]]
- type: Optional[AttributeType] = None
+ value: str | float | bool | Value | Value1 | Value2 | None
+ type: AttributeType | None = None
@dataclass
class BatchSpanProcessor:
exporter: SpanExporter
- schedule_delay: Optional[int] = None
- export_timeout: Optional[int] = None
- max_queue_size: Optional[int] = None
- max_export_batch_size: Optional[int] = None
+ schedule_delay: int | None = None
+ export_timeout: int | None = None
+ max_queue_size: int | None = None
+ max_export_batch_size: int | None = None
@dataclass
class ConsoleMetricExporter:
- temporality_preference: Optional[ExporterTemporalityPreference] = None
- default_histogram_aggregation: Optional[
- ExporterDefaultHistogramAggregation
- ] = None
+ temporality_preference: ExporterTemporalityPreference | None = None
+ default_histogram_aggregation: (
+ ExporterDefaultHistogramAggregation | None
+ ) = None
@dataclass
class ExperimentalCodeInstrumentation:
- semconv: Optional[ExperimentalSemconvConfig] = None
+ semconv: ExperimentalSemconvConfig | None = None
@dataclass
class ExperimentalDbInstrumentation:
- semconv: Optional[ExperimentalSemconvConfig] = None
+ semconv: ExperimentalSemconvConfig | None = None
@dataclass
class ExperimentalGenAiInstrumentation:
- semconv: Optional[ExperimentalSemconvConfig] = None
+ semconv: ExperimentalSemconvConfig | None = None
@dataclass
class ExperimentalHttpInstrumentation:
- semconv: Optional[ExperimentalSemconvConfig] = None
- client: Optional[ExperimentalHttpClientInstrumentation] = None
- server: Optional[ExperimentalHttpServerInstrumentation] = None
+ semconv: ExperimentalSemconvConfig | None = None
+ client: ExperimentalHttpClientInstrumentation | None = None
+ server: ExperimentalHttpServerInstrumentation | None = None
@dataclass
class ExperimentalLoggerConfig:
- enabled: Optional[bool] = None
- minimum_severity: Optional[SeverityNumber] = None
- trace_based: Optional[bool] = None
+ enabled: bool | None = None
+ minimum_severity: SeverityNumber | None = None
+ trace_based: bool | None = None
@dataclass
@@ -474,93 +474,101 @@ class ExperimentalLoggerMatcherAndConfig:
@dataclass
class ExperimentalMessagingInstrumentation:
- semconv: Optional[ExperimentalSemconvConfig] = None
+ semconv: ExperimentalSemconvConfig | None = None
@dataclass
class ExperimentalMeterConfigurator:
- default_config: Optional[ExperimentalMeterConfig] = None
- meters: Optional[list[ExperimentalMeterMatcherAndConfig]] = None
+ default_config: ExperimentalMeterConfig | None = None
+ meters: list[ExperimentalMeterMatcherAndConfig] | None = None
@dataclass
class ExperimentalOtlpFileMetricExporter:
- output_stream: Optional[str] = None
- temporality_preference: Optional[ExporterTemporalityPreference] = None
- default_histogram_aggregation: Optional[
- ExporterDefaultHistogramAggregation
- ] = None
+ output_stream: str | None = None
+ temporality_preference: ExporterTemporalityPreference | None = None
+ default_histogram_aggregation: (
+ ExporterDefaultHistogramAggregation | None
+ ) = None
@dataclass
class ExperimentalPrometheusMetricExporter:
- host: Optional[str] = None
- port: Optional[int] = None
- without_scope_info: Optional[bool] = None
- without_target_info_development: Optional[bool] = None
- with_resource_constant_labels: Optional[IncludeExclude] = None
- translation_strategy: Optional[
- ExperimentalPrometheusTranslationStrategy
- ] = None
+ host: str | None = None
+ port: int | None = None
+ without_scope_info: bool | None = None
+ without_target_info_development: bool | None = None
+ with_resource_constant_labels: IncludeExclude | None = None
+ translation_strategy: ExperimentalPrometheusTranslationStrategy | None = (
+ None
+ )
+@_additional_properties
@dataclass
class ExperimentalResourceDetector:
- container: Optional[ExperimentalContainerResourceDetector] = None
- host: Optional[ExperimentalHostResourceDetector] = None
- process: Optional[ExperimentalProcessResourceDetector] = None
- service: Optional[ExperimentalServiceResourceDetector] = None
+ container: ExperimentalContainerResourceDetector | None = None
+ host: ExperimentalHostResourceDetector | None = None
+ process: ExperimentalProcessResourceDetector | None = None
+ service: ExperimentalServiceResourceDetector | None = None
+ additional_properties: ClassVar[dict[str, Any]]
@dataclass
class ExperimentalRpcInstrumentation:
- semconv: Optional[ExperimentalSemconvConfig] = None
+ semconv: ExperimentalSemconvConfig | None = None
@dataclass
class ExperimentalSanitization:
- url: Optional[ExperimentalUrlSanitization] = None
+ url: ExperimentalUrlSanitization | None = None
@dataclass
class ExperimentalTracerConfigurator:
- default_config: Optional[ExperimentalTracerConfig] = None
- tracers: Optional[list[ExperimentalTracerMatcherAndConfig]] = None
+ default_config: ExperimentalTracerConfig | None = None
+ tracers: list[ExperimentalTracerMatcherAndConfig] | None = None
+@_additional_properties
@dataclass
class LogRecordExporter:
- otlp_http: Optional[OtlpHttpExporter] = None
- otlp_grpc: Optional[OtlpGrpcExporter] = None
- otlp_file_development: Optional[ExperimentalOtlpFileExporter] = None
- console: Optional[ConsoleExporter] = None
+ otlp_http: OtlpHttpExporter | None = None
+ otlp_grpc: OtlpGrpcExporter | None = None
+ otlp_file_development: ExperimentalOtlpFileExporter | None = None
+ console: ConsoleExporter | None = None
+ additional_properties: ClassVar[dict[str, Any]]
+@_additional_properties
@dataclass
class MetricProducer:
- opencensus: Optional[OpenCensusMetricProducer] = None
+ opencensus: OpenCensusMetricProducer | None = None
+ additional_properties: ClassVar[dict[str, Any]]
+@_additional_properties
@dataclass
class PullMetricExporter:
- prometheus_development: Optional[ExperimentalPrometheusMetricExporter] = (
- None
- )
+ prometheus_development: ExperimentalPrometheusMetricExporter | None = None
+ additional_properties: ClassVar[dict[str, Any]]
@dataclass
class PullMetricReader:
exporter: PullMetricExporter
- producers: Optional[list[MetricProducer]] = None
- cardinality_limits: Optional[CardinalityLimits] = None
+ producers: list[MetricProducer] | None = None
+ cardinality_limits: CardinalityLimits | None = None
+@_additional_properties
@dataclass
class PushMetricExporter:
- otlp_http: Optional[OtlpHttpMetricExporter] = None
- otlp_grpc: Optional[OtlpGrpcMetricExporter] = None
- otlp_file_development: Optional[ExperimentalOtlpFileMetricExporter] = None
- console: Optional[ConsoleMetricExporter] = None
+ otlp_http: OtlpHttpMetricExporter | None = None
+ otlp_grpc: OtlpGrpcMetricExporter | None = None
+ otlp_file_development: ExperimentalOtlpFileMetricExporter | None = None
+ console: ConsoleMetricExporter | None = None
+ additional_properties: ClassVar[dict[str, Any]]
@dataclass
@@ -573,105 +581,111 @@ class SimpleSpanProcessor:
exporter: SpanExporter
+@_additional_properties
@dataclass
class SpanProcessor:
- batch: Optional[BatchSpanProcessor] = None
- simple: Optional[SimpleSpanProcessor] = None
+ batch: BatchSpanProcessor | None = None
+ simple: SimpleSpanProcessor | None = None
+ additional_properties: ClassVar[dict[str, Any]]
+@_additional_properties
@dataclass
class TextMapPropagator:
- tracecontext: Optional[TraceContextPropagator] = None
- baggage: Optional[BaggagePropagator] = None
- b3: Optional[B3Propagator] = None
- b3multi: Optional[B3MultiPropagator] = None
+ tracecontext: TraceContextPropagator | None = None
+ baggage: BaggagePropagator | None = None
+ b3: B3Propagator | None = None
+ b3multi: B3MultiPropagator | None = None
+ additional_properties: ClassVar[dict[str, Any]]
@dataclass
class ViewStream:
- name: Optional[str] = None
- description: Optional[str] = None
- aggregation: Optional[Aggregation] = None
- aggregation_cardinality_limit: Optional[int] = None
- attribute_keys: Optional[IncludeExclude] = None
+ name: str | None = None
+ description: str | None = None
+ aggregation: Aggregation | None = None
+ aggregation_cardinality_limit: int | None = None
+ attribute_keys: IncludeExclude | None = None
@dataclass
class BatchLogRecordProcessor:
exporter: LogRecordExporter
- schedule_delay: Optional[int] = None
- export_timeout: Optional[int] = None
- max_queue_size: Optional[int] = None
- max_export_batch_size: Optional[int] = None
+ schedule_delay: int | None = None
+ export_timeout: int | None = None
+ max_queue_size: int | None = None
+ max_export_batch_size: int | None = None
@dataclass
class ExperimentalGeneralInstrumentation:
- http: Optional[ExperimentalHttpInstrumentation] = None
- code: Optional[ExperimentalCodeInstrumentation] = None
- db: Optional[ExperimentalDbInstrumentation] = None
- gen_ai: Optional[ExperimentalGenAiInstrumentation] = None
- messaging: Optional[ExperimentalMessagingInstrumentation] = None
- rpc: Optional[ExperimentalRpcInstrumentation] = None
- sanitization: Optional[ExperimentalSanitization] = None
- stability_opt_in_list: Optional[str] = None
+ http: ExperimentalHttpInstrumentation | None = None
+ code: ExperimentalCodeInstrumentation | None = None
+ db: ExperimentalDbInstrumentation | None = None
+ gen_ai: ExperimentalGenAiInstrumentation | None = None
+ messaging: ExperimentalMessagingInstrumentation | None = None
+ rpc: ExperimentalRpcInstrumentation | None = None
+ sanitization: ExperimentalSanitization | None = None
+ stability_opt_in_list: str | None = None
@dataclass
class ExperimentalInstrumentation:
- general: Optional[ExperimentalGeneralInstrumentation] = None
- cpp: Optional[ExperimentalLanguageSpecificInstrumentation] = None
- dotnet: Optional[ExperimentalLanguageSpecificInstrumentation] = None
- erlang: Optional[ExperimentalLanguageSpecificInstrumentation] = None
- go: Optional[ExperimentalLanguageSpecificInstrumentation] = None
- java: Optional[ExperimentalLanguageSpecificInstrumentation] = None
- js: Optional[ExperimentalLanguageSpecificInstrumentation] = None
- php: Optional[ExperimentalLanguageSpecificInstrumentation] = None
- python: Optional[ExperimentalLanguageSpecificInstrumentation] = None
- ruby: Optional[ExperimentalLanguageSpecificInstrumentation] = None
- rust: Optional[ExperimentalLanguageSpecificInstrumentation] = None
- swift: Optional[ExperimentalLanguageSpecificInstrumentation] = None
+ general: ExperimentalGeneralInstrumentation | None = None
+ cpp: ExperimentalLanguageSpecificInstrumentation | None = None
+ dotnet: ExperimentalLanguageSpecificInstrumentation | None = None
+ erlang: ExperimentalLanguageSpecificInstrumentation | None = None
+ go: ExperimentalLanguageSpecificInstrumentation | None = None
+ java: ExperimentalLanguageSpecificInstrumentation | None = None
+ js: ExperimentalLanguageSpecificInstrumentation | None = None
+ php: ExperimentalLanguageSpecificInstrumentation | None = None
+ python: ExperimentalLanguageSpecificInstrumentation | None = None
+ ruby: ExperimentalLanguageSpecificInstrumentation | None = None
+ rust: ExperimentalLanguageSpecificInstrumentation | None = None
+ swift: ExperimentalLanguageSpecificInstrumentation | None = None
@dataclass
class ExperimentalLoggerConfigurator:
- default_config: Optional[ExperimentalLoggerConfig] = None
- loggers: Optional[list[ExperimentalLoggerMatcherAndConfig]] = None
+ default_config: ExperimentalLoggerConfig | None = None
+ loggers: list[ExperimentalLoggerMatcherAndConfig] | None = None
@dataclass
class ExperimentalResourceDetection:
- attributes: Optional[IncludeExclude] = None
- detectors: Optional[list[ExperimentalResourceDetector]] = None
+ attributes: IncludeExclude | None = None
+ detectors: list[ExperimentalResourceDetector] | None = None
+@_additional_properties
@dataclass
class LogRecordProcessor:
- batch: Optional[BatchLogRecordProcessor] = None
- simple: Optional[SimpleLogRecordProcessor] = None
+ batch: BatchLogRecordProcessor | None = None
+ simple: SimpleLogRecordProcessor | None = None
+ additional_properties: ClassVar[dict[str, Any]]
@dataclass
class PeriodicMetricReader:
exporter: PushMetricExporter
- interval: Optional[int] = None
- timeout: Optional[int] = None
- producers: Optional[list[MetricProducer]] = None
- cardinality_limits: Optional[CardinalityLimits] = None
+ interval: int | None = None
+ timeout: int | None = None
+ producers: list[MetricProducer] | None = None
+ cardinality_limits: CardinalityLimits | None = None
@dataclass
class Propagator:
- composite: Optional[list[TextMapPropagator]] = None
- composite_list: Optional[str] = None
+ composite: list[TextMapPropagator] | None = None
+ composite_list: str | None = None
@dataclass
class Resource:
- attributes: Optional[list[AttributeNameValue]] = None
- detection_development: Optional[ExperimentalResourceDetection] = None
- schema_url: Optional[str] = None
- attributes_list: Optional[str] = None
+ attributes: list[AttributeNameValue] | None = None
+ detection_development: ExperimentalResourceDetection | None = None
+ schema_url: str | None = None
+ attributes_list: str | None = None
@dataclass
@@ -683,41 +697,41 @@ class View:
@dataclass
class LoggerProvider:
processors: list[LogRecordProcessor]
- limits: Optional[LogRecordLimits] = None
- logger_configurator_development: Optional[
- ExperimentalLoggerConfigurator
- ] = None
+ limits: LogRecordLimits | None = None
+ logger_configurator_development: ExperimentalLoggerConfigurator | None = (
+ None
+ )
@dataclass
class MetricReader:
- periodic: Optional[PeriodicMetricReader] = None
- pull: Optional[PullMetricReader] = None
+ periodic: PeriodicMetricReader | None = None
+ pull: PullMetricReader | None = None
@dataclass
class MeterProvider:
readers: list[MetricReader]
- views: Optional[list[View]] = None
- exemplar_filter: Optional[ExemplarFilter] = None
- meter_configurator_development: Optional[ExperimentalMeterConfigurator] = (
- None
- )
+ views: list[View] | None = None
+ exemplar_filter: ExemplarFilter | None = None
+ meter_configurator_development: ExperimentalMeterConfigurator | None = None
+@_additional_properties
@dataclass
class OpenTelemetryConfiguration:
file_format: str
- disabled: Optional[bool] = None
- log_level: Optional[SeverityNumber] = None
- attribute_limits: Optional[AttributeLimits] = None
- logger_provider: Optional[LoggerProvider] = None
- meter_provider: Optional[MeterProvider] = None
- propagator: Optional[Propagator] = None
- tracer_provider: Optional[TracerProvider] = None
- resource: Optional[Resource] = None
- instrumentation_development: Optional[ExperimentalInstrumentation] = None
- distribution: Optional[Distribution] = None
+ disabled: bool | None = None
+ log_level: SeverityNumber | None = None
+ attribute_limits: AttributeLimits | None = None
+ logger_provider: LoggerProvider | None = None
+ meter_provider: MeterProvider | None = None
+ propagator: Propagator | None = None
+ tracer_provider: TracerProvider | None = None
+ resource: Resource | None = None
+ instrumentation_development: ExperimentalInstrumentation | None = None
+ distribution: Distribution | None = None
+ additional_properties: ClassVar[dict[str, Any]]
@dataclass
@@ -727,7 +741,7 @@ class ExperimentalComposableParentThresholdSampler:
@dataclass
class ExperimentalComposableRuleBasedSampler:
- rules: Optional[list[ExperimentalComposableRuleBasedSamplerRule]] = None
+ rules: list[ExperimentalComposableRuleBasedSamplerRule] | None = None
@dataclass
@@ -739,59 +753,63 @@ class ExperimentalComposableRuleBasedSamplerRule:
"""
sampler: ExperimentalComposableSampler
- attribute_values: Optional[
- ExperimentalComposableRuleBasedSamplerRuleAttributeValues
- ] = None
- attribute_patterns: Optional[
- ExperimentalComposableRuleBasedSamplerRuleAttributePatterns
- ] = None
- span_kinds: Optional[list[Optional[SpanKind]]] = None
- parent: Optional[list[Optional[ExperimentalSpanParent]]] = None
+ attribute_values: (
+ ExperimentalComposableRuleBasedSamplerRuleAttributeValues | None
+ ) = None
+ attribute_patterns: (
+ ExperimentalComposableRuleBasedSamplerRuleAttributePatterns | None
+ ) = None
+ span_kinds: list[SpanKind] | None = None
+ parent: list[ExperimentalSpanParent] | None = None
+@_additional_properties
@dataclass
class ExperimentalComposableSampler:
- always_off: Optional[ExperimentalComposableAlwaysOffSampler] = None
- always_on: Optional[ExperimentalComposableAlwaysOnSampler] = None
- parent_threshold: Optional[
- ExperimentalComposableParentThresholdSampler
- ] = None
- probability: Optional[ExperimentalComposableProbabilitySampler] = None
- rule_based: Optional[ExperimentalComposableRuleBasedSampler] = None
+ always_off: ExperimentalComposableAlwaysOffSampler | None = None
+ always_on: ExperimentalComposableAlwaysOnSampler | None = None
+ parent_threshold: ExperimentalComposableParentThresholdSampler | None = (
+ None
+ )
+ probability: ExperimentalComposableProbabilitySampler | None = None
+ rule_based: ExperimentalComposableRuleBasedSampler | None = None
+ additional_properties: ClassVar[dict[str, Any]]
@dataclass
class ExperimentalJaegerRemoteSampler:
endpoint: str
initial_sampler: Sampler
- interval: Optional[int] = None
+ interval: int | None = None
@dataclass
class ParentBasedSampler:
- root: Optional[Sampler] = None
- remote_parent_sampled: Optional[Sampler] = None
- remote_parent_not_sampled: Optional[Sampler] = None
- local_parent_sampled: Optional[Sampler] = None
- local_parent_not_sampled: Optional[Sampler] = None
+ root: Sampler | None = None
+ remote_parent_sampled: Sampler | None = None
+ remote_parent_not_sampled: Sampler | None = None
+ local_parent_sampled: Sampler | None = None
+ local_parent_not_sampled: Sampler | None = None
+@_additional_properties
@dataclass
class Sampler:
- always_off: Optional[AlwaysOffSampler] = None
- always_on: Optional[AlwaysOnSampler] = None
- composite_development: Optional[ExperimentalComposableSampler] = None
- jaeger_remote_development: Optional[ExperimentalJaegerRemoteSampler] = None
- parent_based: Optional[ParentBasedSampler] = None
- probability_development: Optional[ExperimentalProbabilitySampler] = None
- trace_id_ratio_based: Optional[TraceIdRatioBasedSampler] = None
+ always_off: AlwaysOffSampler | None = None
+ always_on: AlwaysOnSampler | None = None
+ composite_development: ExperimentalComposableSampler | None = None
+ jaeger_remote_development: ExperimentalJaegerRemoteSampler | None = None
+ parent_based: ParentBasedSampler | None = None
+ probability_development: ExperimentalProbabilitySampler | None = None
+ trace_id_ratio_based: TraceIdRatioBasedSampler | None = None
+ additional_properties: ClassVar[dict[str, Any]]
@dataclass
class TracerProvider:
processors: list[SpanProcessor]
- limits: Optional[SpanLimits] = None
- sampler: Optional[Sampler] = None
- tracer_configurator_development: Optional[
- ExperimentalTracerConfigurator
- ] = None
+ limits: SpanLimits | None = None
+ sampler: Sampler | None = None
+ tracer_configurator_development: ExperimentalTracerConfigurator | None = (
+ None
+ )
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_events/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_events/__init__.py
index ca90e9b..5ec8d86 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_events/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_events/__init__.py
@@ -1,20 +1,8 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import logging
from time import time_ns
-from typing import Optional
from typing_extensions import deprecated
@@ -43,9 +31,9 @@ class EventLogger(APIEventLogger):
self,
logger_provider: LoggerProvider,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
):
super().__init__(
name=name,
@@ -82,15 +70,15 @@ class EventLogger(APIEventLogger):
"Deprecated since version 1.39.0 and will be removed in a future release."
)
class EventLoggerProvider(APIEventLoggerProvider):
- def __init__(self, logger_provider: Optional[LoggerProvider] = None):
+ def __init__(self, logger_provider: LoggerProvider | None = None):
self._logger_provider = logger_provider or get_logger_provider()
def get_event_logger(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
) -> EventLogger:
if not name:
_logger.warning("EventLogger created with invalid name: %s", name)
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/__init__.py
index ec0b3df..37d4704 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.sdk._logs._internal import (
LogDroppedAttributesWarning,
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py
index fa53997..b6b1ce8 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
import abc
@@ -52,12 +41,24 @@ from opentelemetry.attributes import _VALID_ANY_VALUE_TYPES, BoundedAttributes
from opentelemetry.context import get_current
from opentelemetry.context.context import Context
from opentelemetry.metrics import MeterProvider, get_meter_provider
-from opentelemetry.sdk._logs._internal._logger_metrics import LoggerMetrics
+from opentelemetry.sdk._logs._internal._exceptions import (
+ _copy_log_record_with_exception,
+ _create_log_record_with_exception,
+ _set_log_record_exception_attributes,
+)
+from opentelemetry.sdk._logs._internal._logger_metrics import (
+ LoggerMetricsT,
+ create_logger_metrics,
+)
from opentelemetry.sdk.environment_variables import (
OTEL_ATTRIBUTE_COUNT_LIMIT,
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
OTEL_SDK_DISABLED,
)
+from opentelemetry.sdk.environment_variables._internal import (
+ parse_boolean_environment_variable,
+)
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.util import ns_to_iso_str
from opentelemetry.sdk.util._configurator import RuleBasedConfigurator
@@ -72,6 +73,8 @@ from opentelemetry.trace import (
)
from opentelemetry.util.types import AnyValue, _ExtendedAttributes
+# pylint: disable=too-many-lines
+
_DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT = 128
_ENV_VALUE_UNSET = ""
@@ -410,15 +413,16 @@ class SynchronousMultiLogRecordProcessor(LogRecordProcessor):
False otherwise.
"""
deadline_ns = time_ns() + timeout_millis * 1000000
+ all_flushed = True
for lp in self._log_record_processors:
current_ts = time_ns()
if current_ts >= deadline_ns:
return False
- if not lp.force_flush((deadline_ns - current_ts) // 1000000):
- return False
+ if lp.force_flush((deadline_ns - current_ts) // 1000000) is False:
+ all_flushed = False
- return True
+ return all_flushed
class ConcurrentMultiLogRecordProcessor(LogRecordProcessor):
@@ -492,7 +496,7 @@ class ConcurrentMultiLogRecordProcessor(LogRecordProcessor):
return False
for future in done_futures:
- if not future.result():
+ if future.result() is False:
return False
return True
@@ -664,13 +668,11 @@ class Logger(APILogger):
def __init__(
self,
resource: Resource,
- multi_log_record_processor: Union[
- SynchronousMultiLogRecordProcessor,
- ConcurrentMultiLogRecordProcessor,
- ],
+ multi_log_record_processor: SynchronousMultiLogRecordProcessor
+ | ConcurrentMultiLogRecordProcessor,
instrumentation_scope: InstrumentationScope,
*,
- logger_metrics: LoggerMetrics,
+ logger_metrics: LoggerMetricsT,
_logger_config: _LoggerConfig,
):
super().__init__(
@@ -712,6 +714,7 @@ class Logger(APILogger):
body: AnyValue | None = None,
attributes: _ExtendedAttributes | None = None,
event_name: str | None = None,
+ exception: BaseException | None = None,
) -> None:
"""Emits the :class:`ReadWriteLogRecord` by setting instrumentation scope
and forwarding to the processor.
@@ -721,6 +724,8 @@ class Logger(APILogger):
# If a record is provided, use it directly
if record is not None:
if not isinstance(record, ReadWriteLogRecord):
+ if record.exception is not None:
+ record = _copy_log_record_with_exception(record)
# pylint:disable=protected-access
writable_record = ReadWriteLogRecord._from_api_log_record(
record=record,
@@ -728,10 +733,11 @@ class Logger(APILogger):
instrumentation_scope=self._instrumentation_scope,
)
else:
+ _set_log_record_exception_attributes(record.log_record)
writable_record = record
else:
# Create a record from individual parameters
- log_record = LogRecord(
+ log_record = _create_log_record_with_exception(
timestamp=timestamp,
observed_timestamp=observed_timestamp,
context=context,
@@ -740,6 +746,7 @@ class Logger(APILogger):
body=body,
attributes=attributes,
event_name=event_name,
+ exception=exception,
)
# pylint:disable=protected-access
writable_record = ReadWriteLogRecord._from_api_log_record(
@@ -787,8 +794,11 @@ class LoggerProvider(APILoggerProvider):
self._multi_log_record_processor = (
multi_log_record_processor or SynchronousMultiLogRecordProcessor()
)
- self._logger_metrics = LoggerMetrics(
- meter_provider or get_meter_provider()
+ self._logger_metrics = create_logger_metrics(
+ meter_provider or get_meter_provider(),
+ parse_boolean_environment_variable(
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED
+ ),
)
disabled = environ.get(OTEL_SDK_DISABLED, "")
self._disabled = disabled.lower().strip() == "true"
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/_exceptions.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/_exceptions.py
new file mode 100644
index 0000000..f4805c7
--- /dev/null
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/_exceptions.py
@@ -0,0 +1,121 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+from __future__ import annotations
+
+import traceback
+
+from opentelemetry._logs import LogRecord
+from opentelemetry.attributes import BoundedAttributes
+from opentelemetry.semconv.attributes import exception_attributes
+from opentelemetry.util.types import AnyValue, _ExtendedAttributes
+
+
+def _get_exception_attributes(
+ exception: BaseException,
+) -> dict[str, AnyValue]:
+ stacktrace = "".join(
+ traceback.format_exception(
+ type(exception), value=exception, tb=exception.__traceback__
+ )
+ )
+ module = type(exception).__module__
+ qualname = type(exception).__qualname__
+ exception_type = (
+ f"{module}.{qualname}" if module and module != "builtins" else qualname
+ )
+ return {
+ exception_attributes.EXCEPTION_TYPE: exception_type,
+ exception_attributes.EXCEPTION_MESSAGE: str(exception),
+ exception_attributes.EXCEPTION_STACKTRACE: stacktrace,
+ }
+
+
+def _get_attributes_with_exception(
+ attributes: _ExtendedAttributes | None,
+ exception: BaseException | None,
+) -> _ExtendedAttributes | None:
+ if exception is None:
+ return attributes
+
+ exception_attributes_map = _get_exception_attributes(exception)
+ if attributes is None:
+ attributes_map: _ExtendedAttributes = {}
+ else:
+ attributes_map = attributes
+
+ if isinstance(attributes_map, BoundedAttributes):
+ bounded_attributes = attributes_map
+ merged = BoundedAttributes(
+ maxlen=bounded_attributes.maxlen,
+ attributes=bounded_attributes,
+ immutable=False,
+ max_value_len=bounded_attributes.max_value_len,
+ extended_attributes=bounded_attributes._extended_attributes, # pylint: disable=protected-access
+ )
+ merged.dropped = bounded_attributes.dropped
+ for key, value in exception_attributes_map.items():
+ if key not in merged:
+ merged[key] = value
+ return merged
+
+ return exception_attributes_map | dict(attributes_map.items())
+
+
+def _copy_log_record(
+ record: LogRecord,
+ attributes: _ExtendedAttributes | None,
+) -> LogRecord:
+ copied_record = LogRecord(
+ timestamp=record.timestamp,
+ observed_timestamp=record.observed_timestamp,
+ context=record.context,
+ severity_text=record.severity_text,
+ severity_number=record.severity_number,
+ body=record.body,
+ attributes=attributes,
+ event_name=record.event_name,
+ exception=getattr(record, "exception", None),
+ )
+ copied_record.trace_id = record.trace_id
+ copied_record.span_id = record.span_id
+ copied_record.trace_flags = record.trace_flags
+ return copied_record
+
+
+def _copy_log_record_with_exception(record: LogRecord) -> LogRecord:
+ return _copy_log_record(
+ record,
+ _get_attributes_with_exception(record.attributes, record.exception),
+ )
+
+
+def _set_log_record_exception_attributes(record: LogRecord) -> None:
+ record.attributes = _get_attributes_with_exception(
+ record.attributes,
+ record.exception,
+ )
+
+
+def _create_log_record_with_exception(
+ *,
+ timestamp: int | None = None,
+ observed_timestamp: int | None = None,
+ context=None,
+ severity_number=None,
+ severity_text: str | None = None,
+ body: AnyValue | None = None,
+ attributes: _ExtendedAttributes | None = None,
+ event_name: str | None = None,
+ exception: BaseException | None = None,
+) -> LogRecord:
+ return LogRecord(
+ timestamp=timestamp,
+ observed_timestamp=observed_timestamp,
+ context=context,
+ severity_number=severity_number,
+ severity_text=severity_text,
+ body=body,
+ attributes=_get_attributes_with_exception(attributes, exception),
+ event_name=event_name,
+ exception=exception,
+ )
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/_logger_metrics.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/_logger_metrics.py
index 92a4c76..1b068c5 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/_logger_metrics.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/_logger_metrics.py
@@ -1,16 +1,7 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
+
+from typing import Protocol
from opentelemetry import metrics as metrics_api
from opentelemetry.semconv._incubating.metrics.otel_metrics import (
@@ -18,6 +9,15 @@ from opentelemetry.semconv._incubating.metrics.otel_metrics import (
)
+class LoggerMetricsT(Protocol):
+ def emit_log(self) -> None: ...
+
+
+class NoOpLoggerMetrics:
+ def emit_log(self) -> None:
+ pass
+
+
class LoggerMetrics:
def __init__(self, meter_provider: metrics_api.MeterProvider) -> None:
meter = meter_provider.get_meter("opentelemetry-sdk")
@@ -25,3 +25,13 @@ class LoggerMetrics:
def emit_log(self) -> None:
self._created_logs.add(1)
+
+
+def create_logger_metrics(
+ meter_provider: metrics_api.MeterProvider,
+ enabled: bool,
+) -> LoggerMetricsT:
+ if not enabled:
+ return NoOpLoggerMetrics()
+
+ return LoggerMetrics(meter_provider)
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py
index 1c0f82a..31d4b96 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py
@@ -1,24 +1,14 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
import abc
import enum
import logging
import sys
+from collections.abc import Callable, Sequence
from os import environ, linesep
-from typing import IO, Callable, Optional, Sequence
+from typing import IO
from typing_extensions import deprecated
@@ -39,13 +29,19 @@ from opentelemetry.sdk._logs import (
from opentelemetry.sdk._shared_internal import (
BatchProcessor,
DuplicateFilter,
- ProcessorMetrics,
+)
+from opentelemetry.sdk._shared_internal._processor_metrics import (
+ create_processor_metrics,
)
from opentelemetry.sdk.environment_variables import (
OTEL_BLRP_EXPORT_TIMEOUT,
OTEL_BLRP_MAX_EXPORT_BATCH_SIZE,
OTEL_BLRP_MAX_QUEUE_SIZE,
OTEL_BLRP_SCHEDULE_DELAY,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
+)
+from opentelemetry.sdk.environment_variables._internal import (
+ parse_boolean_environment_variable,
)
from opentelemetry.sdk.resources import Resource
from opentelemetry.semconv._incubating.attributes.otel_attributes import (
@@ -186,10 +182,13 @@ class SimpleLogRecordProcessor(LogRecordProcessor):
):
self._exporter = exporter
self._shutdown = False
- self._metrics = ProcessorMetrics(
+ self._metrics = create_processor_metrics(
"logs",
OtelComponentTypeValues.SIMPLE_LOG_PROCESSOR,
meter_provider or get_meter_provider(),
+ enabled=parse_boolean_environment_variable(
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED
+ ),
)
def on_emit(self, log_record: ReadWriteLogRecord):
@@ -299,11 +298,14 @@ class BatchLogRecordProcessor(LogRecordProcessor):
export_timeout_millis,
max_queue_size,
"Log",
- ProcessorMetrics(
+ create_processor_metrics(
"logs",
OtelComponentTypeValues.BATCHING_LOG_PROCESSOR,
meter_provider or get_meter_provider(),
capacity=max_queue_size,
+ enabled=parse_boolean_environment_variable(
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED
+ ),
),
)
@@ -326,7 +328,7 @@ class BatchLogRecordProcessor(LogRecordProcessor):
def shutdown(self):
return self._batch_processor.shutdown()
- def force_flush(self, timeout_millis: Optional[int] = None) -> bool:
+ def force_flush(self, timeout_millis: int | None = None) -> bool:
return self._batch_processor.force_flush(timeout_millis)
@staticmethod
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py
index a724f81..5a6a049 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import threading
import typing
@@ -41,7 +30,7 @@ class InMemoryLogRecordExporter(LogRecordExporter):
with self._lock:
self._logs.clear()
- def get_finished_logs(self) -> typing.Tuple[ReadableLogRecord, ...]:
+ def get_finished_logs(self) -> tuple[ReadableLogRecord, ...]:
with self._lock:
return tuple(self._logs)
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py
index 2edcf7e..3925889 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.sdk._logs._internal.export import (
BatchLogRecordProcessor,
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py
index cde1916..bf816e2 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
@@ -25,7 +14,6 @@ import weakref
from abc import abstractmethod
from typing import (
Generic,
- Optional,
Protocol,
TypeVar,
)
@@ -37,7 +25,7 @@ from opentelemetry.context import (
set_value,
)
from opentelemetry.sdk._shared_internal._processor_metrics import (
- ProcessorMetrics,
+ ProcessorMetricsT,
)
from opentelemetry.util._once import Once
@@ -101,7 +89,7 @@ class BatchProcessor(Generic[Telemetry]):
export_timeout_millis: float,
max_queue_size: int,
exporting: str,
- metrics: ProcessorMetrics,
+ metrics: ProcessorMetricsT,
):
self._bsp_reset_once = Once()
self._exporter = exporter
@@ -249,7 +237,7 @@ class BatchProcessor(Generic[Telemetry]):
# the thread to finish.
# TODO: Fix force flush so the timeout is used https://github.com/open-telemetry/opentelemetry-python/issues/4568.
- def force_flush(self, timeout_millis: Optional[int] = None) -> bool:
+ def force_flush(self, timeout_millis: int | None = None) -> bool:
if self._shutdown:
return False
# Blocking call to export.
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/_processor_metrics.py b/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/_processor_metrics.py
index 47f90c2..9f7e7d7 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/_processor_metrics.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/_processor_metrics.py
@@ -1,22 +1,11 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
from collections import Counter
from collections.abc import Callable
-from typing import Literal
+from typing import Literal, Protocol
from opentelemetry.metrics import CallbackOptions, MeterProvider, Observation
from opentelemetry.semconv._incubating.attributes.otel_attributes import (
@@ -37,6 +26,27 @@ from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
_component_counter = Counter()
+class ProcessorMetricsT(Protocol):
+ def register_queue_size(
+ self, get_queue_size: Callable[[], int]
+ ) -> None: ...
+
+ def drop_items(self, count: int) -> None: ...
+
+ def finish_items(self, count: int, error: Exception | None) -> None: ...
+
+
+class NoOpProcessorMetrics:
+ def register_queue_size(self, get_queue_size: Callable[[], int]) -> None:
+ pass
+
+ def drop_items(self, count: int) -> None:
+ pass
+
+ def finish_items(self, count: int, error: Exception | None) -> None:
+ pass
+
+
class ProcessorMetrics:
def __init__(
self,
@@ -114,3 +124,22 @@ class ProcessorMetrics:
ERROR_TYPE: type(error).__name__,
}
self._processed.add(count, attrs)
+
+
+def create_processor_metrics(
+ signal: Literal["traces", "logs"],
+ component_type: OtelComponentTypeValues,
+ meter_provider: MeterProvider,
+ *,
+ capacity: int | None = None,
+ enabled: bool,
+) -> ProcessorMetricsT:
+ if not enabled:
+ return NoOpProcessorMetrics()
+
+ return ProcessorMetrics(
+ signal,
+ component_type,
+ meter_provider,
+ capacity=capacity,
+ )
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py
index 2959163..94738ca 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
OTEL_SDK_DISABLED = "OTEL_SDK_DISABLED"
"""
@@ -494,6 +483,19 @@ Entry point providers should implement the following:
Note: This environment variable is experimental and subject to change.
"""
+_OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES = (
+ "OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES"
+)
+"""
+.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES
+
+The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES` stores a comma-separated list of human-readable
+gRPC error codes that are considered retryable for the OTLP gRPC exporters (e.g. `UNAVAILABLE, DEADLINE_EXCEEDED`).
+Supported error codes are defined in `grpc.StatusCode` and are parsed in a case-insensitive manner.
+
+Note: This environment variable is experimental and subject to change.
+"""
+
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE = "OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE"
"""
.. envvar:: OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE
@@ -777,6 +779,13 @@ of names of resource detectors. These names must be the same as the names of
entry points for the ```opentelemetry_resource_detector``` entry point. This is an
experimental feature and the name of this variable and its behavior can change
in a non-backwards compatible way.
+
+Detectors are run in the order they are listed and their attributes are merged
+in that order, with later detectors taking precedence over earlier ones on
+conflicting keys. The ``otel`` detector (which reads
+:envvar:`OTEL_RESOURCE_ATTRIBUTES` and :envvar:`OTEL_SERVICE_NAME`) is always
+appended last unless explicitly placed elsewhere in the list, ensuring
+environment variable attributes take highest priority among detectors.
"""
OTEL_EXPORTER_PROMETHEUS_HOST = "OTEL_EXPORTER_PROMETHEUS_HOST"
@@ -838,3 +847,14 @@ Default: opentelemetry.sdk._logs._internal._default_logger_configurator
This is an experimental environment variable and the name of this variable and its behavior can
change in a non-backwards compatible way.
"""
+
+OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED = (
+ "OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED"
+)
+"""
+.. envvar:: OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED
+
+The :envvar:`OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED` environment variable enables
+metrics emitted by the SDK about its own internal state.
+Default: "false"
+"""
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/_internal.py b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/_internal.py
new file mode 100644
index 0000000..c8825d9
--- /dev/null
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables/_internal.py
@@ -0,0 +1,28 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+from logging import getLogger
+from os import environ
+
+_logger = getLogger(__name__)
+
+
+def parse_boolean_environment_variable(
+ environment_variable: str, default: bool = False
+) -> bool:
+ value = environ.get(environment_variable)
+ if value is None:
+ return default
+
+ match value.strip().lower():
+ case "true":
+ return True
+ case "false":
+ return False
+ case _:
+ _logger.warning(
+ "Invalid value for %s: %r. Expected 'true' or 'false'.",
+ environment_variable,
+ value,
+ )
+ return default
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/error_handler/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/error_handler/__init__.py
index d58c900..7506e8d 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/error_handler/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/error_handler/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
Global Error Handler
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py
index 4507c5e..c40c740 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.sdk.metrics import export, view
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/__init__.py
index e6583d1..a514be7 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/__init__.py
@@ -1,25 +1,14 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import weakref
from atexit import register, unregister
+from collections.abc import Callable, Sequence
from dataclasses import dataclass
from logging import getLogger
from os import environ
from threading import Lock
from time import time_ns
-from typing import Callable, Optional, Sequence
# This kind of import is needed to avoid Sphinx errors.
import opentelemetry.sdk.metrics
@@ -104,7 +93,7 @@ class Meter(APIMeter):
instrumentation_scope: InstrumentationScope,
measurement_consumer: MeasurementConsumer,
*,
- _meter_config: Optional[_MeterConfig] = None,
+ _meter_config: _MeterConfig | None = None,
):
super().__init__(
name=instrumentation_scope.name,
@@ -238,7 +227,7 @@ class Meter(APIMeter):
unit: str = "",
description: str = "",
*,
- explicit_bucket_boundaries_advisory: Optional[Sequence[float]] = None,
+ explicit_bucket_boundaries_advisory: Sequence[float] | None = None,
) -> APIHistogram:
if explicit_bucket_boundaries_advisory is not None:
invalid_advisory = False
@@ -481,12 +470,12 @@ class MeterProvider(APIMeterProvider):
metric_readers: Sequence[
"opentelemetry.sdk.metrics.export.MetricReader"
] = (),
- resource: Optional[Resource] = None,
- exemplar_filter: Optional[ExemplarFilter] = None,
+ resource: Resource | None = None,
+ exemplar_filter: ExemplarFilter | None = None,
shutdown_on_exit: bool = True,
views: Sequence["opentelemetry.sdk.metrics.view.View"] = (),
*,
- _meter_configurator: Optional[_MeterConfiguratorT] = None,
+ _meter_configurator: _MeterConfiguratorT | None = None,
):
self._lock = Lock()
self._meter_lock = Lock()
@@ -654,9 +643,9 @@ class MeterProvider(APIMeterProvider):
def get_meter(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[Attributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: Attributes | None = None,
) -> APIMeter:
if self._disabled:
return NoOpMeter(name, version=version, schema_url=schema_url)
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py
index be81d70..d9ba053 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py
@@ -1,31 +1,21 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
+from collections.abc import Sequence
from logging import getLogger
from threading import Lock
from time import time_ns
-from typing import Dict, List, Optional, Sequence
+from typing import cast
-from opentelemetry.metrics import Instrument
from opentelemetry.sdk.metrics._internal.aggregation import (
Aggregation,
+ AggregationTemporality,
DefaultAggregation,
_Aggregation,
_SumAggregation,
)
-from opentelemetry.sdk.metrics._internal.export import AggregationTemporality
+from opentelemetry.sdk.metrics._internal.instrument import _Instrument
from opentelemetry.sdk.metrics._internal.measurement import Measurement
from opentelemetry.sdk.metrics._internal.point import DataPointT
from opentelemetry.sdk.metrics._internal.view import View
@@ -37,12 +27,12 @@ class _ViewInstrumentMatch:
def __init__(
self,
view: View,
- instrument: Instrument,
- instrument_class_aggregation: Dict[type, Aggregation],
+ instrument: _Instrument,
+ instrument_class_aggregation: dict[type, Aggregation],
):
self._view = view
self._instrument = instrument
- self._attributes_aggregation: Dict[frozenset, _Aggregation] = {}
+ self._attributes_aggregation: dict[frozenset, _Aggregation] = {}
self._lock = Lock()
self._instrument_class_aggregation = instrument_class_aggregation
self._name = self._view._name or self._instrument.name
@@ -76,10 +66,16 @@ class _ViewInstrumentMatch:
# type since they are functionally equivalent.
and self._aggregation.__class__ == other._aggregation.__class__
)
+ if not result:
+ return result
+
if isinstance(self._aggregation, _SumAggregation):
+ # if result is True the two aggregation are of the same type
+ self._aggregation = cast(_SumAggregation, self._aggregation)
+ other._aggregation = cast(_SumAggregation, other._aggregation)
+
result = (
- result
- and self._aggregation._instrument_is_monotonic
+ self._aggregation._instrument_is_monotonic
== other._aggregation._instrument_is_monotonic
and self._aggregation._instrument_aggregation_temporality
== other._aggregation._instrument_aggregation_temporality
@@ -98,7 +94,7 @@ class _ViewInstrumentMatch:
if key in self._view._attribute_keys:
attributes[key] = value
elif measurement.attributes is not None:
- attributes = measurement.attributes
+ attributes = dict(measurement.attributes)
else:
attributes = {}
@@ -137,8 +133,8 @@ class _ViewInstrumentMatch:
self,
collection_aggregation_temporality: AggregationTemporality,
collection_start_nanos: int,
- ) -> Optional[Sequence[DataPointT]]:
- data_points: List[DataPointT] = []
+ ) -> Sequence[DataPointT] | None:
+ data_points: list[DataPointT] = []
with self._lock:
for aggregation in self._attributes_aggregation.values():
data_point = aggregation.collect(
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/aggregation.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/aggregation.py
index 46c30f9..dea213f 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/aggregation.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/aggregation.py
@@ -1,41 +1,26 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-lines
from abc import ABC, abstractmethod
from bisect import bisect_left
+from collections.abc import Callable, Sequence
from enum import IntEnum
from functools import partial
from logging import getLogger
from math import inf
from threading import Lock
from typing import (
- Callable,
Generic,
- List,
- Optional,
- Sequence,
- Type,
TypeVar,
+ cast,
)
from opentelemetry.metrics import (
Asynchronous,
Counter,
Histogram,
- Instrument,
ObservableCounter,
ObservableGauge,
ObservableUpDownCounter,
@@ -59,6 +44,7 @@ from opentelemetry.sdk.metrics._internal.exponential_histogram.mapping.exponent_
from opentelemetry.sdk.metrics._internal.exponential_histogram.mapping.logarithm_mapping import (
LogarithmMapping,
)
+from opentelemetry.sdk.metrics._internal.instrument import _Instrument
from opentelemetry.sdk.metrics._internal.measurement import Measurement
from opentelemetry.sdk.metrics._internal.point import Buckets as BucketsPoint
from opentelemetry.sdk.metrics._internal.point import (
@@ -117,7 +103,7 @@ class _Aggregation(ABC, Generic[_DataPointVarT]):
self,
collection_aggregation_temporality: AggregationTemporality,
collection_start_nano: int,
- ) -> Optional[_DataPointVarT]:
+ ) -> _DataPointVarT | None:
pass
def _collect_exemplars(self) -> Sequence[Exemplar]:
@@ -158,7 +144,7 @@ class _DropAggregation(_Aggregation):
self,
collection_aggregation_temporality: AggregationTemporality,
collection_start_nano: int,
- ) -> Optional[_DataPointVarT]:
+ ) -> _DataPointVarT | None:
pass
@@ -199,7 +185,7 @@ class _SumAggregation(_Aggregation[Sum]):
self,
collection_aggregation_temporality: AggregationTemporality,
collection_start_nano: int,
- ) -> Optional[NumberDataPoint]:
+ ) -> NumberDataPoint | None:
"""
Atomically return a point for the current value of the metric and
reset the aggregation value.
@@ -424,7 +410,7 @@ class _LastValueAggregation(_Aggregation[GaugePoint]):
self,
collection_aggregation_temporality: AggregationTemporality,
collection_start_nano: int,
- ) -> Optional[_DataPointVarT]:
+ ) -> _DataPointVarT | None:
"""
Atomically return a point for the current value of the metric.
"""
@@ -471,7 +457,7 @@ class _ExplicitBucketHistogramAggregation(_Aggregation[HistogramPoint]):
instrument_aggregation_temporality: AggregationTemporality,
start_time_unix_nano: int,
reservoir_builder: ExemplarReservoirBuilder,
- boundaries: Optional[Sequence[float]] = None,
+ boundaries: Sequence[float] | None = None,
record_min_max: bool = True,
):
if boundaries is None:
@@ -504,7 +490,7 @@ class _ExplicitBucketHistogramAggregation(_Aggregation[HistogramPoint]):
self._previous_collection_start_nano = self._start_time_unix_nano
- def _get_empty_bucket_counts(self) -> List[int]:
+ def _get_empty_bucket_counts(self) -> list[int]:
return [0] * (len(self._boundaries) + 1)
def aggregate(
@@ -530,7 +516,7 @@ class _ExplicitBucketHistogramAggregation(_Aggregation[HistogramPoint]):
self,
collection_aggregation_temporality: AggregationTemporality,
collection_start_nano: int,
- ) -> Optional[_DataPointVarT]:
+ ) -> _DataPointVarT | None:
"""
Atomically return a point for the current value of the metric.
"""
@@ -825,7 +811,7 @@ class _ExponentialBucketHistogramAggregation(_Aggregation[HistogramPoint]):
self,
collection_aggregation_temporality: AggregationTemporality,
collection_start_nano: int,
- ) -> Optional[_DataPointVarT]:
+ ) -> _DataPointVarT | None:
"""
Atomically return a point for the current value of the metric.
"""
@@ -968,6 +954,14 @@ class _ExponentialBucketHistogramAggregation(_Aggregation[HistogramPoint]):
if scale is None and self._previous_scale is not None:
scale = self._previous_scale
+ # here self._previous_value_negative and self._previous_value_positive are not Optional anymore
+ self._previous_value_negative = cast(
+ Buckets, self._previous_value_negative
+ )
+ self._previous_value_positive = cast(
+ Buckets, self._previous_value_positive
+ )
+
min_scale = min(self._previous_scale, scale)
low_positive, high_positive = (
@@ -1200,10 +1194,10 @@ class Aggregation(ABC):
@abstractmethod
def _create_aggregation(
self,
- instrument: Instrument,
+ instrument: _Instrument,
attributes: Attributes,
reservoir_factory: Callable[
- [Type[_Aggregation]], ExemplarReservoirBuilder
+ [type[_Aggregation]], ExemplarReservoirBuilder
],
start_time_unix_nano: int,
) -> _Aggregation:
@@ -1231,10 +1225,10 @@ class DefaultAggregation(Aggregation):
def _create_aggregation(
self,
- instrument: Instrument,
+ instrument: _Instrument,
attributes: Attributes,
reservoir_factory: Callable[
- [Type[_Aggregation]], ExemplarReservoirBuilder
+ [type[_Aggregation]], ExemplarReservoirBuilder
],
start_time_unix_nano: int,
) -> _Aggregation:
@@ -1323,10 +1317,10 @@ class ExponentialBucketHistogramAggregation(Aggregation):
def _create_aggregation(
self,
- instrument: Instrument,
+ instrument: _Instrument,
attributes: Attributes,
reservoir_factory: Callable[
- [Type[_Aggregation]], ExemplarReservoirBuilder
+ [type[_Aggregation]], ExemplarReservoirBuilder
],
start_time_unix_nano: int,
) -> _Aggregation:
@@ -1364,7 +1358,7 @@ class ExplicitBucketHistogramAggregation(Aggregation):
def __init__(
self,
- boundaries: Optional[Sequence[float]] = None,
+ boundaries: Sequence[float] | None = None,
record_min_max: bool = True,
) -> None:
self._boundaries = boundaries
@@ -1372,10 +1366,10 @@ class ExplicitBucketHistogramAggregation(Aggregation):
def _create_aggregation(
self,
- instrument: Instrument,
+ instrument: _Instrument,
attributes: Attributes,
reservoir_factory: Callable[
- [Type[_Aggregation]], ExemplarReservoirBuilder
+ [type[_Aggregation]], ExemplarReservoirBuilder
],
start_time_unix_nano: int,
) -> _Aggregation:
@@ -1416,10 +1410,10 @@ class SumAggregation(Aggregation):
def _create_aggregation(
self,
- instrument: Instrument,
+ instrument: _Instrument,
attributes: Attributes,
reservoir_factory: Callable[
- [Type[_Aggregation]], ExemplarReservoirBuilder
+ [type[_Aggregation]], ExemplarReservoirBuilder
],
start_time_unix_nano: int,
) -> _Aggregation:
@@ -1450,10 +1444,10 @@ class LastValueAggregation(Aggregation):
def _create_aggregation(
self,
- instrument: Instrument,
+ instrument: _Instrument,
attributes: Attributes,
reservoir_factory: Callable[
- [Type[_Aggregation]], ExemplarReservoirBuilder
+ [type[_Aggregation]], ExemplarReservoirBuilder
],
start_time_unix_nano: int,
) -> _Aggregation:
@@ -1468,10 +1462,10 @@ class DropAggregation(Aggregation):
def _create_aggregation(
self,
- instrument: Instrument,
+ instrument: _Instrument,
attributes: Attributes,
reservoir_factory: Callable[
- [Type[_Aggregation]], ExemplarReservoirBuilder
+ [type[_Aggregation]], ExemplarReservoirBuilder
],
start_time_unix_nano: int,
) -> _Aggregation:
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exceptions.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exceptions.py
index 0f8c3a7..5ae8519 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exceptions.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exceptions.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
class MetricsTimeoutError(Exception):
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/__init__.py
index ee93dd1..a00eb6c 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from .exemplar import Exemplar
from .exemplar_filter import (
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar.py
index 28237f0..e864529 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar.py
@@ -1,19 +1,7 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import dataclasses
-from typing import Optional, Union
from opentelemetry.util.types import Attributes
@@ -39,7 +27,7 @@ class Exemplar:
"""
filtered_attributes: Attributes
- value: Union[int, float]
+ value: int | float
time_unix_nano: int
- span_id: Optional[int] = None
- trace_id: Optional[int] = None
+ span_id: int | None = None
+ trace_id: int | None = None
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_filter.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_filter.py
index 8961d10..28f0adc 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_filter.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_filter.py
@@ -1,19 +1,7 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from abc import ABC, abstractmethod
-from typing import Union
from opentelemetry import trace
from opentelemetry.context import Context
@@ -35,7 +23,7 @@ class ExemplarFilter(ABC):
@abstractmethod
def should_sample(
self,
- value: Union[int, float],
+ value: int | float,
time_unix_nano: int,
attributes: Attributes,
context: Context,
@@ -62,7 +50,7 @@ class AlwaysOnExemplarFilter(ExemplarFilter):
def should_sample(
self,
- value: Union[int, float],
+ value: int | float,
time_unix_nano: int,
attributes: Attributes,
context: Context,
@@ -89,7 +77,7 @@ class AlwaysOffExemplarFilter(ExemplarFilter):
def should_sample(
self,
- value: Union[int, float],
+ value: int | float,
time_unix_nano: int,
attributes: Attributes,
context: Context,
@@ -115,7 +103,7 @@ class TraceBasedExemplarFilter(ExemplarFilter):
def should_sample(
self,
- value: Union[int, float],
+ value: int | float,
time_unix_nano: int,
attributes: Attributes,
context: Context,
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_reservoir.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_reservoir.py
index 22d1ee9..afe2dcb 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_reservoir.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_reservoir.py
@@ -1,29 +1,12 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from abc import ABC, abstractmethod
from collections import defaultdict
+from collections.abc import Callable, Mapping, Sequence
from random import randrange
from typing import (
Any,
- Callable,
- Dict,
- List,
- Mapping,
- Optional,
- Sequence,
- Union,
)
from opentelemetry import trace
@@ -49,7 +32,7 @@ class ExemplarReservoir(ABC):
@abstractmethod
def offer(
self,
- value: Union[int, float],
+ value: int | float,
time_unix_nano: int,
attributes: Attributes,
context: Context,
@@ -65,7 +48,7 @@ class ExemplarReservoir(ABC):
raise NotImplementedError("ExemplarReservoir.offer is not implemented")
@abstractmethod
- def collect(self, point_attributes: Attributes) -> List[Exemplar]:
+ def collect(self, point_attributes: Attributes) -> list[Exemplar]:
"""Returns accumulated Exemplars and also resets the reservoir for the next
sampling period
@@ -84,16 +67,16 @@ class ExemplarReservoir(ABC):
class ExemplarBucket:
def __init__(self) -> None:
- self.__value: Union[int, float] = 0
+ self.__value: int | float = 0
self.__attributes: Attributes = None
self.__time_unix_nano: int = 0
- self.__span_id: Optional[int] = None
- self.__trace_id: Optional[int] = None
+ self.__span_id: int | None = None
+ self.__trace_id: int | None = None
self.__offered: bool = False
def offer(
self,
- value: Union[int, float],
+ value: int | float,
time_unix_nano: int,
attributes: Attributes,
context: Context,
@@ -117,7 +100,7 @@ class ExemplarBucket:
self.__offered = True
- def collect(self, point_attributes: Attributes) -> Optional[Exemplar]:
+ def collect(self, point_attributes: Attributes) -> Exemplar | None:
"""May return an Exemplar and resets the bucket for the next sampling period."""
if not self.__offered:
return None
@@ -169,7 +152,7 @@ class FixedSizeExemplarReservoirABC(ExemplarReservoir):
ExemplarBucket
)
- def collect(self, point_attributes: Attributes) -> List[Exemplar]:
+ def collect(self, point_attributes: Attributes) -> list[Exemplar]:
"""Returns accumulated Exemplars and also resets the reservoir for the next
sampling period
@@ -194,7 +177,7 @@ class FixedSizeExemplarReservoirABC(ExemplarReservoir):
def offer(
self,
- value: Union[int, float],
+ value: int | float,
time_unix_nano: int,
attributes: Attributes,
context: Context,
@@ -222,7 +205,7 @@ class FixedSizeExemplarReservoirABC(ExemplarReservoir):
@abstractmethod
def _find_bucket_index(
self,
- value: Union[int, float],
+ value: int | float,
time_unix_nano: int,
attributes: Attributes,
context: Context,
@@ -267,7 +250,7 @@ class SimpleFixedSizeExemplarReservoir(FixedSizeExemplarReservoirABC):
def _find_bucket_index(
self,
- value: Union[int, float],
+ value: int | float,
time_unix_nano: int,
attributes: Attributes,
context: Context,
@@ -298,7 +281,7 @@ class AlignedHistogramBucketExemplarReservoir(FixedSizeExemplarReservoirABC):
def offer(
self,
- value: Union[int, float],
+ value: int | float,
time_unix_nano: int,
attributes: Attributes,
context: Context,
@@ -313,7 +296,7 @@ class AlignedHistogramBucketExemplarReservoir(FixedSizeExemplarReservoirABC):
def _find_bucket_index(
self,
- value: Union[int, float],
+ value: int | float,
time_unix_nano: int,
attributes: Attributes,
context: Context,
@@ -324,9 +307,7 @@ class AlignedHistogramBucketExemplarReservoir(FixedSizeExemplarReservoirABC):
return len(self._boundaries)
-ExemplarReservoirBuilder = Callable[[Dict[str, Any]], ExemplarReservoir]
-ExemplarReservoirBuilder.__doc__ = """ExemplarReservoir builder.
-
-It may receive the Aggregation parameters it is bounded to; e.g.
-the _ExplicitBucketHistogramAggregation will provide the boundaries.
-"""
+# ExemplarReservoirBuilder: A callable that takes Aggregation parameters
+# (e.g. boundaries for _ExplicitBucketHistogramAggregation) and returns
+# an ExemplarReservoir instance.
+ExemplarReservoirBuilder = Callable[[dict[str, Any]], ExemplarReservoir]
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/buckets.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/buckets.py
index e8a9332..62d6edb 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/buckets.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/buckets.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from math import ceil, log2
@@ -182,9 +171,9 @@ class Buckets:
# pylint: disable=protected-access
# pylint: disable=attribute-defined-outside-init
# pylint: disable=invalid-name
- copy._Buckets__index_base = self._Buckets__index_base
- copy._Buckets__index_start = self._Buckets__index_start
- copy._Buckets__index_end = self._Buckets__index_end
+ copy._Buckets__index_base = self._Buckets__index_base # type: ignore[reportArgumentType]
+ copy._Buckets__index_start = self._Buckets__index_start # type: ignore[reportArgumentType]
+ copy._Buckets__index_end = self._Buckets__index_end # type: ignore[reportArgumentType]
copy._counts = [0 for _ in self._counts]
return copy
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__init__.py
index 387b1d1..a62a2e6 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/__init__.py
@@ -1,18 +1,8 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from abc import ABC, abstractmethod
+from threading import Lock
class Mapping(ABC):
@@ -20,6 +10,12 @@ class Mapping(ABC):
Parent class for `LogarithmMapping` and `ExponentialMapping`.
"""
+ _mappings: dict[int, "Mapping"]
+ _mappings_lock: Lock
+
+ _min_scale: int
+ _max_scale: int
+
# pylint: disable=no-member
def __new__(cls, scale: int):
with cls._mappings_lock:
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/errors.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/errors.py
index 477ed6f..8bf64b2 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/errors.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/errors.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
class MappingUnderflowError(Exception):
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/exponent_mapping.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/exponent_mapping.py
index ce8f862..e603d14 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/exponent_mapping.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/exponent_mapping.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from math import ldexp
from threading import Lock
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.py
index d4b7e86..b1815c4 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/ieee_754.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from ctypes import c_double, c_uint64
from sys import float_info
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/logarithm_mapping.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/logarithm_mapping.py
index 980be89..2a3f6ca 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/logarithm_mapping.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/mapping/logarithm_mapping.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from math import exp, floor, ldexp, log
from threading import Lock
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py
index 66f3273..676a356 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py
@@ -1,29 +1,19 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
import math
import os
import weakref
from abc import ABC, abstractmethod
+from collections.abc import Callable, Iterable
from enum import Enum
from logging import getLogger
from os import environ, linesep
from sys import stdout
from threading import Event, Lock, RLock, Thread
from time import perf_counter, time_ns
-from typing import IO, Callable, Iterable, Optional
+from typing import IO
from typing_extensions import final
@@ -39,6 +29,10 @@ from opentelemetry.metrics import MeterProvider, NoOpMeterProvider
from opentelemetry.sdk.environment_variables import (
OTEL_METRIC_EXPORT_INTERVAL,
OTEL_METRIC_EXPORT_TIMEOUT,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
+)
+from opentelemetry.sdk.environment_variables._internal import (
+ parse_boolean_environment_variable,
)
from opentelemetry.sdk.metrics._internal.aggregation import (
AggregationTemporality,
@@ -67,7 +61,7 @@ from opentelemetry.semconv._incubating.attributes.otel_attributes import (
)
from opentelemetry.util._once import Once
-from ._metric_reader_metrics import MetricReaderMetrics
+from ._metric_reader_metrics import create_metric_reader_metrics
_logger = getLogger(__name__)
@@ -331,8 +325,12 @@ class MetricReader(ABC):
if otel_component_type
else type(self).__qualname__
)
- self._metrics = MetricReaderMetrics(
- self._otel_component_type, NoOpMeterProvider()
+ self._metrics = create_metric_reader_metrics(
+ self._otel_component_type,
+ NoOpMeterProvider(),
+ parse_boolean_environment_variable(
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED
+ ),
)
@final
@@ -390,8 +388,12 @@ class MetricReader(ABC):
"""Called by `MetricReader.collect` when it receives a batch of metrics"""
def _set_meter_provider(self, meter_provider: MeterProvider) -> None:
- self._metrics = MetricReaderMetrics(
- self._otel_component_type, meter_provider
+ self._metrics = create_metric_reader_metrics(
+ self._otel_component_type,
+ meter_provider,
+ parse_boolean_environment_variable(
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED
+ ),
)
def force_flush(self, timeout_millis: float = 10_000) -> bool:
@@ -436,7 +438,7 @@ class InMemoryMetricReader(MetricReader):
def get_metrics_data(
self,
- ) -> Optional[MetricsData]:
+ ) -> MetricsData | None:
"""Reads and returns current metrics from the SDK"""
with self._lock:
self.collect()
@@ -470,8 +472,8 @@ class PeriodicExportingMetricReader(MetricReader):
def __init__(
self,
exporter: MetricExporter,
- export_interval_millis: Optional[float] = None,
- export_timeout_millis: Optional[float] = None,
+ export_interval_millis: float | None = None,
+ export_timeout_millis: float | None = None,
) -> None:
# PeriodicExportingMetricReader defers to exporter for configuration
super().__init__(
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/_metric_reader_metrics.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/_metric_reader_metrics.py
index 435d9c2..f7fa95c 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/_metric_reader_metrics.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/_metric_reader_metrics.py
@@ -1,4 +1,8 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
from collections import Counter
+from typing import Protocol
from opentelemetry.metrics import MeterProvider
from opentelemetry.semconv._incubating.attributes.otel_attributes import (
@@ -12,6 +16,15 @@ from opentelemetry.semconv._incubating.metrics.otel_metrics import (
_component_counter = Counter()
+class MetricReaderMetricsT(Protocol):
+ def record_collection(self, duration: float) -> None: ...
+
+
+class NoOpMetricReaderMetrics:
+ def record_collection(self, duration: float) -> None:
+ pass
+
+
class MetricReaderMetrics:
def __init__(
self, component_type: str, meter_provider: MeterProvider
@@ -32,3 +45,14 @@ class MetricReaderMetrics:
def record_collection(self, duration: float) -> None:
self._collection_duration.record(duration, self._standard_attrs)
+
+
+def create_metric_reader_metrics(
+ component_type: str,
+ meter_provider: MeterProvider,
+ enabled: bool,
+) -> MetricReaderMetricsT:
+ if not enabled:
+ return NoOpMetricReaderMetrics()
+
+ return MetricReaderMetrics(component_type, meter_provider)
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/instrument.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/instrument.py
index 2f6e47a..946cdf1 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/instrument.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/instrument.py
@@ -1,27 +1,22 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-ancestors, unused-import
from __future__ import annotations
+from collections.abc import Generator, Iterable, Sequence
from logging import getLogger
from time import time_ns
-from typing import TYPE_CHECKING, Generator, Iterable, List, Sequence, Union
+from typing import (
+ TYPE_CHECKING,
+ Protocol,
+ cast,
+ runtime_checkable,
+)
# This kind of import is needed to avoid Sphinx errors.
from opentelemetry.context import Context, get_current
-from opentelemetry.metrics import CallbackT
+from opentelemetry.metrics import Asynchronous, CallbackT, Synchronous
from opentelemetry.metrics import Counter as APICounter
from opentelemetry.metrics import Histogram as APIHistogram
from opentelemetry.metrics import ObservableCounter as APIObservableCounter
@@ -39,9 +34,11 @@ from opentelemetry.sdk.metrics._internal.measurement import Measurement
if TYPE_CHECKING:
from opentelemetry.sdk.metrics._internal import (
- MeasurementConsumer,
_ProxyMeterConfig,
)
+ from opentelemetry.sdk.metrics._internal.measurement_consumer import (
+ MeasurementConsumer,
+ )
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
@@ -53,7 +50,15 @@ _ERROR_MESSAGE = (
)
-class _Synchronous:
+@runtime_checkable
+class _Instrument(Protocol):
+ name: str
+ unit: str
+ description: str
+ instrumentation_scope: InstrumentationScope
+
+
+class _Synchronous(_Instrument, Synchronous):
def __init__(
self,
name: str,
@@ -77,7 +82,7 @@ class _Synchronous:
name = result["name"]
unit = result["unit"]
- description = result["description"]
+ description = cast(str, result["description"])
self.name = name.lower()
self.unit = unit
@@ -91,13 +96,13 @@ class _Synchronous:
return self._meter_config is None or self._meter_config.is_enabled
-class _Asynchronous:
+class _Asynchronous(_Instrument, Asynchronous):
def __init__(
self,
name: str,
instrumentation_scope: InstrumentationScope,
measurement_consumer: MeasurementConsumer,
- callbacks: Iterable[CallbackT] | None = None,
+ callbacks: Sequence[CallbackT] | None = None,
unit: str = "",
description: str = "",
*,
@@ -116,7 +121,7 @@ class _Asynchronous:
name = result["name"]
unit = result["unit"]
- description = result["description"]
+ description = cast(str, result["description"])
self.name = name.lower()
self.unit = unit
@@ -126,7 +131,7 @@ class _Asynchronous:
self._meter_config = _meter_config
super().__init__(name, callbacks, unit=unit, description=description)
- self._callbacks: List[CallbackT] = []
+ self._callbacks: list[CallbackT] = []
if callbacks is not None:
for callback in callbacks:
@@ -179,7 +184,7 @@ class Counter(_Synchronous, APICounter):
def add(
self,
- amount: Union[int, float],
+ amount: int | float,
attributes: dict[str, str] | None = None,
context: Context | None = None,
):
@@ -212,7 +217,7 @@ class UpDownCounter(_Synchronous, APIUpDownCounter):
def add(
self,
- amount: Union[int, float],
+ amount: int | float,
attributes: dict[str, str] | None = None,
context: Context | None = None,
):
@@ -281,7 +286,7 @@ class Histogram(_Synchronous, APIHistogram):
def record(
self,
- amount: Union[int, float],
+ amount: int | float,
attributes: dict[str, str] | None = None,
context: Context | None = None,
):
@@ -315,7 +320,7 @@ class Gauge(_Synchronous, APIGauge):
def set(
self,
- amount: Union[int, float],
+ amount: int | float,
attributes: dict[str, str] | None = None,
context: Context | None = None,
):
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/measurement.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/measurement.py
index a73d600..cd81c39 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/measurement.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/measurement.py
@@ -1,24 +1,17 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
+
+from __future__ import annotations
from dataclasses import dataclass
-from typing import Union
+from typing import TYPE_CHECKING
from opentelemetry.context import Context
-from opentelemetry.metrics import Instrument
from opentelemetry.util.types import Attributes
+if TYPE_CHECKING:
+ from opentelemetry.sdk.metrics._internal.instrument import _Instrument
+
@dataclass(frozen=True)
class Measurement:
@@ -33,8 +26,8 @@ class Measurement:
attributes: Measurement attributes
"""
- value: Union[int, float]
+ value: int | float
time_unix_nano: int
- instrument: Instrument
+ instrument: _Instrument
context: Context
attributes: Attributes = None
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/measurement_consumer.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/measurement_consumer.py
index 302f82d..64d9967 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/measurement_consumer.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/measurement_consumer.py
@@ -1,23 +1,12 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=unused-import
from abc import ABC, abstractmethod
+from collections.abc import Mapping
from threading import Lock
from time import time_ns
-from typing import List, Mapping, Optional
# This kind of import is needed to avoid Sphinx errors.
import opentelemetry.sdk.metrics
@@ -51,14 +40,14 @@ class MeasurementConsumer(ABC):
self,
metric_reader: "opentelemetry.sdk.metrics.export.MetricReader",
timeout_millis: float = 10_000,
- ) -> Optional[MetricsData]:
+ ) -> MetricsData | None:
pass
class SynchronousMeasurementConsumer(MeasurementConsumer):
def __init__(
self,
- sdk_config: "opentelemetry.sdk.metrics._internal.SdkConfiguration",
+ sdk_config: "opentelemetry.sdk.metrics._internal.sdk_configuration.SdkConfiguration",
) -> None:
self._lock = Lock()
self._sdk_config = sdk_config
@@ -73,7 +62,7 @@ class SynchronousMeasurementConsumer(MeasurementConsumer):
)
for reader in sdk_config.metric_readers
}
- self._async_instruments: List[
+ self._async_instruments: list[
opentelemetry.sdk.metrics._internal.instrument._Asynchronous
] = []
@@ -104,7 +93,7 @@ class SynchronousMeasurementConsumer(MeasurementConsumer):
self,
metric_reader: "opentelemetry.sdk.metrics.export.MetricReader",
timeout_millis: float = 10_000,
- ) -> Optional[MetricsData]:
+ ) -> MetricsData | None:
with self._lock:
metric_reader_storage = self._reader_storages[metric_reader]
# for now, just use the defaults
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/metric_reader_storage.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/metric_reader_storage.py
index 317fda0..7c40943 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/metric_reader_storage.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/metric_reader_storage.py
@@ -1,26 +1,13 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from logging import getLogger
from threading import RLock
from time import time_ns
-from typing import Dict, List, Optional
from opentelemetry.metrics import (
Asynchronous,
Counter,
- Instrument,
ObservableCounter,
)
from opentelemetry.sdk.metrics._internal._view_instrument_match import (
@@ -28,6 +15,7 @@ from opentelemetry.sdk.metrics._internal._view_instrument_match import (
)
from opentelemetry.sdk.metrics._internal.aggregation import (
Aggregation,
+ AggregationTemporality,
ExplicitBucketHistogramAggregation,
_DropAggregation,
_ExplicitBucketHistogramAggregation,
@@ -35,7 +23,7 @@ from opentelemetry.sdk.metrics._internal.aggregation import (
_LastValueAggregation,
_SumAggregation,
)
-from opentelemetry.sdk.metrics._internal.export import AggregationTemporality
+from opentelemetry.sdk.metrics._internal.instrument import _Instrument
from opentelemetry.sdk.metrics._internal.measurement import Measurement
from opentelemetry.sdk.metrics._internal.point import (
ExponentialHistogram,
@@ -64,20 +52,20 @@ class MetricReaderStorage:
def __init__(
self,
sdk_config: SdkConfiguration,
- instrument_class_temporality: Dict[type, AggregationTemporality],
- instrument_class_aggregation: Dict[type, Aggregation],
+ instrument_class_temporality: dict[type, AggregationTemporality],
+ instrument_class_aggregation: dict[type, Aggregation],
) -> None:
self._lock = RLock()
self._sdk_config = sdk_config
- self._instrument_view_instrument_matches: Dict[
- Instrument, List[_ViewInstrumentMatch]
+ self._instrument_view_instrument_matches: dict[
+ _Instrument, list[_ViewInstrumentMatch]
] = {}
self._instrument_class_temporality = instrument_class_temporality
self._instrument_class_aggregation = instrument_class_aggregation
def _get_or_init_view_instrument_match(
- self, instrument: Instrument
- ) -> List[_ViewInstrumentMatch]:
+ self, instrument: _Instrument
+ ) -> list[_ViewInstrumentMatch]:
# Optimistically get the relevant views for the given instrument. Once set for a given
# instrument, the mapping will never change
@@ -123,7 +111,7 @@ class MetricReaderStorage:
measurement, should_sample_exemplar
)
- def collect(self) -> Optional[MetricsData]:
+ def collect(self) -> MetricsData | None:
# Use a list instead of yielding to prevent a slow reader from holding
# SDK locks
@@ -139,7 +127,7 @@ class MetricReaderStorage:
collection_start_nanos = time_ns()
with self._lock:
- instrumentation_scope_scope_metrics: Dict[
+ instrumentation_scope_scope_metrics: dict[
InstrumentationScope, ScopeMetrics
] = {}
@@ -155,7 +143,7 @@ class MetricReaderStorage:
instrument.__class__
]
- metrics: List[Metric] = []
+ metrics: list[Metric] = []
for view_instrument_match in view_instrument_matches:
data_points = view_instrument_match.collect(
@@ -253,8 +241,8 @@ class MetricReaderStorage:
def _handle_view_instrument_match(
self,
- instrument: Instrument,
- view_instrument_matches: List["_ViewInstrumentMatch"],
+ instrument: _Instrument,
+ view_instrument_matches: list["_ViewInstrumentMatch"],
) -> None:
for view in self._sdk_config.views:
# pylint: disable=protected-access
@@ -292,7 +280,7 @@ class MetricReaderStorage:
@staticmethod
def _check_view_instrument_compatibility(
- view: View, instrument: Instrument
+ view: View, instrument: _Instrument
) -> bool:
"""
Checks if a view and an instrument are compatible.
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py
index 8c7e346..4f7df8a 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py
@@ -1,22 +1,11 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=unused-import
+from collections.abc import Sequence
from dataclasses import asdict, dataclass, field
from json import dumps, loads
-from typing import Optional, Sequence, Union
# This kind of import is needed to avoid Sphinx errors.
import opentelemetry.sdk.metrics._internal
@@ -35,10 +24,10 @@ class NumberDataPoint:
attributes: Attributes
start_time_unix_nano: int
time_unix_nano: int
- value: Union[int, float]
+ value: int | float
exemplars: Sequence[Exemplar] = field(default_factory=list)
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(asdict(self), indent=indent)
@@ -52,14 +41,14 @@ class HistogramDataPoint:
start_time_unix_nano: int
time_unix_nano: int
count: int
- sum: Union[int, float]
+ sum: int | float
bucket_counts: Sequence[int]
explicit_bounds: Sequence[float]
min: float
max: float
exemplars: Sequence[Exemplar] = field(default_factory=list)
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(asdict(self), indent=indent)
@@ -80,7 +69,7 @@ class ExponentialHistogramDataPoint:
start_time_unix_nano: int
time_unix_nano: int
count: int
- sum: Union[int, float]
+ sum: int | float
scale: int
zero_count: int
positive: Buckets
@@ -90,7 +79,7 @@ class ExponentialHistogramDataPoint:
max: float
exemplars: Sequence[Exemplar] = field(default_factory=list)
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(asdict(self), indent=indent)
@@ -105,7 +94,7 @@ class ExponentialHistogram:
"opentelemetry.sdk.metrics.export.AggregationTemporality"
)
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(
{
"data_points": [
@@ -129,7 +118,7 @@ class Sum:
)
is_monotonic: bool
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(
{
"data_points": [
@@ -151,7 +140,7 @@ class Gauge:
data_points: Sequence[NumberDataPoint]
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(
{
"data_points": [
@@ -173,7 +162,7 @@ class Histogram:
"opentelemetry.sdk.metrics.export.AggregationTemporality"
)
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(
{
"data_points": [
@@ -187,10 +176,10 @@ class Histogram:
# pylint: disable=invalid-name
-DataT = Union[Sum, Gauge, Histogram, ExponentialHistogram]
-DataPointT = Union[
- NumberDataPoint, HistogramDataPoint, ExponentialHistogramDataPoint
-]
+DataT = Sum | Gauge | Histogram | ExponentialHistogram
+DataPointT = (
+ NumberDataPoint | HistogramDataPoint | ExponentialHistogramDataPoint
+)
@dataclass(frozen=True)
@@ -199,11 +188,11 @@ class Metric:
exported."""
name: str
- description: Optional[str]
- unit: Optional[str]
+ description: str | None
+ unit: str | None
data: DataT
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(
{
"name": self.name,
@@ -223,7 +212,7 @@ class ScopeMetrics:
metrics: Sequence[Metric]
schema_url: str
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(
{
"scope": loads(self.scope.to_json(indent=indent)),
@@ -245,7 +234,7 @@ class ResourceMetrics:
scope_metrics: Sequence[ScopeMetrics]
schema_url: str
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(
{
"resource": loads(self.resource.to_json(indent=indent)),
@@ -265,7 +254,7 @@ class MetricsData:
resource_metrics: Sequence[ResourceMetrics]
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(
{
"resource_metrics": [
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/sdk_configuration.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/sdk_configuration.py
index f5d176d..6c2b8de 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/sdk_configuration.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/sdk_configuration.py
@@ -1,21 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=unused-import
+from collections.abc import Sequence
from dataclasses import dataclass
-from typing import Sequence
# This kind of import is needed to avoid Sphinx errors.
import opentelemetry.sdk.metrics
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/view.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/view.py
index b3fa029..7eb1fcc 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/view.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/view.py
@@ -1,21 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
+from collections.abc import Callable
from fnmatch import fnmatch
from logging import getLogger
-from typing import Callable, Optional, Set, Type
from opentelemetry.metrics import Instrument
from opentelemetry.sdk.metrics._internal.aggregation import (
@@ -30,12 +19,13 @@ from opentelemetry.sdk.metrics._internal.exemplar import (
ExemplarReservoirBuilder,
SimpleFixedSizeExemplarReservoir,
)
+from opentelemetry.sdk.metrics._internal.instrument import _Instrument
_logger = getLogger(__name__)
def _default_reservoir_factory(
- aggregation_type: Type[_Aggregation],
+ aggregation_type: type[_Aggregation],
) -> ExemplarReservoirBuilder:
"""Default reservoir factory per aggregation."""
if issubclass(aggregation_type, _ExplicitBucketHistogramAggregation):
@@ -105,19 +95,20 @@ class View:
def __init__(
self,
- instrument_type: Optional[Type[Instrument]] = None,
- instrument_name: Optional[str] = None,
- meter_name: Optional[str] = None,
- meter_version: Optional[str] = None,
- meter_schema_url: Optional[str] = None,
- name: Optional[str] = None,
- description: Optional[str] = None,
- attribute_keys: Optional[Set[str]] = None,
- aggregation: Optional[Aggregation] = None,
- exemplar_reservoir_factory: Optional[
- Callable[[Type[_Aggregation]], ExemplarReservoirBuilder]
- ] = None,
- instrument_unit: Optional[str] = None,
+ instrument_type: type[Instrument] | None = None,
+ instrument_name: str | None = None,
+ meter_name: str | None = None,
+ meter_version: str | None = None,
+ meter_schema_url: str | None = None,
+ name: str | None = None,
+ description: str | None = None,
+ attribute_keys: set[str] | None = None,
+ aggregation: Aggregation | None = None,
+ exemplar_reservoir_factory: Callable[
+ [type[_Aggregation]], ExemplarReservoirBuilder
+ ]
+ | None = None,
+ instrument_unit: str | None = None,
):
if (
instrument_type
@@ -164,7 +155,7 @@ class View:
# pylint: disable=too-many-return-statements
# pylint: disable=too-many-branches
- def _match(self, instrument: Instrument) -> bool:
+ def _match(self, instrument: _Instrument) -> bool:
if self._instrument_type is not None:
if not isinstance(instrument, self._instrument_type):
return False
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py
index 1b6d27e..56034c0 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.sdk.metrics._internal.aggregation import (
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/view/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/view/__init__.py
index c07adf6..3aeb664 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/view/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/view/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.sdk.metrics._internal.aggregation import (
Aggregation,
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
index a04d27e..c559390 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
This package implements `OpenTelemetry Resources
@@ -67,10 +56,11 @@ import platform
import socket
import sys
import typing
+from collections.abc import Sequence
from json import dumps
from os import environ
from types import ModuleType
-from typing import List, Optional, Set, cast
+from typing import cast
from urllib import parse
from opentelemetry.attributes import BoundedAttributes
@@ -79,14 +69,13 @@ from opentelemetry.sdk.environment_variables import (
OTEL_RESOURCE_ATTRIBUTES,
OTEL_SERVICE_NAME,
)
-from opentelemetry.semconv.resource import ResourceAttributes
-from opentelemetry.util._importlib_metadata import (
- entry_points, # type: ignore[reportUnknownVariableType]
- version,
+from opentelemetry.sdk.version import (
+ __version__ as _OPENTELEMETRY_SDK_VERSION,
)
+from opentelemetry.semconv.resource import ResourceAttributes
from opentelemetry.util.types import AttributeValue
-psutil: Optional[ModuleType] = None
+psutil: ModuleType | None = None
try:
import psutil as psutil_module
@@ -158,8 +147,6 @@ TELEMETRY_SDK_VERSION = ResourceAttributes.TELEMETRY_SDK_VERSION
TELEMETRY_AUTO_VERSION = ResourceAttributes.TELEMETRY_AUTO_VERSION
TELEMETRY_SDK_LANGUAGE = ResourceAttributes.TELEMETRY_SDK_LANGUAGE
-_OPENTELEMETRY_SDK_VERSION: str = version("opentelemetry-sdk")
-
class Resource:
"""A Resource is an immutable representation of the entity producing telemetry as Attributes."""
@@ -167,9 +154,7 @@ class Resource:
_attributes: BoundedAttributes
_schema_url: str
- def __init__(
- self, attributes: Attributes, schema_url: typing.Optional[str] = None
- ):
+ def __init__(self, attributes: Attributes, schema_url: str | None = None):
self._attributes = BoundedAttributes(attributes=attributes)
if schema_url is None:
schema_url = ""
@@ -177,8 +162,8 @@ class Resource:
@staticmethod
def create(
- attributes: typing.Optional[Attributes] = None,
- schema_url: typing.Optional[str] = None,
+ attributes: Attributes | None = None,
+ schema_url: str | None = None,
) -> "Resource":
"""Creates a new `Resource` from attributes.
@@ -195,49 +180,14 @@ class Resource:
if not attributes:
attributes = {}
- otel_experimental_resource_detectors: Set[str] = {"otel"}.union(
- {
- otel_experimental_resource_detector.strip()
- for otel_experimental_resource_detector in environ.get(
- OTEL_EXPERIMENTAL_RESOURCE_DETECTORS, ""
- ).split(",")
- if otel_experimental_resource_detector
- }
- )
-
- resource_detectors: List[ResourceDetector] = []
-
- if "*" in otel_experimental_resource_detectors:
- otel_experimental_resource_detectors = entry_points(
- group="opentelemetry_resource_detector"
- ).names
-
- for resource_detector in otel_experimental_resource_detectors:
- try:
- resource_detectors.append(
- next(
- iter(
- entry_points(
- group="opentelemetry_resource_detector",
- name=resource_detector.strip(),
- ) # type: ignore[reportUnknownArgumentType]
- )
- ).load()()
- )
- except Exception: # pylint: disable=broad-exception-caught
- logger.exception(
- "Failed to load resource detector '%s', skipping",
- resource_detector,
- )
- continue
resource = get_aggregated_resources(
- resource_detectors, _DEFAULT_RESOURCE
+ _build_resource_detectors(), _DEFAULT_RESOURCE
).merge(Resource(attributes, schema_url))
if not resource.attributes.get(SERVICE_NAME, None):
default_service_name = "unknown_service"
process_executable_name = cast(
- Optional[str],
+ str | None,
resource.attributes.get(PROCESS_EXECUTABLE_NAME, None),
)
if process_executable_name:
@@ -307,7 +257,7 @@ class Resource:
f"{dumps(self._attributes.copy(), sort_keys=True)}|{self._schema_url}"
)
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(
{
"attributes": dict(self.attributes),
@@ -318,6 +268,7 @@ class Resource:
_EMPTY_RESOURCE = Resource({})
+
_DEFAULT_RESOURCE = Resource(
{
TELEMETRY_SDK_LANGUAGE: "python",
@@ -380,9 +331,16 @@ class ProcessResourceDetector(ResourceDetector):
_process_pid = os.getpid()
_process_executable_name = sys.executable
_process_executable_path = os.path.dirname(_process_executable_name)
- _process_command = sys.argv[0]
- _process_command_line = " ".join(sys.argv)
- _process_command_args = sys.argv
+ # Use sys.orig_argv, which preserves the original arguments received
+ # by the interpreter. This correctly captures ``python -m <module>``
+ # invocations where sys.argv is rewritten to the resolved module path
+ # and the ``-m <module>`` information is lost. sys.orig_argv also
+ # aligns with /proc/<pid>/cmdline, which the OTel semantic
+ # conventions reference for these attributes.
+ _process_argv = list(sys.orig_argv)
+ _process_command = _process_argv[0] if _process_argv else ""
+ _process_command_line = " ".join(_process_argv)
+ _process_command_args = _process_argv
resource_info = {
PROCESS_RUNTIME_DESCRIPTION: sys.version,
PROCESS_RUNTIME_NAME: sys.implementation.name,
@@ -504,9 +462,76 @@ class _HostResourceDetector(ResourceDetector): # type: ignore[reportUnusedClass
)
+def _build_resource_detectors() -> list["ResourceDetector"]:
+ """Returns the ordered list of resource detectors to use for Resource.create.
+
+ Fast path: if no extra detectors are configured, returns only
+ OTELResourceDetector without scanning entry_points.
+
+ "otel" (OTELResourceDetector) defaults to last position so that
+ OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME take highest merge priority,
+ but an explicit position in OTEL_EXPERIMENTAL_RESOURCE_DETECTORS is respected.
+ """
+ detector_names: list[str] = list(
+ dict.fromkeys(
+ [
+ name.strip()
+ for name in environ.get(
+ OTEL_EXPERIMENTAL_RESOURCE_DETECTORS, ""
+ ).split(",")
+ if name.strip()
+ ]
+ + ["otel"]
+ )
+ )
+
+ # Fast path: only the built-in "otel" detector — no entry_points scan needed.
+ if detector_names == ["otel"]:
+ return [OTELResourceDetector()]
+
+ # pylint: disable=import-outside-toplevel
+ from opentelemetry.util._importlib_metadata import ( # noqa: PLC0415
+ entry_points, # type: ignore[reportUnknownVariableType]
+ )
+
+ if "*" in detector_names:
+ registered = set(
+ name
+ for name in entry_points(
+ group="opentelemetry_resource_detector"
+ ).names # type: ignore[reportUnknownArgumentType]
+ if name != "otel"
+ )
+ expansion = sorted(registered - set(detector_names))
+ idx = detector_names.index("*")
+ detector_names = (
+ detector_names[:idx] + expansion + detector_names[idx + 1 :]
+ )
+
+ detectors: list[ResourceDetector] = []
+ for name in detector_names:
+ try:
+ detectors.append(
+ next(
+ iter(
+ entry_points(
+ group="opentelemetry_resource_detector",
+ name=name,
+ ) # type: ignore[reportUnknownArgumentType]
+ )
+ ).load()()
+ )
+ except Exception: # pylint: disable=broad-exception-caught
+ logger.exception(
+ "Failed to load resource detector '%s', skipping",
+ name,
+ )
+ return detectors
+
+
def get_aggregated_resources(
- detectors: typing.List["ResourceDetector"],
- initial_resource: typing.Optional[Resource] = None,
+ detectors: Sequence["ResourceDetector"],
+ initial_resource: Resource | None = None,
timeout: int = 5,
) -> "Resource":
"""Retrieves resources from detectors in the order that they were passed
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
index 18fced7..3817408 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-lines
import abc
@@ -23,22 +12,19 @@ import threading
import traceback
import typing
import weakref
+from collections.abc import (
+ Callable,
+ Iterator,
+ Mapping,
+ MutableMapping,
+ Sequence,
+)
from dataclasses import dataclass
from os import environ
from time import time_ns
from types import MappingProxyType, TracebackType
from typing import (
Any,
- Callable,
- Dict,
- Iterator,
- List,
- Mapping,
- MutableMapping,
- Optional,
- Sequence,
- Type,
- Union,
)
from warnings import filterwarnings
@@ -54,15 +40,19 @@ from opentelemetry.sdk.environment_variables import (
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT,
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
OTEL_SDK_DISABLED,
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT,
OTEL_SPAN_EVENT_COUNT_LIMIT,
OTEL_SPAN_LINK_COUNT_LIMIT,
)
+from opentelemetry.sdk.environment_variables._internal import (
+ parse_boolean_environment_variable,
+)
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import sampling
-from opentelemetry.sdk.trace._tracer_metrics import TracerMetrics
+from opentelemetry.sdk.trace._tracer_metrics import create_tracer_metrics
from opentelemetry.sdk.trace.id_generator import IdGenerator, RandomIdGenerator
from opentelemetry.sdk.util import BoundedList
from opentelemetry.sdk.util._configurator import RuleBasedConfigurator
@@ -106,7 +96,7 @@ class SpanProcessor:
def on_start(
self,
span: "Span",
- parent_context: Optional[context_api.Context] = None,
+ parent_context: context_api.Context | None = None,
) -> None:
"""Called when a :class:`opentelemetry.trace.Span` is started.
@@ -141,7 +131,7 @@ class SpanProcessor:
def shutdown(self) -> None:
"""Called when a :class:`opentelemetry.sdk.trace.TracerProvider` is shutdown."""
- def force_flush(self, timeout_millis: int = 30000) -> bool: # type: ignore[reportReturnType]
+ def force_flush(self, timeout_millis: int = 30000) -> bool: # pylint: disable=no-self-use
"""Export all ended spans to the configured Exporter that have not yet
been exported.
@@ -152,6 +142,7 @@ class SpanProcessor:
Returns:
False if the timeout is exceeded, True otherwise.
"""
+ return True
# Temporary fix until https://github.com/PyCQA/pylint/issues/4098 is resolved
@@ -180,7 +171,7 @@ class SynchronousMultiSpanProcessor(SpanProcessor):
def on_start(
self,
span: "Span",
- parent_context: Optional[context_api.Context] = None,
+ parent_context: context_api.Context | None = None,
) -> None:
for sp in self._span_processors:
sp.on_start(span, parent_context=parent_context)
@@ -214,15 +205,19 @@ class SynchronousMultiSpanProcessor(SpanProcessor):
given timeout, False otherwise.
"""
deadline_ns = time_ns() + timeout_millis * 1000000
+ all_flushed = True
for sp in self._span_processors:
current_time_ns = time_ns()
if current_time_ns >= deadline_ns:
return False
- if not sp.force_flush((deadline_ns - current_time_ns) // 1000000):
- return False
+ if (
+ sp.force_flush((deadline_ns - current_time_ns) // 1000000)
+ is False
+ ):
+ all_flushed = False
- return True
+ return all_flushed
class ConcurrentMultiSpanProcessor(SpanProcessor):
@@ -284,7 +279,7 @@ class ConcurrentMultiSpanProcessor(SpanProcessor):
def on_start(
self,
span: "Span",
- parent_context: Optional[context_api.Context] = None,
+ parent_context: context_api.Context | None = None,
) -> None:
self._submit_and_await(
lambda sp: sp.on_start, span, parent_context=parent_context
@@ -325,14 +320,14 @@ class ConcurrentMultiSpanProcessor(SpanProcessor):
return False
for future in done_futures:
- if not future.result():
+ if future.result() is False:
return False
return True
class EventBase(abc.ABC):
- def __init__(self, name: str, timestamp: Optional[int] = None) -> None:
+ def __init__(self, name: str, timestamp: int | None = None) -> None:
self._name = name
if timestamp is None:
self._timestamp = time_ns()
@@ -368,8 +363,8 @@ class Event(EventBase):
self,
name: str,
attributes: types.Attributes = None,
- timestamp: Optional[int] = None,
- limit: Optional[int] = _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
+ timestamp: int | None = None,
+ limit: int | None = _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
) -> None:
super().__init__(name, timestamp)
self._attributes = attributes
@@ -417,18 +412,18 @@ class ReadableSpan:
def __init__(
self,
name: str,
- context: Optional[trace_api.SpanContext] = None,
- parent: Optional[trace_api.SpanContext] = None,
- resource: Optional[Resource] = None,
+ context: trace_api.SpanContext | None = None,
+ parent: trace_api.SpanContext | None = None,
+ resource: Resource | None = None,
attributes: types.Attributes = None,
events: Sequence[Event] = (),
links: Sequence[trace_api.Link] = (),
kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL,
- instrumentation_info: Optional[InstrumentationInfo] = None,
+ instrumentation_info: InstrumentationInfo | None = None,
status: Status = Status(StatusCode.UNSET),
- start_time: Optional[int] = None,
- end_time: Optional[int] = None,
- instrumentation_scope: Optional[InstrumentationScope] = None,
+ start_time: int | None = None,
+ end_time: int | None = None,
+ instrumentation_scope: InstrumentationScope | None = None,
) -> None:
self._name = name
self._context = context
@@ -469,7 +464,7 @@ class ReadableSpan:
def name(self) -> str:
return self._name
- def get_span_context(self) -> Optional[trace_api.SpanContext]:
+ def get_span_context(self) -> trace_api.SpanContext | None:
return self._context
@property
@@ -481,15 +476,15 @@ class ReadableSpan:
return self._kind
@property
- def parent(self) -> Optional[trace_api.SpanContext]:
+ def parent(self) -> trace_api.SpanContext | None:
return self._parent
@property
- def start_time(self) -> Optional[int]:
+ def start_time(self) -> int | None:
return self._start_time
@property
- def end_time(self) -> Optional[int]:
+ def end_time(self) -> int | None:
return self._end_time
@property
@@ -516,14 +511,14 @@ class ReadableSpan:
@deprecated(
"You should use instrumentation_scope. Deprecated since version 1.11.1."
)
- def instrumentation_info(self) -> Optional[InstrumentationInfo]:
+ def instrumentation_info(self) -> InstrumentationInfo | None:
return self._instrumentation_info
@property
- def instrumentation_scope(self) -> Optional[InstrumentationScope]:
+ def instrumentation_scope(self) -> InstrumentationScope | None:
return self._instrumentation_scope
- def to_json(self, indent: Optional[int] = 4):
+ def to_json(self, indent: int | None = 4):
parent_id = None
if self.parent is not None:
parent_id = f"0x{trace_api.format_span_id(self.parent.span_id)}"
@@ -561,7 +556,7 @@ class ReadableSpan:
return json.dumps(f_span, indent=indent)
@staticmethod
- def _format_context(context: SpanContext) -> Dict[str, str]:
+ def _format_context(context: SpanContext) -> dict[str, str]:
return {
"trace_id": f"0x{trace_api.format_trace_id(context.trace_id)}",
"span_id": f"0x{trace_api.format_span_id(context.span_id)}",
@@ -571,13 +566,13 @@ class ReadableSpan:
@staticmethod
def _format_attributes(
attributes: types.Attributes,
- ) -> Optional[Dict[str, Any]]:
+ ) -> dict[str, Any] | None:
if attributes is not None and not isinstance(attributes, dict):
return dict(attributes)
return attributes
@staticmethod
- def _format_events(events: Sequence[Event]) -> List[Dict[str, Any]]:
+ def _format_events(events: Sequence[Event]) -> list[dict[str, Any]]:
return [
{
"name": event.name,
@@ -590,7 +585,7 @@ class ReadableSpan:
]
@staticmethod
- def _format_links(links: Sequence[trace_api.Link]) -> List[Dict[str, Any]]:
+ def _format_links(links: Sequence[trace_api.Link]) -> list[dict[str, Any]]:
return [
{
"context": Span._format_context( # pylint: disable=protected-access
@@ -651,14 +646,14 @@ class SpanLimits:
def __init__(
self,
- max_attributes: Optional[int] = None,
- max_events: Optional[int] = None,
- max_links: Optional[int] = None,
- max_span_attributes: Optional[int] = None,
- max_event_attributes: Optional[int] = None,
- max_link_attributes: Optional[int] = None,
- max_attribute_length: Optional[int] = None,
- max_span_attribute_length: Optional[int] = None,
+ max_attributes: int | None = None,
+ max_events: int | None = None,
+ max_links: int | None = None,
+ max_span_attributes: int | None = None,
+ max_event_attributes: int | None = None,
+ max_link_attributes: int | None = None,
+ max_attribute_length: int | None = None,
+ max_span_attribute_length: int | None = None,
):
# span events and links count
self.max_events = self._from_env_if_absent(
@@ -727,8 +722,8 @@ class SpanLimits:
@classmethod
def _from_env_if_absent(
- cls, value: Optional[int], env_var: str, default: Optional[int] = None
- ) -> Optional[int]:
+ cls, value: int | None, env_var: str, default: int | None = None
+ ) -> int | None:
if value == cls.UNSET:
return None
@@ -807,22 +802,22 @@ class Span(trace_api.Span, ReadableSpan):
self,
name: str,
context: trace_api.SpanContext,
- parent: Optional[trace_api.SpanContext] = None,
- sampler: Optional[sampling.Sampler] = None,
+ parent: trace_api.SpanContext | None = None,
+ sampler: sampling.Sampler | None = None,
trace_config: None = None, # TODO
- resource: Optional[Resource] = None,
+ resource: Resource | None = None,
attributes: types.Attributes = None,
- events: Optional[Sequence[Event]] = None,
+ events: Sequence[Event] | None = None,
links: Sequence[trace_api.Link] = (),
kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL,
span_processor: SpanProcessor = SpanProcessor(),
- instrumentation_info: Optional[InstrumentationInfo] = None,
+ instrumentation_info: InstrumentationInfo | None = None,
record_exception: bool = True,
set_status_on_exception: bool = True,
limits=_UnsetLimits,
- instrumentation_scope: Optional[InstrumentationScope] = None,
+ instrumentation_scope: InstrumentationScope | None = None,
*,
- record_end_metrics: Optional[Callable[[], None]] = None,
+ record_end_metrics: Callable[[], None] | None = None,
) -> None:
if resource is None:
resource = Resource.create({})
@@ -910,7 +905,7 @@ class Span(trace_api.Span, ReadableSpan):
self,
name: str,
attributes: types.Attributes = None,
- timestamp: Optional[int] = None,
+ timestamp: int | None = None,
) -> None:
attributes = BoundedAttributes(
self._limits.max_event_attributes,
@@ -968,8 +963,8 @@ class Span(trace_api.Span, ReadableSpan):
def start(
self,
- start_time: Optional[int] = None,
- parent_context: Optional[context_api.Context] = None,
+ start_time: int | None = None,
+ parent_context: context_api.Context | None = None,
) -> None:
with self._lock:
if self._start_time is not None:
@@ -981,7 +976,7 @@ class Span(trace_api.Span, ReadableSpan):
self._span_processor.on_start(self, parent_context=parent_context)
- def end(self, end_time: Optional[int] = None) -> None:
+ def end(self, end_time: int | None = None) -> None:
with self._lock:
if self._start_time is None:
raise RuntimeError("Calling end() on a not started span.")
@@ -1007,8 +1002,8 @@ class Span(trace_api.Span, ReadableSpan):
@_check_span_ended
def set_status(
self,
- status: typing.Union[Status, StatusCode],
- description: typing.Optional[str] = None,
+ status: Status | StatusCode,
+ description: str | None = None,
) -> None:
# Ignore future calls if status is already set to OK
# Ignore calls to set to StatusCode.UNSET
@@ -1036,9 +1031,9 @@ class Span(trace_api.Span, ReadableSpan):
def __exit__(
self,
- exc_type: Optional[Type[BaseException]],
- exc_val: Optional[BaseException],
- exc_tb: Optional[TracebackType],
+ exc_type: type[BaseException] | None,
+ exc_val: BaseException | None,
+ exc_tb: TracebackType | None,
) -> None:
"""Ends context manager and calls `end` on the `Span`."""
if exc_val is not None and self.is_recording():
@@ -1062,7 +1057,7 @@ class Span(trace_api.Span, ReadableSpan):
self,
exception: BaseException,
attributes: types.Attributes = None,
- timestamp: Optional[int] = None,
+ timestamp: int | None = None,
escaped: bool = False,
) -> None:
"""Records an exception as a span event."""
@@ -1116,16 +1111,15 @@ class Tracer(trace_api.Tracer):
self,
sampler: sampling.Sampler,
resource: Resource,
- span_processor: Union[
- SynchronousMultiSpanProcessor, ConcurrentMultiSpanProcessor
- ],
+ span_processor: SynchronousMultiSpanProcessor
+ | ConcurrentMultiSpanProcessor,
id_generator: IdGenerator,
instrumentation_info: InstrumentationInfo,
span_limits: SpanLimits,
instrumentation_scope: InstrumentationScope,
*,
- meter_provider: Optional[metrics_api.MeterProvider] = None,
- _tracer_config: Optional[_TracerConfig] = None,
+ meter_provider: metrics_api.MeterProvider | None = None,
+ _tracer_config: _TracerConfig | None = None,
) -> None:
self.sampler = sampler
self.resource = resource
@@ -1137,7 +1131,12 @@ class Tracer(trace_api.Tracer):
self._tracer_config = _tracer_config or _TracerConfig.default()
meter_provider = meter_provider or metrics_api.get_meter_provider()
- self._tracer_metrics = TracerMetrics(meter_provider)
+ self._tracer_metrics = create_tracer_metrics(
+ meter_provider,
+ parse_boolean_environment_variable(
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED
+ ),
+ )
def _set_tracer_config(self, tracer_config: _TracerConfig):
self._tracer_config = tracer_config
@@ -1150,11 +1149,11 @@ class Tracer(trace_api.Tracer):
def start_as_current_span(
self,
name: str,
- context: Optional[context_api.Context] = None,
+ context: context_api.Context | None = None,
kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL,
attributes: types.Attributes = None,
- links: Optional[Sequence[trace_api.Link]] = (),
- start_time: Optional[int] = None,
+ links: Sequence[trace_api.Link] | None = (),
+ start_time: int | None = None,
record_exception: bool = True,
set_status_on_exception: bool = True,
end_on_exit: bool = True,
@@ -1180,11 +1179,11 @@ class Tracer(trace_api.Tracer):
def start_span( # pylint: disable=too-many-locals
self,
name: str,
- context: Optional[context_api.Context] = None,
+ context: context_api.Context | None = None,
kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL,
attributes: types.Attributes = None,
- links: Optional[Sequence[trace_api.Link]] = (),
- start_time: Optional[int] = None,
+ links: Sequence[trace_api.Link] | None = (),
+ start_time: int | None = None,
record_exception: bool = True,
set_status_on_exception: bool = True,
) -> trace_api.Span:
@@ -1225,6 +1224,17 @@ class Tracer(trace_api.Tracer):
if sampling_result.decision.is_sampled()
else trace_api.TraceFlags(trace_api.TraceFlags.DEFAULT)
)
+
+ if parent_span_context is None:
+ random_trace_id = self.id_generator.is_trace_id_random()
+ else:
+ random_trace_id = parent_span_context.trace_flags.random_trace_id
+
+ if random_trace_id:
+ trace_flags = trace_api.TraceFlags(
+ trace_flags | trace_api.TraceFlags.RANDOM_TRACE_ID
+ )
+
span_context = trace_api.SpanContext(
trace_id,
self.id_generator.generate_span_id(),
@@ -1295,17 +1305,17 @@ class TracerProvider(trace_api.TracerProvider):
def __init__(
self,
- sampler: Optional[sampling.Sampler] = None,
- resource: Optional[Resource] = None,
+ sampler: sampling.Sampler | None = None,
+ resource: Resource | None = None,
shutdown_on_exit: bool = True,
- active_span_processor: Union[
- SynchronousMultiSpanProcessor, ConcurrentMultiSpanProcessor, None
- ] = None,
- id_generator: Optional[IdGenerator] = None,
- span_limits: Optional[SpanLimits] = None,
+ active_span_processor: SynchronousMultiSpanProcessor
+ | ConcurrentMultiSpanProcessor
+ | None = None,
+ id_generator: IdGenerator | None = None,
+ span_limits: SpanLimits | None = None,
*,
- meter_provider: Optional[metrics_api.MeterProvider] = None,
- _tracer_configurator: Optional[_TracerConfiguratorT] = None,
+ meter_provider: metrics_api.MeterProvider | None = None,
+ _tracer_configurator: _TracerConfiguratorT | None = None,
) -> None:
self._active_span_processor = (
active_span_processor or SynchronousMultiSpanProcessor()
@@ -1372,9 +1382,9 @@ class TracerProvider(trace_api.TracerProvider):
def get_tracer(
self,
instrumenting_module_name: str,
- instrumenting_library_version: typing.Optional[str] = None,
- schema_url: typing.Optional[str] = None,
- attributes: typing.Optional[types.Attributes] = None,
+ instrumenting_library_version: str | None = None,
+ schema_url: str | None = None,
+ attributes: types.Attributes | None = None,
) -> "trace_api.Tracer":
if self._disabled:
return NoOpTracer()
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/__init__.py
index 4dc08da..e838448 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
__all__ = [
"ComposableSampler",
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_always_off.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_always_off.py
index eaafe16..7c5c4e1 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_always_off.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_always_off.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from opentelemetry.context import Context
from opentelemetry.trace import Link, SpanKind, TraceState
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_always_on.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_always_on.py
index 88ac61c..fd2dbdb 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_always_on.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_always_on.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from opentelemetry.context import Context
from opentelemetry.trace import Link, SpanKind, TraceState
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_composable.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_composable.py
index 5829601..2651599 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_composable.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_composable.py
@@ -1,21 +1,11 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
+from collections.abc import Callable, Sequence
from dataclasses import dataclass, field
-from typing import Callable, Protocol, Sequence
+from typing import Protocol
from opentelemetry.context import Context
from opentelemetry.trace import Link, SpanKind, TraceState
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_parent_threshold.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_parent_threshold.py
index 83b7b7d..69e10f5 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_parent_threshold.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_parent_threshold.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from opentelemetry.context import Context
from opentelemetry.trace import Link, SpanKind, TraceState, get_current_span
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_rule_based.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_rule_based.py
index f03e308..747c73a 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_rule_based.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_rule_based.py
@@ -1,20 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
-from typing import Protocol, Sequence
+from collections.abc import Sequence
+from typing import Protocol
from opentelemetry.context import Context
from opentelemetry.trace import Link, SpanKind, TraceState
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_sampler.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_sampler.py
index 989cc36..7a9ec41 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_sampler.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_sampler.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from opentelemetry.context import Context
from opentelemetry.sdk.trace.sampling import Decision, Sampler, SamplingResult
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_trace_state.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_trace_state.py
index bc06420..ea0a4fb 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_trace_state.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_trace_state.py
@@ -1,21 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
+from collections.abc import Sequence
from dataclasses import dataclass
-from typing import Sequence
from opentelemetry.trace import TraceState
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_traceid_ratio.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_traceid_ratio.py
index d63b6f8..951d305 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_traceid_ratio.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_traceid_ratio.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from opentelemetry.context import Context
from opentelemetry.trace import Link, SpanKind, TraceState
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_util.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_util.py
index 4e9fd7d..7a5598d 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_util.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_sampling_experimental/_util.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
RANDOM_VALUE_BITS = 56
MAX_THRESHOLD = 1 << RANDOM_VALUE_BITS # 0% sampling
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_tracer_metrics.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_tracer_metrics.py
index ad7de33..480d7b5 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/_tracer_metrics.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/_tracer_metrics.py
@@ -1,20 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
from collections.abc import Callable
+from typing import Protocol
from opentelemetry import metrics as metrics_api
from opentelemetry.sdk.trace.sampling import Decision
@@ -30,6 +20,24 @@ from opentelemetry.semconv._incubating.metrics.otel_metrics import (
from opentelemetry.trace.span import SpanContext
+class TracerMetricsT(Protocol):
+ def start_span(
+ self,
+ parent_span_context: SpanContext | None,
+ sampling_decision: Decision,
+ ) -> Callable[[], None]: ...
+
+
+# pylint: disable=no-self-use
+class NoOpTracerMetrics:
+ def start_span(
+ self,
+ parent_span_context: SpanContext | None,
+ sampling_decision: Decision,
+ ) -> Callable[[], None]:
+ return noop
+
+
class TracerMetrics:
def __init__(self, meter_provider: metrics_api.MeterProvider) -> None:
meter = meter_provider.get_meter("opentelemetry-sdk")
@@ -65,6 +73,16 @@ class TracerMetrics:
return end_span
+def create_tracer_metrics(
+ meter_provider: metrics_api.MeterProvider,
+ enabled: bool,
+) -> TracerMetricsT:
+ if not enabled:
+ return NoOpTracerMetrics()
+
+ return TracerMetrics(meter_provider)
+
+
def noop() -> None:
pass
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py
index 8cf9c5e..d147784 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
import logging
@@ -27,12 +16,21 @@ from opentelemetry.context import (
set_value,
)
from opentelemetry.metrics import MeterProvider, get_meter_provider
-from opentelemetry.sdk._shared_internal import BatchProcessor, ProcessorMetrics
+from opentelemetry.sdk._shared_internal import (
+ BatchProcessor,
+)
+from opentelemetry.sdk._shared_internal._processor_metrics import (
+ create_processor_metrics,
+)
from opentelemetry.sdk.environment_variables import (
OTEL_BSP_EXPORT_TIMEOUT,
OTEL_BSP_MAX_EXPORT_BATCH_SIZE,
OTEL_BSP_MAX_QUEUE_SIZE,
OTEL_BSP_SCHEDULE_DELAY,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
+)
+from opentelemetry.sdk.environment_variables._internal import (
+ parse_boolean_environment_variable,
)
from opentelemetry.sdk.trace import ReadableSpan, Span, SpanProcessor
from opentelemetry.semconv._incubating.attributes.otel_attributes import (
@@ -102,14 +100,17 @@ class SimpleSpanProcessor(SpanProcessor):
meter_provider: MeterProvider | None = None,
):
self.span_exporter = span_exporter
- self._metrics = ProcessorMetrics(
+ self._metrics = create_processor_metrics(
"traces",
OtelComponentTypeValues.SIMPLE_SPAN_PROCESSOR,
meter_provider or get_meter_provider(),
+ enabled=parse_boolean_environment_variable(
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED
+ ),
)
def on_start(
- self, span: Span, parent_context: typing.Optional[Context] = None
+ self, span: Span, parent_context: Context | None = None
) -> None:
pass
@@ -196,11 +197,14 @@ class BatchSpanProcessor(SpanProcessor):
export_timeout_millis,
max_queue_size,
"Span",
- ProcessorMetrics(
+ create_processor_metrics(
"traces",
OtelComponentTypeValues.BATCHING_SPAN_PROCESSOR,
meter_provider or get_meter_provider(),
capacity=max_queue_size,
+ enabled=parse_boolean_environment_variable(
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED
+ ),
),
)
@@ -225,7 +229,7 @@ class BatchSpanProcessor(SpanProcessor):
def shutdown(self):
return self._batch_processor.shutdown()
- def force_flush(self, timeout_millis: typing.Optional[int] = None) -> bool:
+ def force_flush(self, timeout_millis: int | None = None) -> bool:
return self._batch_processor.force_flush(timeout_millis)
@staticmethod
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py
index c28ecfd..25a6629 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import threading
import typing
@@ -28,7 +17,7 @@ class InMemorySpanExporter(SpanExporter):
"""
def __init__(self) -> None:
- self._finished_spans: typing.List[ReadableSpan] = []
+ self._finished_spans: list[ReadableSpan] = []
self._stopped = False
self._lock = threading.Lock()
@@ -37,7 +26,7 @@ class InMemorySpanExporter(SpanExporter):
with self._lock:
self._finished_spans.clear()
- def get_finished_spans(self) -> typing.Tuple[ReadableSpan, ...]:
+ def get_finished_spans(self) -> tuple[ReadableSpan, ...]:
"""Get list of collected spans."""
with self._lock:
return tuple(self._finished_spans)
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/id_generator.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/id_generator.py
index cd1f89b..f23e333 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/id_generator.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/id_generator.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import abc
import random
@@ -31,16 +20,36 @@ class IdGenerator(abc.ABC):
def generate_trace_id(self) -> int:
"""Get a new trace ID.
- Implementations should at least make the 64 least significant bits
+ Implementations should at least make the 56 least significant bits
uniformly random. Samplers like the `TraceIdRatioBased` sampler rely on
this randomness to make sampling decisions.
+ If the implementation does randomly generate the 56 least significant bits,
+ it should also implement `is_trace_id_random` to return True.
+
See `the specification on TraceIdRatioBased <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#traceidratiobased>`_.
Returns:
A 128-bit int for use as a trace ID
"""
+ # pylint: disable=no-self-use
+ def is_trace_id_random(self) -> bool:
+ """Indicates whether generated trace IDs are random.
+
+ When True, the `trace-id` field will have the `random-trace-id` flag set
+ in the W3C traceparent header. Per the W3C Trace Context specification,
+ this indicates that at least the 7 rightmost bytes (56 bits) of the
+ trace ID were generated randomly with uniform distribution.
+
+ See `the W3C Trace Context specification <https://www.w3.org/TR/trace-context-2/#considerations-for-trace-id-field-generation>`_.
+
+ Returns:
+ True if this generator produces random IDs, False otherwise.
+ """
+ # By default, return False for backwards compatibility.
+ return False
+
class RandomIdGenerator(IdGenerator):
"""The default ID generator for TracerProvider which randomly generates all
@@ -58,3 +67,6 @@ class RandomIdGenerator(IdGenerator):
while trace_id == trace.INVALID_TRACE_ID:
trace_id = random.getrandbits(128)
return trace_id
+
+ def is_trace_id_random(self) -> bool:
+ return True
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/sampling.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/sampling.py
index 68466eb..27cd4a0 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/sampling.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/sampling.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
For general information about sampling, see `the specification <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampling>`_.
@@ -135,9 +124,10 @@ above sampler, set ``OTEL_TRACES_SAMPLER=custom_sampler_name`` and ``OTEL_TRACES
import abc
import enum
import os
+from collections.abc import Sequence
from logging import getLogger
from types import MappingProxyType
-from typing import Optional, Sequence
+from typing import Optional
# pylint: disable=unused-import
from opentelemetry.context import Context
@@ -202,9 +192,9 @@ class Sampler(abc.ABC):
parent_context: Optional["Context"],
trace_id: int,
name: str,
- kind: Optional[SpanKind] = None,
+ kind: SpanKind | None = None,
attributes: Attributes = None,
- links: Optional[Sequence["Link"]] = None,
+ links: Sequence["Link"] | None = None,
trace_state: Optional["TraceState"] = None,
) -> "SamplingResult":
pass
@@ -225,9 +215,9 @@ class StaticSampler(Sampler):
parent_context: Optional["Context"],
trace_id: int,
name: str,
- kind: Optional[SpanKind] = None,
+ kind: SpanKind | None = None,
attributes: Attributes = None,
- links: Optional[Sequence["Link"]] = None,
+ links: Sequence["Link"] | None = None,
trace_state: Optional["TraceState"] = None,
) -> "SamplingResult":
if self._decision is Decision.DROP:
@@ -286,9 +276,9 @@ class TraceIdRatioBased(Sampler):
parent_context: Optional["Context"],
trace_id: int,
name: str,
- kind: Optional[SpanKind] = None,
+ kind: SpanKind | None = None,
attributes: Attributes = None,
- links: Optional[Sequence["Link"]] = None,
+ links: Sequence["Link"] | None = None,
trace_state: Optional["TraceState"] = None,
) -> "SamplingResult":
decision = Decision.DROP
@@ -341,9 +331,9 @@ class ParentBased(Sampler):
parent_context: Optional["Context"],
trace_id: int,
name: str,
- kind: Optional[SpanKind] = None,
+ kind: SpanKind | None = None,
attributes: Attributes = None,
- links: Optional[Sequence["Link"]] = None,
+ links: Sequence["Link"] | None = None,
trace_state: Optional["TraceState"] = None,
) -> "SamplingResult":
parent_span_context = get_current_span(
@@ -445,7 +435,7 @@ def _get_from_env_or_default() -> Sampler:
def _get_parent_trace_state(
- parent_context: Optional[Context],
+ parent_context: Context | None,
) -> Optional["TraceState"]:
parent_span_context = get_current_span(parent_context).get_span_context()
if parent_span_context is None or not parent_span_context.is_valid:
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/util/__init__.py
index 4adf4ed..c753a78 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/util/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/util/__init__.py
@@ -1,23 +1,11 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import copy
import datetime
import threading
from collections import deque
from collections.abc import MutableMapping, Sequence
-from typing import Optional
from typing_extensions import deprecated
@@ -51,7 +39,7 @@ class BoundedList(Sequence):
not enough room.
"""
- def __init__(self, maxlen: Optional[int]):
+ def __init__(self, maxlen: int | None):
self.dropped = 0
self._dq = deque(maxlen=maxlen) # type: deque
self._lock = threading.Lock()
@@ -110,7 +98,7 @@ class BoundedDict(MutableMapping):
added.
"""
- def __init__(self, maxlen: Optional[int]):
+ def __init__(self, maxlen: int | None):
if maxlen is not None:
if not isinstance(maxlen, int):
raise ValueError
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util/__init__.pyi b/opentelemetry-sdk/src/opentelemetry/sdk/util/__init__.pyi
index 00d1e7c..53d372a 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/util/__init__.pyi
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/util/__init__.pyi
@@ -12,14 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from typing import (
- Any,
+from collections.abc import (
Iterable,
Iterator,
Mapping,
MutableMapping,
- Optional,
Sequence,
+)
+from typing import (
+ Any,
TypeVar,
overload,
)
@@ -44,7 +45,7 @@ class BoundedList(Sequence[_T]):
"""
dropped: int
- def __init__(self, maxlen: Optional[int]): ...
+ def __init__(self, maxlen: int | None): ...
def __deepcopy__(self, memo: dict[int, Any]) -> BoundedList[_T]: ...
def insert(self, index: int, value: _T) -> None: ...
@overload
@@ -56,7 +57,7 @@ class BoundedList(Sequence[_T]):
def extend(self, seq: Sequence[_T]) -> None: ...
@classmethod
def from_seq(
- cls, maxlen: Optional[int], seq: Iterable[_T]
+ cls, maxlen: int | None, seq: Iterable[_T]
) -> BoundedList[_T]: ... # pylint: disable=undefined-variable
class BoundedDict(MutableMapping[_KT, _VT]):
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util/_configurator.py b/opentelemetry-sdk/src/opentelemetry/sdk/util/_configurator.py
index c7c9e78..d431495 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/util/_configurator.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/util/_configurator.py
@@ -1,17 +1,7 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 Generic, Sequence, TypeVar
+# SPDX-License-Identifier: Apache-2.0
+from collections.abc import Sequence
+from typing import Generic, TypeVar
from opentelemetry.sdk.util.instrumentation import (
InstrumentationScope,
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py b/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py
index fd8af27..fc408e4 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py
@@ -1,24 +1,17 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import fnmatch
+from collections.abc import Callable
from json import dumps
-from typing import Callable, Optional
from typing_extensions import deprecated
from opentelemetry.attributes import BoundedAttributes
-from opentelemetry.util.types import Attributes, _ExtendedAttributes
+from opentelemetry.util.types import ( # TODO: see if we can remove F401 when using new sphinx version # noqa: F401 # pylint: disable=unused-import
+ AnyValue,
+ Attributes,
+ _ExtendedAttributes,
+)
class InstrumentationInfo:
@@ -36,8 +29,8 @@ class InstrumentationInfo:
def __init__(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
):
self._name = name
self._version = version
@@ -68,11 +61,11 @@ class InstrumentationInfo:
)
@property
- def schema_url(self) -> Optional[str]:
+ def schema_url(self) -> str | None:
return self._schema_url
@property
- def version(self) -> Optional[str]:
+ def version(self) -> str | None:
return self._version
@property
@@ -93,9 +86,9 @@ class InstrumentationScope:
def __init__(
self,
name: str,
- version: Optional[str] = None,
- schema_url: Optional[str] = None,
- attributes: Optional[_ExtendedAttributes] = None,
+ version: str | None = None,
+ schema_url: str | None = None,
+ attributes: _ExtendedAttributes | None = None,
) -> None:
self._name = name
self._version = version
@@ -141,11 +134,11 @@ class InstrumentationScope:
)
@property
- def schema_url(self) -> Optional[str]:
+ def schema_url(self) -> str | None:
return self._schema_url
@property
- def version(self) -> Optional[str]:
+ def version(self) -> str | None:
return self._version
@property
@@ -156,7 +149,7 @@ class InstrumentationScope:
def attributes(self) -> Attributes:
return self._attributes
- def to_json(self, indent: Optional[int] = 4) -> str:
+ def to_json(self, indent: int | None = 4) -> str:
return dumps(
{
"name": self._name,
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/version/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/version/__init__.py
index 49e1254..a400b9b 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/version/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.0"
+__version__ = "1.42.1"
diff --git a/opentelemetry-sdk/test-requirements.txt b/opentelemetry-sdk/test-requirements.txt
index 3b8f3d9..e4e7933 100644
--- a/opentelemetry-sdk/test-requirements.txt
+++ b/opentelemetry-sdk/test-requirements.txt
@@ -1,6 +1,5 @@
asgiref==3.7.2
flaky==3.7.0
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
@@ -8,6 +7,8 @@ psutil==7.2.2
py-cpuinfo==9.0.0
pytest==7.4.4
jsonschema==4.25.1
+rpds-py==0.23.1; python_version == "3.10"
+rpds-py==0.30.0; python_version >= "3.11"
pyyaml==6.0.3
tomli==2.0.1
typing_extensions==4.12.0
diff --git a/opentelemetry-sdk/tests/__init__.py b/opentelemetry-sdk/tests/__init__.py
index b0a6f42..e57cf4a 100644
--- a/opentelemetry-sdk/tests/__init__.py
+++ b/opentelemetry-sdk/tests/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/opentelemetry-sdk/tests/_configuration/__init__.py b/opentelemetry-sdk/tests/_configuration/__init__.py
index b0a6f42..e57cf4a 100644
--- a/opentelemetry-sdk/tests/_configuration/__init__.py
+++ b/opentelemetry-sdk/tests/_configuration/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/opentelemetry-sdk/tests/_configuration/file/__init__.py b/opentelemetry-sdk/tests/_configuration/file/__init__.py
index b0a6f42..e57cf4a 100644
--- a/opentelemetry-sdk/tests/_configuration/file/__init__.py
+++ b/opentelemetry-sdk/tests/_configuration/file/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/opentelemetry-sdk/tests/_configuration/file/test_env_substitution.py b/opentelemetry-sdk/tests/_configuration/file/test_env_substitution.py
index 1a8bdc9..fee46c9 100644
--- a/opentelemetry-sdk/tests/_configuration/file/test_env_substitution.py
+++ b/opentelemetry-sdk/tests/_configuration/file/test_env_substitution.py
@@ -1,21 +1,12 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import os
import unittest
from unittest.mock import patch
+import yaml
+
from opentelemetry.sdk._configuration.file import (
EnvSubstitutionError,
substitute_env_vars,
@@ -115,3 +106,45 @@ line2: value2"""
"""Test string with only escaped dollar signs."""
result = substitute_env_vars("$$$$")
self.assertEqual(result, "$$")
+
+ def test_newline_in_value_prevents_yaml_injection(self):
+ """Values containing newlines must not inject YAML structure.
+
+ Per spec: "It MUST NOT be possible to inject YAML structures by
+ environment variables." A value like "legit\\nmalicious_key: val"
+ must be emitted as a quoted scalar, not raw YAML.
+ """
+ with patch.dict(
+ os.environ,
+ {"SERVICE_NAME": "legit-service\nmalicious_key: injected_value"},
+ ):
+ result = substitute_env_vars(
+ "file_format: '0.1'\nservice_name: ${SERVICE_NAME}"
+ )
+ parsed = yaml.safe_load(result)
+ self.assertNotIn("malicious_key", parsed)
+ self.assertIn("legit-service", parsed["service_name"])
+
+ def test_newline_in_value_preserved_as_literal(self):
+ """Newline within a value is preserved as a literal newline character."""
+ with patch.dict(os.environ, {"MULTI": "line1\nline2"}):
+ result = substitute_env_vars("key: ${MULTI}")
+ parsed = yaml.safe_load(result)
+ self.assertEqual(parsed["key"], "line1\nline2")
+
+ def test_carriage_return_in_value_is_escaped(self):
+ """Carriage return in value is escaped, not injected."""
+ with patch.dict(os.environ, {"VAL": "text\r\nmore"}):
+ result = substitute_env_vars("key: ${VAL}")
+ parsed = yaml.safe_load(result)
+ self.assertIsInstance(parsed["key"], str)
+
+ def test_type_coercion_preserved_for_simple_values(self):
+ """Simple values without newlines still undergo YAML type coercion per spec."""
+ with patch.dict(os.environ, {"BOOL_VAL": "true", "INT_VAL": "42"}):
+ bool_result = yaml.safe_load(
+ substitute_env_vars("key: ${BOOL_VAL}")
+ )
+ int_result = yaml.safe_load(substitute_env_vars("key: ${INT_VAL}"))
+ self.assertIs(bool_result["key"], True)
+ self.assertEqual(int_result["key"], 42)
diff --git a/opentelemetry-sdk/tests/_configuration/file/test_loader.py b/opentelemetry-sdk/tests/_configuration/file/test_loader.py
index e6b0da6..b38f301 100644
--- a/opentelemetry-sdk/tests/_configuration/file/test_loader.py
+++ b/opentelemetry-sdk/tests/_configuration/file/test_loader.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import os
import tempfile
diff --git a/opentelemetry-sdk/tests/_configuration/test_common.py b/opentelemetry-sdk/tests/_configuration/test_common.py
index 5c3fcf1..d8236a8 100644
--- a/opentelemetry-sdk/tests/_configuration/test_common.py
+++ b/opentelemetry-sdk/tests/_configuration/test_common.py
@@ -1,21 +1,28 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
+import inspect
import unittest
+from dataclasses import dataclass
from types import SimpleNamespace
+from typing import Any, ClassVar
+from unittest.mock import MagicMock, patch
-from opentelemetry.sdk._configuration._common import _parse_headers
+from opentelemetry.sdk._configuration._common import (
+ _additional_properties,
+ _map_compression,
+ _parse_headers,
+ load_entry_point,
+)
+from opentelemetry.sdk._configuration._exceptions import ConfigurationError
+from opentelemetry.sdk._configuration.models import (
+ ExperimentalResourceDetector,
+ LogRecordExporter,
+ PushMetricExporter,
+ Sampler,
+ SpanExporter,
+ TextMapPropagator,
+)
class TestParseHeaders(unittest.TestCase):
@@ -79,3 +86,206 @@ class TestParseHeaders(unittest.TestCase):
def test_both_empty_struct_and_none_list_returns_empty_dict(self):
self.assertEqual(_parse_headers([], None), {})
+
+
+class TestLoadEntryPoint(unittest.TestCase):
+ def test_returns_loaded_class(self):
+ mock_class = MagicMock()
+ mock_ep = MagicMock()
+ mock_ep.load.return_value = mock_class
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[mock_ep],
+ ):
+ result = load_entry_point("some_group", "some_name")
+ self.assertIs(result, mock_class)
+
+ def test_raises_when_not_found(self):
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[],
+ ):
+ with self.assertRaises(ConfigurationError) as ctx:
+ load_entry_point("some_group", "missing")
+ self.assertIn("missing", str(ctx.exception))
+ self.assertIn("some_group", str(ctx.exception))
+
+ def test_wraps_load_exception_in_configuration_error(self):
+ mock_ep = MagicMock()
+ mock_ep.load.side_effect = ImportError("bad import")
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[mock_ep],
+ ):
+ with self.assertRaises(ConfigurationError) as ctx:
+ load_entry_point("some_group", "some_name")
+ self.assertIn("bad import", str(ctx.exception))
+
+ def test_instantiation_error_not_wrapped(self):
+ """load_entry_point returns the class; instantiation is the caller's
+ responsibility. Errors from calling the returned class are NOT wrapped
+ in ConfigurationError — they propagate as-is."""
+ mock_class = MagicMock(side_effect=TypeError("bad init"))
+ mock_ep = MagicMock()
+ mock_ep.load.return_value = mock_class
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[mock_ep],
+ ):
+ cls = load_entry_point("some_group", "some_name")
+ # load_entry_point itself succeeds
+ self.assertIs(cls, mock_class)
+ # Calling the returned class raises the original error, not
+ # ConfigurationError
+ with self.assertRaises(TypeError, msg="bad init"):
+ cls()
+
+
+class _CompressionWithDeflate:
+ Gzip = "gzip"
+ Deflate = "deflate"
+
+
+class _CompressionWithoutDeflate:
+ Gzip = "gzip"
+
+
+class TestMapCompression(unittest.TestCase):
+ def test_none_returns_none(self):
+ self.assertIsNone(_map_compression(None, _CompressionWithDeflate))
+
+ def test_none_string_returns_none(self):
+ self.assertIsNone(_map_compression("none", _CompressionWithDeflate))
+
+ def test_gzip_maps_to_gzip(self):
+ self.assertEqual(
+ _map_compression("gzip", _CompressionWithDeflate), "gzip"
+ )
+
+ def test_deflate_maps_when_enabled(self):
+ self.assertEqual(
+ _map_compression(
+ "deflate", _CompressionWithDeflate, allow_deflate=True
+ ),
+ "deflate",
+ )
+
+ def test_deflate_raises_by_default(self):
+ with self.assertRaises(ConfigurationError) as ctx:
+ _map_compression("deflate", _CompressionWithDeflate)
+
+ self.assertEqual(
+ str(ctx.exception),
+ "Unsupported compression value 'deflate'. Supported values: "
+ "'gzip', 'none'.",
+ )
+
+ def test_deflate_raises_when_http_enum_lacks_support(self):
+ with self.assertRaises(ConfigurationError) as ctx:
+ _map_compression(
+ "deflate", _CompressionWithoutDeflate, allow_deflate=True
+ )
+
+ self.assertEqual(
+ str(ctx.exception),
+ "Unsupported compression value 'deflate'. Supported values: "
+ "'gzip', 'none'.",
+ )
+
+ def test_http_error_message_includes_deflate(self):
+ with self.assertRaises(ConfigurationError) as ctx:
+ _map_compression(
+ "brotli", _CompressionWithDeflate, allow_deflate=True
+ )
+
+ self.assertEqual(
+ str(ctx.exception),
+ "Unsupported compression value 'brotli'. Supported values: "
+ "'gzip', 'deflate', 'none'.",
+ )
+
+
+class TestAdditionalPropertiesSupport(unittest.TestCase):
+ def setUp(self):
+ @_additional_properties
+ @dataclass
+ class _SampleConfig:
+ known_field: dict | None = None
+ another_field: str | None = None
+ additional_properties: ClassVar[dict[str, Any]]
+
+ self.cls = _SampleConfig
+
+ def test_known_fields_work_normally(self):
+ obj = self.cls(known_field={}, another_field="val")
+ self.assertEqual(obj.known_field, {})
+ self.assertEqual(obj.another_field, "val")
+ self.assertEqual(obj.additional_properties, {})
+
+ def test_unknown_kwargs_captured_in_additional_properties(self):
+ # pylint: disable=unexpected-keyword-arg
+ obj = self.cls(my_plugin={"key": "val"})
+ self.assertIsNone(obj.known_field)
+ self.assertEqual(
+ obj.additional_properties, {"my_plugin": {"key": "val"}}
+ )
+
+ def test_mixed_known_and_unknown_kwargs(self):
+ # pylint: disable=unexpected-keyword-arg
+ obj = self.cls(known_field={}, my_plugin={})
+ self.assertEqual(obj.known_field, {})
+ self.assertEqual(obj.additional_properties, {"my_plugin": {}})
+
+ def test_no_args_creates_empty_additional_properties(self):
+ obj = self.cls()
+ self.assertIsNone(obj.known_field)
+ self.assertEqual(obj.additional_properties, {})
+
+ def test_signature_preserves_known_fields_and_adds_kwargs(self):
+ sig = inspect.signature(self.cls)
+ param_names = list(sig.parameters.keys())
+ self.assertIn("known_field", param_names)
+ self.assertIn("another_field", param_names)
+ # **kwargs signals that extras are accepted
+ kwargs_param = sig.parameters.get("kwargs")
+ self.assertIsNotNone(kwargs_param)
+ self.assertEqual(kwargs_param.kind, inspect.Parameter.VAR_KEYWORD)
+
+
+class TestGeneratedModelsHaveAdditionalProperties(unittest.TestCase):
+ """Guards against regressions in the custom datamodel-codegen template.
+
+ The codegen/dataclass.jinja2 template conditionally applies the
+ @_additional_properties decorator based on the
+ additionalPropertiesType template variable. If datamodel-codegen
+ changes how it passes this variable, these tests will fail.
+ """
+
+ def _assert_supports_additional_properties(self, model_cls):
+ # pylint: disable=unexpected-keyword-arg
+ obj = model_cls(_test_plugin_key={})
+ self.assertTrue(
+ hasattr(obj, "additional_properties"),
+ f"{model_cls.__name__} missing additional_properties attribute",
+ )
+ self.assertIn("_test_plugin_key", obj.additional_properties)
+
+ def test_sampler(self):
+ self._assert_supports_additional_properties(Sampler)
+
+ def test_span_exporter(self):
+ self._assert_supports_additional_properties(SpanExporter)
+
+ def test_text_map_propagator(self):
+ self._assert_supports_additional_properties(TextMapPropagator)
+
+ def test_resource_detector(self):
+ self._assert_supports_additional_properties(
+ ExperimentalResourceDetector
+ )
+
+ def test_log_record_exporter(self):
+ self._assert_supports_additional_properties(LogRecordExporter)
+
+ def test_push_metric_exporter(self):
+ self._assert_supports_additional_properties(PushMetricExporter)
diff --git a/opentelemetry-sdk/tests/_configuration/test_logger_provider.py b/opentelemetry-sdk/tests/_configuration/test_logger_provider.py
new file mode 100644
index 0000000..3e21853
--- /dev/null
+++ b/opentelemetry-sdk/tests/_configuration/test_logger_provider.py
@@ -0,0 +1,413 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+# Tests access private members of SDK classes to assert correct configuration.
+# pylint: disable=protected-access
+
+import sys
+import unittest
+from unittest.mock import MagicMock, patch
+
+from opentelemetry._logs import get_logger_provider
+from opentelemetry.sdk._configuration._logger_provider import (
+ _DEFAULT_EXPORT_TIMEOUT_MILLIS,
+ _DEFAULT_MAX_EXPORT_BATCH_SIZE,
+ _DEFAULT_MAX_QUEUE_SIZE,
+ _DEFAULT_SCHEDULE_DELAY_MILLIS,
+ _create_batch_log_record_processor,
+ _create_log_record_exporter,
+ _create_log_record_processor,
+ _create_simple_log_record_processor,
+ configure_logger_provider,
+ create_logger_provider,
+)
+from opentelemetry.sdk._configuration.file._loader import ConfigurationError
+from opentelemetry.sdk._configuration.models import (
+ BatchLogRecordProcessor as BatchLogRecordProcessorConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ LoggerProvider as LoggerProviderConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ LogRecordExporter as LogRecordExporterConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ LogRecordLimits as LogRecordLimitsConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ LogRecordProcessor as LogRecordProcessorConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ NameStringValuePair,
+)
+from opentelemetry.sdk._configuration.models import (
+ OtlpGrpcExporter as OtlpGrpcExporterConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ OtlpHttpExporter as OtlpHttpExporterConfig,
+)
+from opentelemetry.sdk._configuration.models import (
+ SimpleLogRecordProcessor as SimpleLogRecordProcessorConfig,
+)
+from opentelemetry.sdk._logs import LoggerProvider
+from opentelemetry.sdk._logs._internal.export import (
+ BatchLogRecordProcessor,
+ ConsoleLogRecordExporter,
+ SimpleLogRecordProcessor,
+)
+from opentelemetry.sdk.resources import Resource
+
+
+class TestCreateLoggerProviderBasic(unittest.TestCase):
+ def test_none_config_returns_provider(self):
+ provider = create_logger_provider(None)
+ self.assertIsInstance(provider, LoggerProvider)
+
+ def test_none_config_uses_supplied_resource(self):
+ resource = Resource({"service.name": "svc"})
+ provider = create_logger_provider(None, resource)
+ self.assertIs(provider.resource, resource)
+
+ def test_config_with_no_processors(self):
+ config = LoggerProviderConfig(processors=[])
+ provider = create_logger_provider(config)
+ self.assertIsInstance(provider, LoggerProvider)
+
+ def test_configure_none_is_noop(self):
+ original = get_logger_provider()
+ configure_logger_provider(None)
+ self.assertIs(get_logger_provider(), original)
+
+
+class TestCreateLogRecordProcessors(unittest.TestCase):
+ @staticmethod
+ def _make_batch_config(
+ exporter_config=None,
+ schedule_delay=None,
+ export_timeout=None,
+ max_queue_size=None,
+ max_export_batch_size=None,
+ ):
+ if exporter_config is None:
+ exporter_config = LogRecordExporterConfig(console={})
+ return BatchLogRecordProcessorConfig(
+ exporter=exporter_config,
+ schedule_delay=schedule_delay,
+ export_timeout=export_timeout,
+ max_queue_size=max_queue_size,
+ max_export_batch_size=max_export_batch_size,
+ )
+
+ def test_batch_processor_default_schedule_delay(self):
+ processor = _create_batch_log_record_processor(
+ self._make_batch_config()
+ )
+ self.assertEqual(
+ processor._batch_processor._schedule_delay_millis,
+ _DEFAULT_SCHEDULE_DELAY_MILLIS,
+ )
+
+ def test_batch_processor_default_export_timeout(self):
+ processor = _create_batch_log_record_processor(
+ self._make_batch_config()
+ )
+ self.assertEqual(
+ processor._batch_processor._export_timeout_millis,
+ _DEFAULT_EXPORT_TIMEOUT_MILLIS,
+ )
+
+ def test_batch_processor_default_max_queue_size(self):
+ processor = _create_batch_log_record_processor(
+ self._make_batch_config()
+ )
+ self.assertEqual(
+ processor._batch_processor._max_queue_size,
+ _DEFAULT_MAX_QUEUE_SIZE,
+ )
+
+ def test_batch_processor_default_max_export_batch_size(self):
+ processor = _create_batch_log_record_processor(
+ self._make_batch_config()
+ )
+ self.assertEqual(
+ processor._batch_processor._max_export_batch_size,
+ _DEFAULT_MAX_EXPORT_BATCH_SIZE,
+ )
+
+ def test_batch_processor_explicit_schedule_delay(self):
+ processor = _create_batch_log_record_processor(
+ self._make_batch_config(schedule_delay=2000)
+ )
+ self.assertEqual(
+ processor._batch_processor._schedule_delay_millis, 2000.0
+ )
+
+ def test_batch_processor_explicit_export_timeout(self):
+ processor = _create_batch_log_record_processor(
+ self._make_batch_config(export_timeout=5000)
+ )
+ self.assertEqual(
+ processor._batch_processor._export_timeout_millis, 5000.0
+ )
+
+ def test_batch_processor_explicit_max_queue_size(self):
+ processor = _create_batch_log_record_processor(
+ self._make_batch_config(max_queue_size=512)
+ )
+ self.assertEqual(processor._batch_processor._max_queue_size, 512)
+
+ def test_batch_processor_explicit_max_export_batch_size(self):
+ processor = _create_batch_log_record_processor(
+ self._make_batch_config(max_export_batch_size=128)
+ )
+ self.assertEqual(
+ processor._batch_processor._max_export_batch_size, 128
+ )
+
+ def test_batch_processor_uses_console_exporter(self):
+ processor = _create_batch_log_record_processor(
+ self._make_batch_config()
+ )
+ self.assertIsInstance(
+ processor._batch_processor._exporter, ConsoleLogRecordExporter
+ )
+
+ def test_simple_processor_uses_console_exporter(self):
+ config = SimpleLogRecordProcessorConfig(
+ exporter=LogRecordExporterConfig(console={})
+ )
+ processor = _create_simple_log_record_processor(config)
+ self.assertIsInstance(processor, SimpleLogRecordProcessor)
+ self.assertIsInstance(processor._exporter, ConsoleLogRecordExporter)
+
+ def test_batch_processor_dispatched_from_processor_config(self):
+ config = LogRecordProcessorConfig(batch=self._make_batch_config())
+ processor = _create_log_record_processor(config)
+ self.assertIsInstance(processor, BatchLogRecordProcessor)
+
+ def test_simple_processor_dispatched_from_processor_config(self):
+ config = LogRecordProcessorConfig(
+ simple=SimpleLogRecordProcessorConfig(
+ exporter=LogRecordExporterConfig(console={})
+ )
+ )
+ processor = _create_log_record_processor(config)
+ self.assertIsInstance(processor, SimpleLogRecordProcessor)
+
+ def test_no_processor_type_raises(self):
+ config = LogRecordProcessorConfig()
+ with self.assertRaises(ConfigurationError):
+ _create_log_record_processor(config)
+
+ def test_batch_processor_suppresses_env_var(self):
+ """schedule_delay default must not read OTEL_BLRP_SCHEDULE_DELAY."""
+ with patch.dict("os.environ", {"OTEL_BLRP_SCHEDULE_DELAY": "9999"}):
+ processor = _create_batch_log_record_processor(
+ self._make_batch_config()
+ )
+ self.assertEqual(
+ processor._batch_processor._schedule_delay_millis,
+ _DEFAULT_SCHEDULE_DELAY_MILLIS,
+ )
+
+
+class TestCreateLogRecordExporters(unittest.TestCase):
+ def test_console_exporter(self):
+ config = LogRecordExporterConfig(console={})
+ exporter = _create_log_record_exporter(config)
+ self.assertIsInstance(exporter, ConsoleLogRecordExporter)
+
+ def test_no_exporter_type_raises(self):
+ config = LogRecordExporterConfig()
+ with self.assertRaises(ConfigurationError):
+ _create_log_record_exporter(config)
+
+ def test_plugin_log_exporter_loaded_via_entry_point(self):
+ mock_exporter = MagicMock()
+ mock_class = MagicMock(return_value=mock_exporter)
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[MagicMock(**{"load.return_value": mock_class})],
+ ):
+ # pylint: disable=unexpected-keyword-arg
+ result = _create_log_record_exporter(
+ LogRecordExporterConfig(my_custom_exporter={})
+ )
+ self.assertIs(result, mock_exporter)
+
+ def test_unknown_log_exporter_raises_configuration_error(self):
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[],
+ ):
+ with self.assertRaises(ConfigurationError):
+ # pylint: disable=unexpected-keyword-arg
+ _create_log_record_exporter(
+ LogRecordExporterConfig(no_such_exporter={})
+ )
+
+ def test_otlp_http_missing_package_raises(self):
+ config = LogRecordExporterConfig(
+ otlp_http=OtlpHttpExporterConfig(endpoint="http://localhost:4318")
+ )
+ with patch.dict(
+ sys.modules,
+ {
+ "opentelemetry.exporter.otlp.proto.http": None,
+ "opentelemetry.exporter.otlp.proto.http._log_exporter": None,
+ },
+ ):
+ with self.assertRaises(ConfigurationError):
+ _create_log_record_exporter(config)
+
+ def test_otlp_grpc_missing_package_raises(self):
+ config = LogRecordExporterConfig(
+ otlp_grpc=OtlpGrpcExporterConfig(endpoint="http://localhost:4317")
+ )
+ with patch.dict(
+ sys.modules,
+ {
+ "grpc": None,
+ "opentelemetry.exporter.otlp.proto.grpc._log_exporter": None,
+ },
+ ):
+ with self.assertRaises(ConfigurationError):
+ _create_log_record_exporter(config)
+
+ def test_otlp_http_exporter_endpoint(self):
+ mock_exporter_cls = MagicMock()
+ mock_compression_cls = MagicMock()
+ mock_compression_cls.Gzip = "gzip"
+
+ mock_module = MagicMock()
+ mock_module.Compression = mock_compression_cls
+ mock_log_module = MagicMock()
+ mock_log_module.OTLPLogExporter = mock_exporter_cls
+
+ with patch.dict(
+ sys.modules,
+ {
+ "opentelemetry.exporter.otlp.proto.http": mock_module,
+ "opentelemetry.exporter.otlp.proto.http._log_exporter": mock_log_module,
+ },
+ ):
+ config = LogRecordExporterConfig(
+ otlp_http=OtlpHttpExporterConfig(
+ endpoint="http://collector:4318",
+ timeout=5000,
+ )
+ )
+ _create_log_record_exporter(config)
+
+ mock_exporter_cls.assert_called_once()
+ call_kwargs = mock_exporter_cls.call_args.kwargs
+ self.assertEqual(call_kwargs["endpoint"], "http://collector:4318")
+ self.assertAlmostEqual(call_kwargs["timeout"], 5.0)
+
+ def test_otlp_http_exporter_headers(self):
+ mock_exporter_cls = MagicMock()
+ mock_compression_cls = MagicMock()
+ mock_module = MagicMock()
+ mock_module.Compression = mock_compression_cls
+ mock_log_module = MagicMock()
+ mock_log_module.OTLPLogExporter = mock_exporter_cls
+
+ with patch.dict(
+ sys.modules,
+ {
+ "opentelemetry.exporter.otlp.proto.http": mock_module,
+ "opentelemetry.exporter.otlp.proto.http._log_exporter": mock_log_module,
+ },
+ ):
+ config = LogRecordExporterConfig(
+ otlp_http=OtlpHttpExporterConfig(
+ headers=[
+ NameStringValuePair(name="x-api-key", value="secret")
+ ]
+ )
+ )
+ _create_log_record_exporter(config)
+
+ call_kwargs = mock_exporter_cls.call_args.kwargs
+ self.assertEqual(call_kwargs["headers"], {"x-api-key": "secret"})
+
+ def test_otlp_http_exporter_deflate_compression(self):
+ mock_exporter_cls = MagicMock()
+ mock_compression_cls = MagicMock()
+ mock_compression_cls.Deflate = "deflate"
+ mock_module = MagicMock()
+ mock_module.Compression = mock_compression_cls
+ mock_log_module = MagicMock()
+ mock_log_module.OTLPLogExporter = mock_exporter_cls
+
+ with patch.dict(
+ sys.modules,
+ {
+ "opentelemetry.exporter.otlp.proto.http": mock_module,
+ "opentelemetry.exporter.otlp.proto.http._log_exporter": mock_log_module,
+ },
+ ):
+ config = LogRecordExporterConfig(
+ otlp_http=OtlpHttpExporterConfig(compression="deflate")
+ )
+ _create_log_record_exporter(config)
+
+ call_kwargs = mock_exporter_cls.call_args.kwargs
+ self.assertEqual(call_kwargs["compression"], "deflate")
+
+ def test_otlp_grpc_exporter_endpoint(self):
+ mock_exporter_cls = MagicMock()
+ mock_grpc = MagicMock()
+ mock_grpc.Compression = MagicMock()
+ mock_grpc_log_module = MagicMock()
+ mock_grpc_log_module.OTLPLogExporter = mock_exporter_cls
+
+ with patch.dict(
+ sys.modules,
+ {
+ "grpc": mock_grpc,
+ "opentelemetry.exporter.otlp.proto.grpc._log_exporter": mock_grpc_log_module,
+ },
+ ):
+ config = LogRecordExporterConfig(
+ otlp_grpc=OtlpGrpcExporterConfig(
+ endpoint="http://collector:4317",
+ timeout=10000,
+ )
+ )
+ _create_log_record_exporter(config)
+
+ mock_exporter_cls.assert_called_once()
+ call_kwargs = mock_exporter_cls.call_args.kwargs
+ self.assertEqual(call_kwargs["endpoint"], "http://collector:4317")
+ self.assertAlmostEqual(call_kwargs["timeout"], 10.0)
+
+
+class TestLogRecordLimits(unittest.TestCase):
+ def test_limits_logs_warning(self):
+ config = LoggerProviderConfig(
+ processors=[],
+ limits=LogRecordLimitsConfig(attribute_count_limit=64),
+ )
+ with self.assertLogs(
+ "opentelemetry.sdk._configuration._logger_provider",
+ level="WARNING",
+ ) as cm:
+ create_logger_provider(config)
+ self.assertTrue(
+ any("limits" in msg for msg in cm.output),
+ "Expected warning about unsupported limits",
+ )
+
+ @staticmethod
+ def test_no_limits_no_warning():
+ config = LoggerProviderConfig(processors=[])
+ with patch(
+ "opentelemetry.sdk._configuration._logger_provider._logger"
+ ) as mock_logger:
+ create_logger_provider(config)
+ mock_logger.warning.assert_not_called()
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/opentelemetry-sdk/tests/_configuration/test_meter_provider.py b/opentelemetry-sdk/tests/_configuration/test_meter_provider.py
index 04d6084..f3a48ac 100644
--- a/opentelemetry-sdk/tests/_configuration/test_meter_provider.py
+++ b/opentelemetry-sdk/tests/_configuration/test_meter_provider.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# Tests access private members of SDK classes to assert correct configuration.
# pylint: disable=protected-access
@@ -274,6 +263,34 @@ class TestCreateMetricReaders(unittest.TestCase):
self.assertIsNone(kwargs["timeout"])
self.assertIsNone(kwargs["compression"])
+ def test_otlp_http_created_with_deflate_compression(self):
+ mock_exporter_cls = MagicMock()
+ mock_compression_cls = MagicMock()
+ mock_compression_cls.Deflate = "deflate_val"
+ mock_http_module = MagicMock()
+ mock_http_module.Compression = mock_compression_cls
+ mock_module = MagicMock()
+ mock_module.OTLPMetricExporter = mock_exporter_cls
+
+ with patch.dict(
+ sys.modules,
+ {
+ "opentelemetry.exporter.otlp.proto.http.metric_exporter": mock_module,
+ "opentelemetry.exporter.otlp.proto.http": mock_http_module,
+ },
+ ):
+ config = self._make_periodic_config(
+ PushMetricExporterConfig(
+ otlp_http=OtlpHttpMetricExporterConfig(
+ compression="deflate"
+ )
+ )
+ )
+ create_meter_provider(config)
+
+ _, kwargs = mock_exporter_cls.call_args
+ self.assertEqual(kwargs["compression"], "deflate_val")
+
def test_otlp_grpc_missing_package_raises(self):
config = self._make_periodic_config(
PushMetricExporterConfig(otlp_grpc=OtlpGrpcMetricExporterConfig())
@@ -306,6 +323,32 @@ class TestCreateMetricReaders(unittest.TestCase):
with self.assertRaises(ConfigurationError):
create_meter_provider(config)
+ def test_plugin_metric_exporter_loaded_via_entry_point(self):
+ mock_exporter = MagicMock()
+ mock_class = MagicMock(return_value=mock_exporter)
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[MagicMock(**{"load.return_value": mock_class})],
+ ):
+ # pylint: disable=unexpected-keyword-arg
+ config = self._make_periodic_config(
+ PushMetricExporterConfig(my_custom_exporter={})
+ )
+ provider = create_meter_provider(config)
+ self.assertEqual(len(provider._sdk_config.metric_readers), 1)
+
+ def test_unknown_metric_exporter_raises_configuration_error(self):
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[],
+ ):
+ # pylint: disable=unexpected-keyword-arg
+ config = self._make_periodic_config(
+ PushMetricExporterConfig(no_such_exporter={})
+ )
+ with self.assertRaises(ConfigurationError):
+ create_meter_provider(config)
+
def test_multiple_readers(self):
config = MeterProviderConfig(
readers=[
diff --git a/opentelemetry-sdk/tests/_configuration/test_models.py b/opentelemetry-sdk/tests/_configuration/test_models.py
index 8a1fdfc..2daa8d7 100644
--- a/opentelemetry-sdk/tests/_configuration/test_models.py
+++ b/opentelemetry-sdk/tests/_configuration/test_models.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
diff --git a/opentelemetry-sdk/tests/_configuration/test_propagator.py b/opentelemetry-sdk/tests/_configuration/test_propagator.py
index a8ce467..a14e131 100644
--- a/opentelemetry-sdk/tests/_configuration/test_propagator.py
+++ b/opentelemetry-sdk/tests/_configuration/test_propagator.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
from os import environ
@@ -89,7 +78,7 @@ class TestCreatePropagator(unittest.TestCase):
mock_ep.load.return_value = lambda: mock_propagator
with patch(
- "opentelemetry.sdk._configuration._propagator.entry_points",
+ "opentelemetry.sdk._configuration._common.entry_points",
return_value=[mock_ep],
):
config = PropagatorConfig(
@@ -106,7 +95,7 @@ class TestCreatePropagator(unittest.TestCase):
mock_ep.load.return_value = lambda: mock_propagator
with patch(
- "opentelemetry.sdk._configuration._propagator.entry_points",
+ "opentelemetry.sdk._configuration._common.entry_points",
return_value=[mock_ep],
):
config = PropagatorConfig(
@@ -118,7 +107,7 @@ class TestCreatePropagator(unittest.TestCase):
def test_b3_not_installed_raises_configuration_error(self):
with patch(
- "opentelemetry.sdk._configuration._propagator.entry_points",
+ "opentelemetry.sdk._configuration._common.entry_points",
return_value=[],
):
config = PropagatorConfig(
@@ -135,7 +124,7 @@ class TestCreatePropagator(unittest.TestCase):
mock_ep.load.return_value = lambda: mock_tc
with patch(
- "opentelemetry.sdk._configuration._propagator.entry_points",
+ "opentelemetry.sdk._configuration._common.entry_points",
return_value=[mock_ep],
):
result = create_propagator(config)
@@ -158,7 +147,7 @@ class TestCreatePropagator(unittest.TestCase):
return []
with patch(
- "opentelemetry.sdk._configuration._propagator.entry_points",
+ "opentelemetry.sdk._configuration._common.entry_points",
side_effect=fake_entry_points,
):
config = PropagatorConfig(composite_list="tracecontext,baggage")
@@ -182,7 +171,7 @@ class TestCreatePropagator(unittest.TestCase):
mock_ep.load.return_value = lambda: mock_tc
with patch(
- "opentelemetry.sdk._configuration._propagator.entry_points",
+ "opentelemetry.sdk._configuration._common.entry_points",
return_value=[mock_ep],
):
config = PropagatorConfig(composite_list=" tracecontext ")
@@ -195,7 +184,7 @@ class TestCreatePropagator(unittest.TestCase):
mock_ep.load.side_effect = RuntimeError("package broken")
with patch(
- "opentelemetry.sdk._configuration._propagator.entry_points",
+ "opentelemetry.sdk._configuration._common.entry_points",
return_value=[mock_ep],
):
config = PropagatorConfig(composite_list="broken-prop")
@@ -210,7 +199,7 @@ class TestCreatePropagator(unittest.TestCase):
mock_ep.load.return_value = lambda: mock_tc
with patch(
- "opentelemetry.sdk._configuration._propagator.entry_points",
+ "opentelemetry.sdk._configuration._common.entry_points",
return_value=[mock_ep],
):
config = PropagatorConfig(
@@ -229,13 +218,47 @@ class TestCreatePropagator(unittest.TestCase):
def test_unknown_composite_list_propagator_raises(self):
with patch(
- "opentelemetry.sdk._configuration._propagator.entry_points",
+ "opentelemetry.sdk._configuration._common.entry_points",
return_value=[],
):
config = PropagatorConfig(composite_list="nonexistent")
with self.assertRaises(ConfigurationError):
create_propagator(config)
+ def test_plugin_propagator_via_entry_point(self):
+ mock_propagator = MagicMock()
+ mock_ep = MagicMock()
+ mock_ep.load.return_value = lambda: mock_propagator
+
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[mock_ep],
+ ):
+ config = PropagatorConfig(
+ composite=[
+ # pylint: disable=unexpected-keyword-arg
+ TextMapPropagatorConfig(my_custom_propagator={})
+ ]
+ )
+ result = create_propagator(config)
+
+ self.assertEqual(len(result._propagators), 1) # type: ignore[attr-defined]
+ self.assertIs(result._propagators[0], mock_propagator) # type: ignore[attr-defined]
+
+ def test_unknown_composite_propagator_raises(self):
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[],
+ ):
+ config = PropagatorConfig(
+ composite=[
+ # pylint: disable=unexpected-keyword-arg
+ TextMapPropagatorConfig(nonexistent={})
+ ]
+ )
+ with self.assertRaises(ConfigurationError):
+ create_propagator(config)
+
class TestConfigurePropagator(unittest.TestCase):
def test_configure_propagator_calls_set_global_textmap(self):
diff --git a/opentelemetry-sdk/tests/_configuration/test_resource.py b/opentelemetry-sdk/tests/_configuration/test_resource.py
index dd089e7..539a6e2 100644
--- a/opentelemetry-sdk/tests/_configuration/test_resource.py
+++ b/opentelemetry-sdk/tests/_configuration/test_resource.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import os
import socket
@@ -18,6 +7,7 @@ import sys
import unittest
from unittest.mock import MagicMock, patch
+from opentelemetry.sdk._configuration._exceptions import ConfigurationError
from opentelemetry.sdk._configuration._resource import create_resource
from opentelemetry.sdk._configuration.models import (
AttributeNameValue,
@@ -33,6 +23,7 @@ from opentelemetry.sdk.resources import (
HOST_NAME,
PROCESS_PID,
PROCESS_RUNTIME_NAME,
+ SERVICE_INSTANCE_ID,
SERVICE_NAME,
TELEMETRY_SDK_LANGUAGE,
TELEMETRY_SDK_NAME,
@@ -307,6 +298,79 @@ class TestCreateResourceAttributesList(unittest.TestCase):
self.assertTrue(any("no-equals" in msg for msg in cm.output))
+class TestServiceResourceDetector(unittest.TestCase):
+ @staticmethod
+ def _config_with_service() -> ResourceConfig:
+ return ResourceConfig(
+ detection_development=ExperimentalResourceDetection(
+ detectors=[ExperimentalResourceDetector(service={})]
+ )
+ )
+
+ def test_service_detector_adds_instance_id(self):
+ resource = create_resource(self._config_with_service())
+ self.assertIn(SERVICE_INSTANCE_ID, resource.attributes)
+
+ def test_service_instance_id_is_unique_per_call(self):
+ r1 = create_resource(self._config_with_service())
+ r2 = create_resource(self._config_with_service())
+ self.assertNotEqual(
+ r1.attributes[SERVICE_INSTANCE_ID],
+ r2.attributes[SERVICE_INSTANCE_ID],
+ )
+
+ def test_service_detector_reads_otel_service_name_env_var(self):
+ with patch.dict(os.environ, {"OTEL_SERVICE_NAME": "my-service"}):
+ resource = create_resource(self._config_with_service())
+ self.assertEqual(resource.attributes[SERVICE_NAME], "my-service")
+
+ def test_service_detector_no_env_var_leaves_default_service_name(self):
+ with patch.dict(os.environ, {}, clear=True):
+ resource = create_resource(self._config_with_service())
+ self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service")
+
+ def test_explicit_service_name_overrides_env_var(self):
+ """Config attributes win over the service detector's env-var value."""
+ config = ResourceConfig(
+ attributes=[
+ AttributeNameValue(name="service.name", value="explicit-svc")
+ ],
+ detection_development=ExperimentalResourceDetection(
+ detectors=[ExperimentalResourceDetector(service={})]
+ ),
+ )
+ with patch.dict(os.environ, {"OTEL_SERVICE_NAME": "env-svc"}):
+ resource = create_resource(config)
+ self.assertEqual(resource.attributes[SERVICE_NAME], "explicit-svc")
+
+ def test_service_detector_not_run_when_absent(self):
+ resource = create_resource(ResourceConfig())
+ self.assertNotIn(SERVICE_INSTANCE_ID, resource.attributes)
+
+ def test_service_detector_not_run_when_detection_development_is_none(self):
+ resource = create_resource(ResourceConfig(detection_development=None))
+ self.assertNotIn(SERVICE_INSTANCE_ID, resource.attributes)
+
+ def test_service_detector_also_includes_sdk_defaults(self):
+ resource = create_resource(self._config_with_service())
+ self.assertEqual(resource.attributes[TELEMETRY_SDK_LANGUAGE], "python")
+ self.assertIn(TELEMETRY_SDK_VERSION, resource.attributes)
+
+ def test_included_filter_limits_service_attributes(self):
+ config = ResourceConfig(
+ detection_development=ExperimentalResourceDetection(
+ detectors=[ExperimentalResourceDetector(service={})],
+ attributes=IncludeExclude(included=["service.instance.id"]),
+ )
+ )
+ with patch.dict(os.environ, {"OTEL_SERVICE_NAME": "my-service"}):
+ resource = create_resource(config)
+ self.assertIn(SERVICE_INSTANCE_ID, resource.attributes)
+ # service.name comes from the filter-excluded detector output, but the
+ # default "unknown_service" is still added by create_resource directly
+ self.assertEqual(resource.attributes[SERVICE_NAME], "unknown_service")
+
+
class TestHostResourceDetector(unittest.TestCase):
@staticmethod
def _config_with_host() -> ResourceConfig:
@@ -404,23 +468,14 @@ class TestContainerResourceDetector(unittest.TestCase):
resource = create_resource(config)
self.assertNotIn(CONTAINER_ID, resource.attributes)
- def test_container_detector_warns_when_package_missing(self):
- """A warning is logged when the contrib entry point is not found."""
+ def test_container_detector_raises_when_package_missing(self):
+ """ConfigurationError is raised when the contrib entry point is not found."""
with patch(
- "opentelemetry.sdk._configuration._resource.entry_points",
+ "opentelemetry.sdk._configuration._common.entry_points",
return_value=[],
):
- with self.assertLogs(
- "opentelemetry.sdk._configuration._resource", level="WARNING"
- ) as cm:
- resource = create_resource(self._config_with_container())
- self.assertNotIn(CONTAINER_ID, resource.attributes)
- self.assertTrue(
- any(
- "opentelemetry-resource-detector-containerid" in msg
- for msg in cm.output
- )
- )
+ with self.assertRaises(ConfigurationError):
+ create_resource(self._config_with_container())
def test_container_detector_uses_contrib_when_available(self):
"""When the contrib entry point is registered, container.id is detected."""
@@ -431,7 +486,7 @@ class TestContainerResourceDetector(unittest.TestCase):
mock_ep.load.return_value = mock_detector
with patch(
- "opentelemetry.sdk._configuration._resource.entry_points",
+ "opentelemetry.sdk._configuration._common.entry_points",
return_value=[mock_ep],
):
resource = create_resource(self._config_with_container())
@@ -455,7 +510,7 @@ class TestContainerResourceDetector(unittest.TestCase):
),
)
with patch(
- "opentelemetry.sdk._configuration._resource.entry_points",
+ "opentelemetry.sdk._configuration._common.entry_points",
return_value=[mock_ep],
):
resource = create_resource(config)
@@ -528,3 +583,40 @@ class TestProcessResourceDetector(unittest.TestCase):
)
resource = create_resource(config)
self.assertEqual(resource.attributes[PROCESS_PID], os.getpid())
+
+
+class TestPluginResourceDetector(unittest.TestCase):
+ def test_plugin_detector_loaded_via_entry_point(self):
+ mock_resource = Resource({"custom.attr": "value"})
+ mock_detector = MagicMock()
+ mock_detector.return_value.detect.return_value = mock_resource
+ mock_ep = MagicMock()
+ mock_ep.load.return_value = mock_detector
+
+ config = ResourceConfig(
+ detection_development=ExperimentalResourceDetection(
+ # pylint: disable=unexpected-keyword-arg
+ detectors=[ExperimentalResourceDetector(my_custom_detector={})]
+ )
+ )
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[mock_ep],
+ ):
+ resource = create_resource(config)
+
+ self.assertEqual(resource.attributes["custom.attr"], "value")
+
+ def test_unknown_detector_raises_configuration_error(self):
+ config = ResourceConfig(
+ detection_development=ExperimentalResourceDetection(
+ # pylint: disable=unexpected-keyword-arg
+ detectors=[ExperimentalResourceDetector(no_such_detector={})]
+ )
+ )
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[],
+ ):
+ with self.assertRaises(ConfigurationError):
+ create_resource(config)
diff --git a/opentelemetry-sdk/tests/_configuration/test_tracer_provider.py b/opentelemetry-sdk/tests/_configuration/test_tracer_provider.py
index 5caf077..c3ada36 100644
--- a/opentelemetry-sdk/tests/_configuration/test_tracer_provider.py
+++ b/opentelemetry-sdk/tests/_configuration/test_tracer_provider.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# Tests access private members of SDK classes to assert correct configuration.
# pylint: disable=protected-access
@@ -69,6 +58,7 @@ from opentelemetry.sdk.trace.sampling import (
ALWAYS_OFF,
ALWAYS_ON,
ParentBased,
+ Sampler,
TraceIdRatioBased,
)
@@ -219,9 +209,27 @@ class TestCreateSampler(unittest.TestCase):
def test_unknown_sampler_raises_configuration_error(self):
with self.assertRaises(ConfigurationError):
- create_tracer_provider(
- TracerProviderConfig(processors=[], sampler=SamplerConfig())
- )
+ self._make_provider(SamplerConfig())
+
+ def test_plugin_sampler_loaded_via_entry_point(self):
+ mock_sampler = MagicMock(spec=Sampler)
+ mock_class = MagicMock(return_value=mock_sampler)
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[MagicMock(**{"load.return_value": mock_class})],
+ ):
+ # pylint: disable=unexpected-keyword-arg
+ provider = self._make_provider(SamplerConfig(my_custom_sampler={}))
+ self.assertIs(provider.sampler, mock_sampler)
+
+ def test_unknown_plugin_raises_configuration_error(self):
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[],
+ ):
+ with self.assertRaises(ConfigurationError):
+ # pylint: disable=unexpected-keyword-arg
+ self._make_provider(SamplerConfig(no_such_sampler={}))
class TestCreateSpanExporterAndProcessor(unittest.TestCase):
@@ -309,6 +317,32 @@ class TestCreateSpanExporterAndProcessor(unittest.TestCase):
compression=None,
)
+ def test_otlp_http_created_with_deflate_compression(self):
+ mock_exporter_cls = MagicMock()
+ mock_compression_cls = MagicMock()
+ mock_compression_cls.Deflate = "deflate_val"
+ mock_module = MagicMock()
+ mock_module.OTLPSpanExporter = mock_exporter_cls
+ mock_http_module = MagicMock()
+ mock_http_module.Compression = mock_compression_cls
+
+ with patch.dict(
+ sys.modules,
+ {
+ "opentelemetry.exporter.otlp.proto.http.trace_exporter": mock_module,
+ "opentelemetry.exporter.otlp.proto.http": mock_http_module,
+ },
+ ):
+ config = self._make_batch_config(
+ SpanExporterConfig(
+ otlp_http=OtlpHttpExporterConfig(compression="deflate")
+ )
+ )
+ create_tracer_provider(config)
+
+ _, kwargs = mock_exporter_cls.call_args
+ self.assertEqual(kwargs["compression"], "deflate_val")
+
def test_otlp_http_headers_list(self):
mock_exporter_cls = MagicMock()
mock_http_module = MagicMock()
@@ -361,6 +395,34 @@ class TestCreateSpanExporterAndProcessor(unittest.TestCase):
with self.assertRaises(ConfigurationError):
create_tracer_provider(config)
+ def test_plugin_span_exporter_loaded_via_entry_point(self):
+ mock_exporter = MagicMock()
+ mock_class = MagicMock(return_value=mock_exporter)
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[MagicMock(**{"load.return_value": mock_class})],
+ ):
+ config = self._make_batch_config(
+ # pylint: disable=unexpected-keyword-arg
+ SpanExporterConfig(my_custom_exporter={})
+ )
+ provider = create_tracer_provider(config)
+ self.assertEqual(
+ len(provider._active_span_processor._span_processors), 1
+ )
+
+ def test_unknown_span_exporter_raises_configuration_error(self):
+ with patch(
+ "opentelemetry.sdk._configuration._common.entry_points",
+ return_value=[],
+ ):
+ config = self._make_batch_config(
+ # pylint: disable=unexpected-keyword-arg
+ SpanExporterConfig(no_such_exporter={})
+ )
+ with self.assertRaises(ConfigurationError):
+ create_tracer_provider(config)
+
class TestCreateSpanLimits(unittest.TestCase):
# pylint: disable=no-self-use
diff --git a/opentelemetry-sdk/tests/conftest.py b/opentelemetry-sdk/tests/conftest.py
index b0b6339..ba17374 100644
--- a/opentelemetry-sdk/tests/conftest.py
+++ b/opentelemetry-sdk/tests/conftest.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import random
from os import environ
diff --git a/opentelemetry-sdk/tests/context/test_asyncio.py b/opentelemetry-sdk/tests/context/test_asyncio.py
index 2c5b27a..778533e 100644
--- a/opentelemetry-sdk/tests/context/test_asyncio.py
+++ b/opentelemetry-sdk/tests/context/test_asyncio.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
import unittest
diff --git a/opentelemetry-sdk/tests/error_handler/test_error_handler.py b/opentelemetry-sdk/tests/error_handler/test_error_handler.py
index b753c1c..fadd409 100644
--- a/opentelemetry-sdk/tests/error_handler/test_error_handler.py
+++ b/opentelemetry-sdk/tests/error_handler/test_error_handler.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from logging import ERROR
from unittest import TestCase
diff --git a/opentelemetry-sdk/tests/events/__init__.py b/opentelemetry-sdk/tests/events/__init__.py
index b0a6f42..e57cf4a 100644
--- a/opentelemetry-sdk/tests/events/__init__.py
+++ b/opentelemetry-sdk/tests/events/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/opentelemetry-sdk/tests/events/test_events.py b/opentelemetry-sdk/tests/events/test_events.py
index df77010..1fb9c3c 100644
--- a/opentelemetry-sdk/tests/events/test_events.py
+++ b/opentelemetry-sdk/tests/events/test_events.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access,no-self-use
diff --git a/opentelemetry-sdk/tests/logs/__init__.py b/opentelemetry-sdk/tests/logs/__init__.py
index b0a6f42..e57cf4a 100644
--- a/opentelemetry-sdk/tests/logs/__init__.py
+++ b/opentelemetry-sdk/tests/logs/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/opentelemetry-sdk/tests/logs/test_export.py b/opentelemetry-sdk/tests/logs/test_export.py
index c36eecc..06af6ef 100644
--- a/opentelemetry-sdk/tests/logs/test_export.py
+++ b/opentelemetry-sdk/tests/logs/test_export.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access
import logging
@@ -19,10 +8,10 @@ import sys
import threading
import time
import unittest
+from collections.abc import Sequence
from concurrent.futures import ( # pylint: disable=no-name-in-module
ThreadPoolExecutor,
)
-from typing import Sequence
from unittest import mock
from unittest.mock import Mock, patch
@@ -51,6 +40,7 @@ from opentelemetry.sdk.environment_variables import (
OTEL_BLRP_MAX_EXPORT_BATCH_SIZE,
OTEL_BLRP_MAX_QUEUE_SIZE,
OTEL_BLRP_SCHEDULE_DELAY,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
)
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import InMemoryMetricReader
@@ -406,6 +396,9 @@ class TestSimpleLogRecordProcessor(unittest.TestCase):
]
self.assertEqual(expected, emitted)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
def test_metrics(self): # pylint: disable=too-many-locals
metric_reader = InMemoryMetricReader()
meter_provider = MeterProvider(metric_readers=[metric_reader])
@@ -695,6 +688,9 @@ class TestBatchLogRecordProcessor(unittest.TestCase):
max_export_batch_size=101,
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
def test_metrics(self): # pylint: disable=too-many-locals,too-many-statements
metric_reader = InMemoryMetricReader()
meter_provider = MeterProvider(metric_readers=[metric_reader])
diff --git a/opentelemetry-sdk/tests/logs/test_handler.py b/opentelemetry-sdk/tests/logs/test_handler.py
index b3b8b47..96fc316 100644
--- a/opentelemetry-sdk/tests/logs/test_handler.py
+++ b/opentelemetry-sdk/tests/logs/test_handler.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import logging
import os
diff --git a/opentelemetry-sdk/tests/logs/test_log_limits.py b/opentelemetry-sdk/tests/logs/test_log_limits.py
index 002639b..daaba3a 100644
--- a/opentelemetry-sdk/tests/logs/test_log_limits.py
+++ b/opentelemetry-sdk/tests/logs/test_log_limits.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
from unittest.mock import patch
diff --git a/opentelemetry-sdk/tests/logs/test_log_record.py b/opentelemetry-sdk/tests/logs/test_log_record.py
index 3f9c476..89cb7b3 100644
--- a/opentelemetry-sdk/tests/logs/test_log_record.py
+++ b/opentelemetry-sdk/tests/logs/test_log_record.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import json
import unittest
diff --git a/opentelemetry-sdk/tests/logs/test_logger_provider_cache.py b/opentelemetry-sdk/tests/logs/test_logger_provider_cache.py
index 83f1bbb..ee7c403 100644
--- a/opentelemetry-sdk/tests/logs/test_logger_provider_cache.py
+++ b/opentelemetry-sdk/tests/logs/test_logger_provider_cache.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
import logging
import unittest
diff --git a/opentelemetry-sdk/tests/logs/test_logs.py b/opentelemetry-sdk/tests/logs/test_logs.py
index d96c0cd..a9b171b 100644
--- a/opentelemetry-sdk/tests/logs/test_logs.py
+++ b/opentelemetry-sdk/tests/logs/test_logs.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access
@@ -18,20 +7,21 @@ import unittest
from unittest.mock import Mock, patch
from opentelemetry._logs import LogRecord, SeverityNumber
+from opentelemetry.attributes import BoundedAttributes
from opentelemetry.context import get_current
-from opentelemetry.metrics import NoOpMeterProvider
from opentelemetry.sdk._logs import (
Logger,
LoggerProvider,
ReadableLogRecord,
+ ReadWriteLogRecord,
)
from opentelemetry.sdk._logs._internal import (
- LoggerMetrics,
NoOpLogger,
SynchronousMultiLogRecordProcessor,
_disable_logger_configurator,
_LoggerConfig,
_RuleBasedLoggerConfigurator,
+ create_logger_metrics,
)
from opentelemetry.sdk.environment_variables import OTEL_SDK_DISABLED
from opentelemetry.sdk.resources import Resource
@@ -39,6 +29,7 @@ from opentelemetry.sdk.util.instrumentation import (
InstrumentationScope,
_scope_name_matches_glob,
)
+from opentelemetry.semconv.attributes import exception_attributes
class TestLoggerProvider(unittest.TestCase):
@@ -286,7 +277,7 @@ class TestLogger(unittest.TestCase):
"schema_url",
{"an": "attribute"},
),
- logger_metrics=LoggerMetrics(NoOpMeterProvider()),
+ logger_metrics=create_logger_metrics(Mock(), False),
_logger_config=_LoggerConfig.default(),
)
return logger, log_record_processor_mock
@@ -354,3 +345,109 @@ class TestLogger(unittest.TestCase):
self.assertEqual(result_log_record.attributes, {"some": "attributes"})
self.assertEqual(result_log_record.event_name, "event_name")
self.assertEqual(log_data.resource, logger.resource)
+
+ def test_emit_with_exception_adds_attributes(self):
+ logger, log_record_processor_mock = self._get_logger()
+ exc = ValueError("boom")
+
+ logger.emit(body="a log line", exception=exc)
+ log_record_processor_mock.on_emit.assert_called_once()
+ log_data = log_record_processor_mock.on_emit.call_args.args[0]
+ attributes = dict(log_data.log_record.attributes)
+ self.assertEqual(
+ attributes[exception_attributes.EXCEPTION_TYPE], "ValueError"
+ )
+ self.assertEqual(
+ attributes[exception_attributes.EXCEPTION_MESSAGE], "boom"
+ )
+ self.assertIn(
+ "ValueError: boom",
+ attributes[exception_attributes.EXCEPTION_STACKTRACE],
+ )
+
+ def test_emit_with_raised_exception_has_stacktrace(self):
+ logger, log_record_processor_mock = self._get_logger()
+
+ try:
+ raise ValueError("boom")
+ except ValueError as exc:
+ logger.emit(body="error", exception=exc)
+
+ log_record_processor_mock.on_emit.assert_called_once()
+ log_data = log_record_processor_mock.on_emit.call_args.args[0]
+ stacktrace = dict(log_data.log_record.attributes)[
+ exception_attributes.EXCEPTION_STACKTRACE
+ ]
+ self.assertIn("Traceback (most recent call last)", stacktrace)
+ self.assertIn("raise ValueError", stacktrace)
+
+ def test_emit_logrecord_exception_preserves_user_attributes(self):
+ logger, log_record_processor_mock = self._get_logger()
+ exc = ValueError("boom")
+ log_record = LogRecord(
+ observed_timestamp=0,
+ body="a log line",
+ attributes={exception_attributes.EXCEPTION_TYPE: "custom"},
+ exception=exc,
+ )
+
+ logger.emit(log_record)
+ log_record_processor_mock.on_emit.assert_called_once()
+ log_data = log_record_processor_mock.on_emit.call_args.args[0]
+ attributes = dict(log_data.log_record.attributes)
+ self.assertEqual(
+ attributes[exception_attributes.EXCEPTION_TYPE], "custom"
+ )
+ self.assertEqual(
+ attributes[exception_attributes.EXCEPTION_MESSAGE], "boom"
+ )
+
+ def test_emit_logrecord_exception_with_immutable_attributes(self):
+ logger, log_record_processor_mock = self._get_logger()
+ exc = ValueError("boom")
+ original_attributes = BoundedAttributes(
+ attributes={"custom": "value"},
+ immutable=True,
+ extended_attributes=True,
+ )
+ log_record = LogRecord(
+ observed_timestamp=0,
+ body="a log line",
+ attributes=original_attributes,
+ exception=exc,
+ )
+
+ logger.emit(log_record)
+
+ self.assertNotIn(
+ exception_attributes.EXCEPTION_TYPE, log_record.attributes
+ )
+ log_record_processor_mock.on_emit.assert_called_once()
+ log_data = log_record_processor_mock.on_emit.call_args.args[0]
+ attributes = dict(log_data.log_record.attributes)
+ self.assertEqual(attributes["custom"], "value")
+ self.assertEqual(
+ attributes[exception_attributes.EXCEPTION_TYPE], "ValueError"
+ )
+
+ def test_emit_readwrite_logrecord_uses_exception(self):
+ logger, log_record_processor_mock = self._get_logger()
+ exc = RuntimeError("kaput")
+ log_record = LogRecord(
+ observed_timestamp=0,
+ body="a log line",
+ exception=exc,
+ )
+ readwrite = ReadWriteLogRecord(
+ log_record=log_record,
+ resource=Resource.create({}),
+ instrumentation_scope=logger._instrumentation_scope,
+ )
+
+ logger.emit(readwrite)
+ log_record_processor_mock.on_emit.assert_called_once()
+ log_data = log_record_processor_mock.on_emit.call_args.args[0]
+ attributes = dict(log_data.log_record.attributes)
+ self.assertEqual(
+ attributes[exception_attributes.EXCEPTION_TYPE], "RuntimeError"
+ )
diff --git a/opentelemetry-sdk/tests/logs/test_multi_log_processor.py b/opentelemetry-sdk/tests/logs/test_multi_log_processor.py
index 05fc0fe..1bc1d4f 100644
--- a/opentelemetry-sdk/tests/logs/test_multi_log_processor.py
+++ b/opentelemetry-sdk/tests/logs/test_multi_log_processor.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint:disable=protected-access,no-self-use,no-member
@@ -172,6 +161,36 @@ class TestSynchronousMultiLogRecordProcessor(
self.assertEqual(mock_processor1.force_flush.call_count, 1)
self.assertEqual(mock_processor2.force_flush.call_count, 0)
+ def test_force_flush_processor_returns_false(self):
+ multi_log_record_processor = SynchronousMultiLogRecordProcessor()
+
+ mock_processor1 = Mock(spec=LogRecordProcessor)
+ mock_processor1.force_flush = Mock(return_value=False)
+ multi_log_record_processor.add_log_record_processor(mock_processor1)
+ mock_processor2 = Mock(spec=LogRecordProcessor)
+ mock_processor2.force_flush = Mock(return_value=True)
+ multi_log_record_processor.add_log_record_processor(mock_processor2)
+
+ ret_value = multi_log_record_processor.force_flush(50)
+ self.assertFalse(ret_value)
+ self.assertEqual(mock_processor1.force_flush.call_count, 1)
+ self.assertEqual(mock_processor2.force_flush.call_count, 1)
+
+ def test_force_flush_processor_returns_none(self):
+ multi_log_record_processor = SynchronousMultiLogRecordProcessor()
+
+ mock_processor1 = Mock(spec=LogRecordProcessor)
+ mock_processor1.force_flush = Mock(return_value=None)
+ multi_log_record_processor.add_log_record_processor(mock_processor1)
+ mock_processor2 = Mock(spec=LogRecordProcessor)
+ mock_processor2.force_flush = Mock(return_value=True)
+ multi_log_record_processor.add_log_record_processor(mock_processor2)
+
+ ret_value = multi_log_record_processor.force_flush(50)
+ self.assertTrue(ret_value)
+ self.assertEqual(mock_processor1.force_flush.call_count, 1)
+ self.assertEqual(mock_processor2.force_flush.call_count, 1)
+
class TestConcurrentMultiLogRecordProcessor(
MultiLogRecordProcessorTestBase, unittest.TestCase
@@ -200,3 +219,39 @@ class TestConcurrentMultiLogRecordProcessor(
for mock in mocks:
self.assertEqual(1, mock.force_flush.call_count)
multi_log_record_processor.shutdown()
+
+ def test_force_flush_processor_returns_false(self):
+ multi_log_record_processor = ConcurrentMultiLogRecordProcessor()
+
+ false_mock = Mock(spec=LogRecordProcessor)
+ false_mock.force_flush = Mock(return_value=False)
+ mocks = [Mock(spec=LogRecordProcessor) for _ in range(4)]
+ mocks.insert(0, false_mock)
+
+ for mock_processor in mocks:
+ multi_log_record_processor.add_log_record_processor(mock_processor)
+
+ ret_value = multi_log_record_processor.force_flush()
+
+ self.assertFalse(ret_value)
+ for mock in mocks:
+ self.assertEqual(1, mock.force_flush.call_count)
+ multi_log_record_processor.shutdown()
+
+ def test_force_flush_processor_returns_none(self):
+ multi_log_record_processor = ConcurrentMultiLogRecordProcessor()
+
+ none_mock = Mock(spec=LogRecordProcessor)
+ none_mock.force_flush = Mock(return_value=None)
+ mocks = [Mock(spec=LogRecordProcessor) for _ in range(4)]
+ mocks.insert(0, none_mock)
+
+ for mock_processor in mocks:
+ multi_log_record_processor.add_log_record_processor(mock_processor)
+
+ ret_value = multi_log_record_processor.force_flush()
+
+ self.assertTrue(ret_value)
+ for mock in mocks:
+ self.assertEqual(1, mock.force_flush.call_count)
+ multi_log_record_processor.shutdown()
diff --git a/opentelemetry-sdk/tests/logs/test_sdk_metrics.py b/opentelemetry-sdk/tests/logs/test_sdk_metrics.py
index 5a0e18c..9b915a9 100644
--- a/opentelemetry-sdk/tests/logs/test_sdk_metrics.py
+++ b/opentelemetry-sdk/tests/logs/test_sdk_metrics.py
@@ -1,24 +1,18 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from unittest import TestCase
+from unittest.mock import patch
from opentelemetry.sdk._logs import LoggerProvider
+from opentelemetry.sdk.environment_variables import (
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
+)
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import InMemoryMetricReader
+@patch.dict("os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"})
class TestLoggerProviderMetrics(TestCase):
def setUp(self):
self.metric_reader = InMemoryMetricReader()
@@ -57,3 +51,16 @@ class TestLoggerProviderMetrics(TestCase):
2,
{},
)
+
+
+class TestLoggerProviderMetricsDisabled(TestCase):
+ def test_disabled_by_default(self):
+ metric_reader = InMemoryMetricReader()
+ meter_provider = MeterProvider(metric_readers=[metric_reader])
+ logger_provider = LoggerProvider(meter_provider=meter_provider)
+ logger = logger_provider.get_logger("test")
+
+ logger.emit(body="log1")
+
+ self.assertIsNone(metric_reader.get_metrics_data())
+ meter_provider.shutdown()
diff --git a/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponent_mapping.py b/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponent_mapping.py
index 0e3b0c7..91edf38 100644
--- a/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponent_mapping.py
+++ b/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponent_mapping.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access
diff --git a/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponential_bucket_histogram_aggregation.py b/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponential_bucket_histogram_aggregation.py
index 820eb10..fea16a7 100644
--- a/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponential_bucket_histogram_aggregation.py
+++ b/opentelemetry-sdk/tests/metrics/exponential_histogram/test_exponential_bucket_histogram_aggregation.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access,too-many-lines,invalid-name
# pylint: disable=consider-using-enumerate,no-self-use,too-many-public-methods
diff --git a/opentelemetry-sdk/tests/metrics/exponential_histogram/test_logarithm_mapping.py b/opentelemetry-sdk/tests/metrics/exponential_histogram/test_logarithm_mapping.py
index d8f9c4a..fc208d1 100644
--- a/opentelemetry-sdk/tests/metrics/exponential_histogram/test_logarithm_mapping.py
+++ b/opentelemetry-sdk/tests/metrics/exponential_histogram/test_logarithm_mapping.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access
diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py b/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py
index ca934b1..a3b14e7 100644
--- a/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py
+++ b/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from io import StringIO
from json import loads
diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_cpu_time.py b/opentelemetry-sdk/tests/metrics/integration_test/test_cpu_time.py
index 8b9483d..a0fedc3 100644
--- a/opentelemetry-sdk/tests/metrics/integration_test/test_cpu_time.py
+++ b/opentelemetry-sdk/tests/metrics/integration_test/test_cpu_time.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
import io
-from typing import Generator, Iterable, List
+from collections.abc import Generator, Iterable
from unittest import TestCase
from unittest.mock import Mock, patch
@@ -52,7 +41,7 @@ softirq 1644603067 0 166540056 208 309152755 8936439 0 1354908 935642970 13 2229
@staticmethod
def create_measurements_expected(
instrument: Instrument,
- ) -> List[Measurement]:
+ ) -> list[Measurement]:
return [
Measurement(
6150.29,
diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_disable_default_views.py b/opentelemetry-sdk/tests/metrics/integration_test/test_disable_default_views.py
index d022456..7fcfd62 100644
--- a/opentelemetry-sdk/tests/metrics/integration_test/test_disable_default_views.py
+++ b/opentelemetry-sdk/tests/metrics/integration_test/test_disable_default_views.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from unittest import TestCase
diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_exemplars.py b/opentelemetry-sdk/tests/metrics/integration_test/test_exemplars.py
index c4dabe9..859a1ec 100644
--- a/opentelemetry-sdk/tests/metrics/integration_test/test_exemplars.py
+++ b/opentelemetry-sdk/tests/metrics/integration_test/test_exemplars.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import os
from unittest import TestCase, mock
diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_explicit_bucket_histogram_aggregation.py b/opentelemetry-sdk/tests/metrics/integration_test/test_explicit_bucket_histogram_aggregation.py
index 8aa2321..857daa9 100644
--- a/opentelemetry-sdk/tests/metrics/integration_test/test_explicit_bucket_histogram_aggregation.py
+++ b/opentelemetry-sdk/tests/metrics/integration_test/test_explicit_bucket_histogram_aggregation.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from platform import system
from time import sleep
diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_exponential_bucket_histogram.py b/opentelemetry-sdk/tests/metrics/integration_test/test_exponential_bucket_histogram.py
index 2e066e1..7da23de 100644
--- a/opentelemetry-sdk/tests/metrics/integration_test/test_exponential_bucket_histogram.py
+++ b/opentelemetry-sdk/tests/metrics/integration_test/test_exponential_bucket_histogram.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from platform import system
from time import sleep
diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_exporter_concurrency.py b/opentelemetry-sdk/tests/metrics/integration_test/test_exporter_concurrency.py
index bbc67ea..43d225b 100644
--- a/opentelemetry-sdk/tests/metrics/integration_test/test_exporter_concurrency.py
+++ b/opentelemetry-sdk/tests/metrics/integration_test/test_exporter_concurrency.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import time
from threading import Lock
diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_histogram_advisory_explicit_buckets.py b/opentelemetry-sdk/tests/metrics/integration_test/test_histogram_advisory_explicit_buckets.py
index 569d7fd..8088875 100644
--- a/opentelemetry-sdk/tests/metrics/integration_test/test_histogram_advisory_explicit_buckets.py
+++ b/opentelemetry-sdk/tests/metrics/integration_test/test_histogram_advisory_explicit_buckets.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from unittest import TestCase
diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_histogram_export.py b/opentelemetry-sdk/tests/metrics/integration_test/test_histogram_export.py
index 303ad18..541be17 100644
--- a/opentelemetry-sdk/tests/metrics/integration_test/test_histogram_export.py
+++ b/opentelemetry-sdk/tests/metrics/integration_test/test_histogram_export.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from unittest import TestCase
diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_provider_shutdown.py b/opentelemetry-sdk/tests/metrics/integration_test/test_provider_shutdown.py
index 1f4a16d..e8eda4d 100644
--- a/opentelemetry-sdk/tests/metrics/integration_test/test_provider_shutdown.py
+++ b/opentelemetry-sdk/tests/metrics/integration_test/test_provider_shutdown.py
@@ -1,21 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import gc
import time
import weakref
-from typing import Sequence
+from collections.abc import Sequence
from unittest import TestCase
from opentelemetry.sdk.metrics import MeterProvider
diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_sum_aggregation.py b/opentelemetry-sdk/tests/metrics/integration_test/test_sum_aggregation.py
index 6137141..a8badf2 100644
--- a/opentelemetry-sdk/tests/metrics/integration_test/test_sum_aggregation.py
+++ b/opentelemetry-sdk/tests/metrics/integration_test/test_sum_aggregation.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from itertools import count
from logging import ERROR
diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_time_align.py b/opentelemetry-sdk/tests/metrics/integration_test/test_time_align.py
index b04056f..88567ea 100644
--- a/opentelemetry-sdk/tests/metrics/integration_test/test_time_align.py
+++ b/opentelemetry-sdk/tests/metrics/integration_test/test_time_align.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from platform import system
from time import sleep
diff --git a/opentelemetry-sdk/tests/metrics/test_aggregation.py b/opentelemetry-sdk/tests/metrics/test_aggregation.py
index bacaf35..1f11393 100644
--- a/opentelemetry-sdk/tests/metrics/test_aggregation.py
+++ b/opentelemetry-sdk/tests/metrics/test_aggregation.py
@@ -1,22 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access
from math import inf
from time import sleep, time_ns
-from typing import Union
from unittest import TestCase
from unittest.mock import Mock
@@ -55,7 +43,7 @@ from opentelemetry.util.types import Attributes
def measurement(
- value: Union[int, float], attributes: Attributes = None
+ value: int | float, attributes: Attributes = None
) -> Measurement:
return Measurement(
value,
diff --git a/opentelemetry-sdk/tests/metrics/test_backward_compat.py b/opentelemetry-sdk/tests/metrics/test_backward_compat.py
index 90e885c..f205c25 100644
--- a/opentelemetry-sdk/tests/metrics/test_backward_compat.py
+++ b/opentelemetry-sdk/tests/metrics/test_backward_compat.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
The purpose of this test is to test for backward compatibility with any user-implementable
@@ -25,7 +14,7 @@ backward-compatible way for *callers*.
Ideally, we could use pyright for this as well, but SDK is not type checked atm.
"""
-from typing import Iterable, Sequence
+from collections.abc import Iterable, Sequence
from opentelemetry.metrics import CallbackOptions, Observation
from opentelemetry.sdk.metrics import MeterProvider
diff --git a/opentelemetry-sdk/tests/metrics/test_exemplarfilter.py b/opentelemetry-sdk/tests/metrics/test_exemplarfilter.py
index daca0e6..5c6b583 100644
--- a/opentelemetry-sdk/tests/metrics/test_exemplarfilter.py
+++ b/opentelemetry-sdk/tests/metrics/test_exemplarfilter.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
from unittest import TestCase
from opentelemetry import trace
diff --git a/opentelemetry-sdk/tests/metrics/test_exemplarreservoir.py b/opentelemetry-sdk/tests/metrics/test_exemplarreservoir.py
index bdc25d1..9c5bf69 100644
--- a/opentelemetry-sdk/tests/metrics/test_exemplarreservoir.py
+++ b/opentelemetry-sdk/tests/metrics/test_exemplarreservoir.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
from time import time_ns
from unittest import TestCase
diff --git a/opentelemetry-sdk/tests/metrics/test_import.py b/opentelemetry-sdk/tests/metrics/test_import.py
index 4e18a77..83cbd8b 100644
--- a/opentelemetry-sdk/tests/metrics/test_import.py
+++ b/opentelemetry-sdk/tests/metrics/test_import.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=unused-import,import-outside-toplevel,too-many-locals
diff --git a/opentelemetry-sdk/tests/metrics/test_in_memory_metric_reader.py b/opentelemetry-sdk/tests/metrics/test_in_memory_metric_reader.py
index ff2b743..b4fb0eb 100644
--- a/opentelemetry-sdk/tests/metrics/test_in_memory_metric_reader.py
+++ b/opentelemetry-sdk/tests/metrics/test_in_memory_metric_reader.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access
diff --git a/opentelemetry-sdk/tests/metrics/test_instrument.py b/opentelemetry-sdk/tests/metrics/test_instrument.py
index 4bd10e3..c5375e3 100644
--- a/opentelemetry-sdk/tests/metrics/test_instrument.py
+++ b/opentelemetry-sdk/tests/metrics/test_instrument.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=no-self-use
diff --git a/opentelemetry-sdk/tests/metrics/test_measurement_consumer.py b/opentelemetry-sdk/tests/metrics/test_measurement_consumer.py
index 22abfbd..9f67271 100644
--- a/opentelemetry-sdk/tests/metrics/test_measurement_consumer.py
+++ b/opentelemetry-sdk/tests/metrics/test_measurement_consumer.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=invalid-name,no-self-use
@@ -156,8 +145,9 @@ class TestSynchronousMeasurementConsumer(TestCase):
"opentelemetry.sdk.metrics._internal."
"measurement_consumer.CallbackOptions"
)
+ @patch("opentelemetry.sdk.metrics._internal.measurement_consumer.time_ns")
def test_collect_deadline(
- self, mock_callback_options, MockMetricReaderStorage
+ self, mock_time_ns, mock_callback_options, MockMetricReaderStorage
):
reader_mock = Mock()
reader_storage_mock = Mock()
@@ -171,17 +161,23 @@ class TestSynchronousMeasurementConsumer(TestCase):
)
)
- def sleep_1(*args, **kwargs):
- sleep(1)
- return []
-
consumer.register_asynchronous_instrument(
- Mock(**{"callback.side_effect": sleep_1})
+ Mock(**{"callback.return_value": []})
)
consumer.register_asynchronous_instrument(
- Mock(**{"callback.side_effect": sleep_1})
+ Mock(**{"callback.return_value": []})
)
+ # collect start, first remaining_time, post-first callback,
+ # second remaining_time, post-second callback
+ mock_time_ns.side_effect = [
+ 0,
+ 0,
+ int(1e9),
+ int(1e9),
+ int(2e9),
+ ]
+
consumer.collect(reader_mock)
callback_options_time_call = mock_callback_options.mock_calls[
diff --git a/opentelemetry-sdk/tests/metrics/test_metric_reader.py b/opentelemetry-sdk/tests/metrics/test_metric_reader.py
index 2f7aad2..48fd681 100644
--- a/opentelemetry-sdk/tests/metrics/test_metric_reader.py
+++ b/opentelemetry-sdk/tests/metrics/test_metric_reader.py
@@ -1,20 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access
-from typing import Dict, Iterable
+from collections.abc import Iterable
from unittest import TestCase
from unittest.mock import patch
@@ -54,8 +43,8 @@ _expected_keys = [
class DummyMetricReader(MetricReader):
def __init__(
self,
- preferred_temporality: Dict[type, AggregationTemporality] = None,
- preferred_aggregation: Dict[type, Aggregation] = None,
+ preferred_temporality: dict[type, AggregationTemporality] = None,
+ preferred_aggregation: dict[type, Aggregation] = None,
) -> None:
super().__init__(
preferred_temporality=preferred_temporality,
diff --git a/opentelemetry-sdk/tests/metrics/test_metric_reader_storage.py b/opentelemetry-sdk/tests/metrics/test_metric_reader_storage.py
index ec1456a..ec70ff1 100644
--- a/opentelemetry-sdk/tests/metrics/test_metric_reader_storage.py
+++ b/opentelemetry-sdk/tests/metrics/test_metric_reader_storage.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access,invalid-name
@@ -251,7 +240,7 @@ class TestMetricReaderStorage(ConcurrencyTestBase):
MockViewInstrumentMatch.side_effect = mock_view_instrument_match_ctor
instrument1 = Mock(name="instrument1")
- view1 = mock_view_matching(instrument1)
+ view1 = mock_view_matching("view1", instrument1)
storage = MetricReaderStorage(
SdkConfiguration(
exemplar_filter=Mock(),
@@ -275,8 +264,12 @@ class TestMetricReaderStorage(ConcurrencyTestBase):
# race sending many measurements concurrently
self.run_with_many_threads(send_measurement)
- # _ViewInstrumentMatch constructor should have only been called once
- self.assertEqual(mock_view_instrument_match_ctor.call_count, 1)
+ # Only one cached entry should exist for the instrument, with exactly
+ # one view-instrument match — duplicate initialization is the bug we're guarding against
+ self.assertIn(instrument1, storage._instrument_view_instrument_matches)
+ self.assertEqual(
+ len(storage._instrument_view_instrument_matches[instrument1]), 1
+ )
def test_race_collect_with_new_instruments(self):
storage = MetricReaderStorage(
diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py
index d2f475f..beb8dbb 100644
--- a/opentelemetry-sdk/tests/metrics/test_metrics.py
+++ b/opentelemetry-sdk/tests/metrics/test_metrics.py
@@ -1,25 +1,14 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access,no-self-use,too-many-lines
import weakref
-from collections.abc import Callable
+from collections.abc import Callable, Iterable, Sequence
from logging import WARNING
from threading import Lock
from time import sleep
-from typing import Any, Iterable, Sequence
+from typing import Any
from unittest.mock import MagicMock, Mock, patch
from opentelemetry.attributes import BoundedAttributes
diff --git a/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py b/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py
index 3e47e57..ab8877c 100644
--- a/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py
+++ b/opentelemetry-sdk/tests/metrics/test_periodic_exporting_metric_reader.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access,invalid-name,no-self-use
@@ -19,11 +8,14 @@ import math
import weakref
from logging import WARNING
from time import sleep, time_ns
-from typing import Optional, cast
-from unittest.mock import Mock
+from typing import cast
+from unittest.mock import Mock, patch
import pytest
+from opentelemetry.sdk.environment_variables import (
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
+)
from opentelemetry.sdk.metrics import (
Counter,
MeterProvider,
@@ -91,8 +83,8 @@ class ExceptionAtCollectionPeriodicExportingMetricReader(
self,
exporter: MetricExporter,
exception: Exception,
- export_interval_millis: Optional[float] = None,
- export_timeout_millis: Optional[float] = None,
+ export_interval_millis: float | None = None,
+ export_timeout_millis: float | None = None,
) -> None:
super().__init__(
exporter, export_interval_millis, export_timeout_millis
@@ -315,6 +307,9 @@ class TestPeriodicExportingMetricReader(ConcurrencyTestBase):
"The PeriodicExportingMetricReader object created by this test wasn't garbage collected",
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
def test_metric_reader_metrics(self):
exporter = FakeMetricsExporter()
pmr = PeriodicExportingMetricReader(
diff --git a/opentelemetry-sdk/tests/metrics/test_point.py b/opentelemetry-sdk/tests/metrics/test_point.py
index c5a4def..18fa05c 100644
--- a/opentelemetry-sdk/tests/metrics/test_point.py
+++ b/opentelemetry-sdk/tests/metrics/test_point.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from unittest import TestCase
diff --git a/opentelemetry-sdk/tests/metrics/test_view.py b/opentelemetry-sdk/tests/metrics/test_view.py
index ee5df52..03914c9 100644
--- a/opentelemetry-sdk/tests/metrics/test_view.py
+++ b/opentelemetry-sdk/tests/metrics/test_view.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access
diff --git a/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py b/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py
index 38d3675..1de1af3 100644
--- a/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py
+++ b/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py
@@ -1,22 +1,11 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access
from __future__ import annotations
+from collections.abc import Callable, Sequence
from time import time_ns
-from typing import Callable, Sequence, Type
from unittest import TestCase
from unittest.mock import MagicMock, Mock, patch
@@ -52,9 +41,9 @@ from opentelemetry.sdk.metrics.view import (
def generalized_reservoir_factory(
size: int = 1, boundaries: Sequence[float] | None = None
-) -> Callable[[Type[_Aggregation]], ExemplarReservoirBuilder]:
+) -> Callable[[type[_Aggregation]], ExemplarReservoirBuilder]:
def factory(
- aggregation_type: Type[_Aggregation],
+ aggregation_type: type[_Aggregation],
) -> ExemplarReservoirBuilder:
if issubclass(aggregation_type, _ExplicitBucketHistogramAggregation):
return lambda **kwargs: AlignedHistogramBucketExemplarReservoir(
@@ -266,6 +255,49 @@ class Test_ViewInstrumentMatch(TestCase): # pylint: disable=invalid-name
self.assertEqual(number_data_point.attributes, {"c": "d"})
self.assertEqual(number_data_point.value, 0)
+ def test_consume_measurement_attributes_are_copied(self):
+ """Mutating the attributes dict after recording must not affect stored data points."""
+ instrument1 = _Counter(
+ "instrument1",
+ Mock(),
+ Mock(),
+ description="description",
+ unit="unit",
+ )
+ instrument1.instrumentation_scope = self.mock_instrumentation_scope
+ view_instrument_match = _ViewInstrumentMatch(
+ view=View(
+ instrument_name="instrument1",
+ name="name",
+ aggregation=DefaultAggregation(),
+ ),
+ instrument=instrument1,
+ instrument_class_aggregation=MagicMock(
+ **{"__getitem__.return_value": DefaultAggregation()}
+ ),
+ )
+
+ attributes = {"key": "original"}
+ view_instrument_match.consume_measurement(
+ Measurement(
+ value=1,
+ time_unix_nano=time_ns(),
+ instrument=instrument1,
+ context=Context(),
+ attributes=attributes,
+ )
+ )
+
+ # Mutate the original dict after recording
+ attributes["key"] = "mutated"
+
+ number_data_points = view_instrument_match.collect(
+ AggregationTemporality.CUMULATIVE, 0
+ )
+ number_data_points = list(number_data_points)
+ self.assertEqual(len(number_data_points), 1)
+ self.assertEqual(number_data_points[0].attributes, {"key": "original"})
+
@patch(
"opentelemetry.sdk.metrics._internal._view_instrument_match.time_ns",
side_effect=[0, 1, 2],
diff --git a/opentelemetry-sdk/tests/resources/test_resources.py b/opentelemetry-sdk/tests/resources/test_resources.py
index c083eff..8c6ff46 100644
--- a/opentelemetry-sdk/tests/resources/test_resources.py
+++ b/opentelemetry-sdk/tests/resources/test_resources.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import os
import sys
import unittest
@@ -57,6 +46,9 @@ from opentelemetry.sdk.resources import (
_HostResourceDetector,
get_aggregated_resources,
)
+from opentelemetry.util._importlib_metadata import (
+ entry_points as real_entry_points,
+)
try:
import psutil
@@ -475,6 +467,16 @@ class TestResources(unittest.TestCase):
# pylint: disable=too-many-public-methods
+def _make_detector_ep(resource):
+ return Mock(
+ **{
+ "load.return_value": Mock(
+ return_value=Mock(**{"detect.return_value": resource})
+ )
+ }
+ )
+
+
class TestOTELResourceDetector(unittest.TestCase):
def setUp(self) -> None:
environ[OTEL_RESOURCE_ATTRIBUTES] = ""
@@ -561,7 +563,7 @@ class TestOTELResourceDetector(unittest.TestCase):
)
@patch(
- "sys.argv",
+ "sys.orig_argv",
["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"],
)
def test_process_detector(self):
@@ -607,15 +609,39 @@ class TestOTELResourceDetector(unittest.TestCase):
os.path.dirname(sys.executable),
)
self.assertEqual(
- aggregated_resource.attributes[PROCESS_COMMAND], sys.argv[0]
+ aggregated_resource.attributes[PROCESS_COMMAND], sys.orig_argv[0]
+ )
+ self.assertEqual(
+ aggregated_resource.attributes[PROCESS_COMMAND_LINE],
+ " ".join(sys.orig_argv),
+ )
+ self.assertEqual(
+ aggregated_resource.attributes[PROCESS_COMMAND_ARGS],
+ tuple(sys.orig_argv),
+ )
+
+ @patch("sys.orig_argv", ["/usr/bin/python", "-m", "myapp"])
+ def test_process_detector_uses_orig_argv_for_python_m(self):
+ """For ``python -m <module>`` invocations sys.argv[0] is rewritten to
+ the resolved module path, losing the ``-m <module>`` information.
+ sys.orig_argv preserves the original invocation and must be preferred.
+ See https://github.com/open-telemetry/opentelemetry-python/issues/4518.
+ """
+ aggregated_resource = get_aggregated_resources(
+ [ProcessResourceDetector()], Resource({"foo": "bar"})
+ )
+
+ self.assertEqual(
+ aggregated_resource.attributes[PROCESS_COMMAND],
+ "/usr/bin/python",
)
self.assertEqual(
aggregated_resource.attributes[PROCESS_COMMAND_LINE],
- " ".join(sys.argv),
+ "/usr/bin/python -m myapp",
)
self.assertEqual(
aggregated_resource.attributes[PROCESS_COMMAND_ARGS],
- tuple(sys.argv),
+ ("/usr/bin/python", "-m", "myapp"),
)
def test_resource_detector_entry_points_default(self):
@@ -651,7 +677,7 @@ class TestOTELResourceDetector(unittest.TestCase):
environ, {OTEL_EXPERIMENTAL_RESOURCE_DETECTORS: "mock"}, clear=True
)
@patch(
- "opentelemetry.sdk.resources.entry_points",
+ "opentelemetry.util._importlib_metadata.entry_points",
Mock(
return_value=[
Mock(
@@ -774,6 +800,54 @@ class TestOTELResourceDetector(unittest.TestCase):
self.assertIn(PROCESS_RUNTIME_VERSION, resource.attributes.keys())
self.assertEqual(resource.schema_url, "")
+ @patch.dict(
+ environ,
+ {OTEL_EXPERIMENTAL_RESOURCE_DETECTORS: "mock_a,mock_b"},
+ clear=True,
+ )
+ def test_resource_detector_ordering_last_wins(self):
+ """Last detector in OTEL_EXPERIMENTAL_RESOURCE_DETECTORS wins on conflict."""
+ ep_a = _make_detector_ep(Resource({"conflict_key": "from_a"}))
+ ep_b = _make_detector_ep(Resource({"conflict_key": "from_b"}))
+
+ def side_effect(*args, **kwargs):
+ return {"mock_a": [ep_a], "mock_b": [ep_b]}.get(
+ kwargs.get("name", ""), []
+ )
+
+ with patch(
+ "opentelemetry.util._importlib_metadata.entry_points",
+ side_effect=side_effect,
+ ):
+ resource = Resource({}).create()
+
+ self.assertEqual(resource.attributes["conflict_key"], "from_b")
+
+ @patch.dict(
+ environ,
+ {
+ OTEL_EXPERIMENTAL_RESOURCE_DETECTORS: "mock",
+ OTEL_RESOURCE_ATTRIBUTES: "conflict_key=otel_value",
+ },
+ clear=True,
+ )
+ def test_otel_detector_appended_last(self):
+ """'otel' detector is always appended last, so its attributes win over earlier detectors."""
+ ep_mock = _make_detector_ep(Resource({"conflict_key": "mock_value"}))
+
+ def side_effect(*args, **kwargs):
+ if kwargs.get("name") == "mock":
+ return [ep_mock]
+ return real_entry_points(*args, **kwargs)
+
+ with patch(
+ "opentelemetry.util._importlib_metadata.entry_points",
+ side_effect=side_effect,
+ ):
+ resource = Resource({}).create()
+
+ self.assertEqual(resource.attributes["conflict_key"], "otel_value")
+
@patch("platform.system", lambda: "Linux")
@patch("platform.release", lambda: "666.5.0-35-generic")
def test_os_detector_linux(self):
diff --git a/opentelemetry-sdk/tests/shared_internal/__init__.py b/opentelemetry-sdk/tests/shared_internal/__init__.py
index b0a6f42..e57cf4a 100644
--- a/opentelemetry-sdk/tests/shared_internal/__init__.py
+++ b/opentelemetry-sdk/tests/shared_internal/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py
index f5ed151..0233775 100644
--- a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py
+++ b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=protected-access
import gc
diff --git a/opentelemetry-sdk/tests/test_configurator.py b/opentelemetry-sdk/tests/test_configurator.py
index 17a2b85..fbf4bd3 100644
--- a/opentelemetry-sdk/tests/test_configurator.py
+++ b/opentelemetry-sdk/tests/test_configurator.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
# pylint: skip-file
@@ -18,9 +7,9 @@ from __future__ import annotations
import logging
import logging.config
+from collections.abc import Iterable, Sequence
from logging import WARNING, getLogger
from os import environ
-from typing import Iterable, Optional, Sequence
from unittest import TestCase, mock
from unittest.mock import Mock, patch
@@ -273,7 +262,7 @@ class CustomSampler(Sampler):
def should_sample(
self,
- parent_context: Optional[Context],
+ parent_context: Context | None,
trace_id: int,
name: str,
kind: SpanKind = None,
@@ -338,6 +327,9 @@ class CustomIdGenerator(IdGenerator):
def generate_trace_id(self):
pass
+ def is_trace_id_random(self):
+ return False
+
class TestTraceInit(TestCase):
def setUp(self):
diff --git a/opentelemetry-sdk/tests/test_environment_variables_internal.py b/opentelemetry-sdk/tests/test_environment_variables_internal.py
new file mode 100644
index 0000000..e261703
--- /dev/null
+++ b/opentelemetry-sdk/tests/test_environment_variables_internal.py
@@ -0,0 +1,62 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+from unittest import TestCase
+from unittest.mock import patch
+
+from opentelemetry.sdk.environment_variables._internal import (
+ parse_boolean_environment_variable,
+)
+
+
+class TestParseBooleanEnvironmentVariable(TestCase):
+ def test_unset_returns_default(self):
+ for default, expected in (
+ (False, False),
+ (True, True),
+ ):
+ with self.subTest(default=default):
+ with patch.dict("os.environ", {}, clear=True):
+ self.assertEqual(
+ parse_boolean_environment_variable(
+ "TEST_BOOL", default=default
+ ),
+ expected,
+ )
+
+ def test_valid_values(self):
+ for value, expected in (
+ ("true", True),
+ (" TrUe ", True),
+ ("false", False),
+ (" FaLsE ", False),
+ ):
+ with self.subTest(value=value):
+ with patch.dict("os.environ", {"TEST_BOOL": value}):
+ self.assertEqual(
+ parse_boolean_environment_variable("TEST_BOOL"),
+ expected,
+ )
+
+ def test_invalid_value_warns_and_returns_default(self):
+ for default, expected in (
+ (False, False),
+ (True, True),
+ ):
+ with self.subTest(default=default):
+ with patch.dict("os.environ", {"TEST_BOOL": "yes"}):
+ with self.assertLogs(
+ "opentelemetry.sdk.environment_variables._internal",
+ level="WARNING",
+ ) as logs:
+ self.assertEqual(
+ parse_boolean_environment_variable(
+ "TEST_BOOL", default=default
+ ),
+ expected,
+ )
+
+ self.assertIn(
+ "Invalid value for TEST_BOOL",
+ logs.records[0].message,
+ )
diff --git a/opentelemetry-sdk/tests/test_util.py b/opentelemetry-sdk/tests/test_util.py
index cd255d5..1b7e81e 100644
--- a/opentelemetry-sdk/tests/test_util.py
+++ b/opentelemetry-sdk/tests/test_util.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import copy
import unittest
diff --git a/opentelemetry-sdk/tests/trace/__init__.py b/opentelemetry-sdk/tests/trace/__init__.py
index b0a6f42..e57cf4a 100644
--- a/opentelemetry-sdk/tests/trace/__init__.py
+++ b/opentelemetry-sdk/tests/trace/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/opentelemetry-sdk/tests/trace/composite_sampler/test_always_off.py b/opentelemetry-sdk/tests/trace/composite_sampler/test_always_off.py
index 0a03344..0b5fe34 100644
--- a/opentelemetry-sdk/tests/trace/composite_sampler/test_always_off.py
+++ b/opentelemetry-sdk/tests/trace/composite_sampler/test_always_off.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.sdk.trace._sampling_experimental import (
composable_always_off,
diff --git a/opentelemetry-sdk/tests/trace/composite_sampler/test_always_on.py b/opentelemetry-sdk/tests/trace/composite_sampler/test_always_on.py
index a787b22..9b9673a 100644
--- a/opentelemetry-sdk/tests/trace/composite_sampler/test_always_on.py
+++ b/opentelemetry-sdk/tests/trace/composite_sampler/test_always_on.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.sdk.trace._sampling_experimental import (
composable_always_on,
diff --git a/opentelemetry-sdk/tests/trace/composite_sampler/test_rule_based.py b/opentelemetry-sdk/tests/trace/composite_sampler/test_rule_based.py
index f2b7cdd..a6bc11c 100644
--- a/opentelemetry-sdk/tests/trace/composite_sampler/test_rule_based.py
+++ b/opentelemetry-sdk/tests/trace/composite_sampler/test_rule_based.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.sdk.trace._sampling_experimental import (
composable_always_off,
diff --git a/opentelemetry-sdk/tests/trace/composite_sampler/test_sampler.py b/opentelemetry-sdk/tests/trace/composite_sampler/test_sampler.py
index 4bd45fd..967defa 100644
--- a/opentelemetry-sdk/tests/trace/composite_sampler/test_sampler.py
+++ b/opentelemetry-sdk/tests/trace/composite_sampler/test_sampler.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
diff --git a/opentelemetry-sdk/tests/trace/composite_sampler/test_traceid_ratio.py b/opentelemetry-sdk/tests/trace/composite_sampler/test_traceid_ratio.py
index ad0dc1f..b092fbd 100644
--- a/opentelemetry-sdk/tests/trace/composite_sampler/test_traceid_ratio.py
+++ b/opentelemetry-sdk/tests/trace/composite_sampler/test_traceid_ratio.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import pytest
diff --git a/opentelemetry-sdk/tests/trace/composite_sampler/test_tracestate.py b/opentelemetry-sdk/tests/trace/composite_sampler/test_tracestate.py
index 0af527c..3fe09b8 100644
--- a/opentelemetry-sdk/tests/trace/composite_sampler/test_tracestate.py
+++ b/opentelemetry-sdk/tests/trace/composite_sampler/test_tracestate.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import pytest
diff --git a/opentelemetry-sdk/tests/trace/export/__init__.py b/opentelemetry-sdk/tests/trace/export/__init__.py
index b0a6f42..e57cf4a 100644
--- a/opentelemetry-sdk/tests/trace/export/__init__.py
+++ b/opentelemetry-sdk/tests/trace/export/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/opentelemetry-sdk/tests/trace/export/test_export.py b/opentelemetry-sdk/tests/trace/export/test_export.py
index 2d1321d..d67aceb 100644
--- a/opentelemetry-sdk/tests/trace/export/test_export.py
+++ b/opentelemetry-sdk/tests/trace/export/test_export.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import os
@@ -28,6 +17,7 @@ from opentelemetry.sdk.environment_variables import (
OTEL_BSP_MAX_EXPORT_BATCH_SIZE,
OTEL_BSP_MAX_QUEUE_SIZE,
OTEL_BSP_SCHEDULE_DELAY,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
)
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import InMemoryMetricReader
@@ -146,6 +136,9 @@ class TestSimpleSpanProcessor(unittest.TestCase):
self.assertListEqual([], spans_names_list)
+ @mock.patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
def test_metrics(self):
metric_reader = InMemoryMetricReader()
meter_provider = MeterProvider(metric_readers=[metric_reader])
@@ -397,6 +390,9 @@ class TestBatchSpanProcessor(unittest.TestCase):
max_export_batch_size=512,
)
+ @mock.patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
def test_metrics(self): # pylint: disable=too-many-locals,too-many-statements
metric_reader = InMemoryMetricReader()
meter_provider = MeterProvider(metric_readers=[metric_reader])
diff --git a/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py b/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py
index eb36672..7f30f70 100644
--- a/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py
+++ b/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
from unittest import mock
diff --git a/opentelemetry-sdk/tests/trace/test_globals.py b/opentelemetry-sdk/tests/trace/test_globals.py
index ab57ff0..28accb7 100644
--- a/opentelemetry-sdk/tests/trace/test_globals.py
+++ b/opentelemetry-sdk/tests/trace/test_globals.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
# type:ignore
import unittest
from logging import WARNING
diff --git a/opentelemetry-sdk/tests/trace/test_implementation.py b/opentelemetry-sdk/tests/trace/test_implementation.py
index 961e68d..e7fc51a 100644
--- a/opentelemetry-sdk/tests/trace/test_implementation.py
+++ b/opentelemetry-sdk/tests/trace/test_implementation.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
diff --git a/opentelemetry-sdk/tests/trace/test_sampling.py b/opentelemetry-sdk/tests/trace/test_sampling.py
index 09057ee..1d33a1a 100644
--- a/opentelemetry-sdk/tests/trace/test_sampling.py
+++ b/opentelemetry-sdk/tests/trace/test_sampling.py
@@ -1,20 +1,8 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import contextlib
import sys
-import typing
import unittest
from opentelemetry import context as context_api
@@ -67,7 +55,7 @@ class TestSamplingResult(unittest.TestCase):
class TestSampler(unittest.TestCase):
def _create_parent(
self, trace_flags: trace.TraceFlags, is_remote=False, trace_state=None
- ) -> typing.Optional[context_api.Context]:
+ ) -> context_api.Context | None:
if trace_flags is None:
return None
return trace.set_span_in_context(
diff --git a/opentelemetry-sdk/tests/trace/test_sdk_metrics.py b/opentelemetry-sdk/tests/trace/test_sdk_metrics.py
index 2baa967..cd1d91c 100644
--- a/opentelemetry-sdk/tests/trace/test_sdk_metrics.py
+++ b/opentelemetry-sdk/tests/trace/test_sdk_metrics.py
@@ -1,20 +1,13 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from unittest import TestCase
+from unittest.mock import patch
from opentelemetry import trace as trace_api
+from opentelemetry.sdk.environment_variables import (
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
+)
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import InMemoryMetricReader
from opentelemetry.sdk.trace import TracerProvider
@@ -27,6 +20,7 @@ from opentelemetry.sdk.trace.sampling import (
from opentelemetry.trace.span import SpanContext
+@patch.dict("os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"})
class TestTracerProviderMetrics(TestCase):
def setUp(self):
self.metric_reader = InMemoryMetricReader()
@@ -159,6 +153,7 @@ class TestTracerProviderMetrics(TestCase):
},
)
self.assert_live_spans(metric_data, None, {})
+
span.end()
metric_data = self.metric_reader.get_metrics_data()
self.assert_started_spans(
@@ -242,3 +237,19 @@ class TestTracerProviderMetrics(TestCase):
},
)
self.assert_live_spans(metric_data, None, {})
+
+
+class TestTracerProviderMetricsDisabled(TestCase):
+ def test_disabled_by_default(self):
+ metric_reader = InMemoryMetricReader()
+ meter_provider = MeterProvider(metric_readers=[metric_reader])
+ tracer_provider = TracerProvider(
+ sampler=ALWAYS_ON, meter_provider=meter_provider
+ )
+ tracer = tracer_provider.get_tracer("test")
+
+ with tracer.start_as_current_span("span"):
+ pass
+
+ self.assertIsNone(metric_reader.get_metrics_data())
+ meter_provider.shutdown()
diff --git a/opentelemetry-sdk/tests/trace/test_span_processor.py b/opentelemetry-sdk/tests/trace/test_span_processor.py
index 0ba4ab8..4499427 100644
--- a/opentelemetry-sdk/tests/trace/test_span_processor.py
+++ b/opentelemetry-sdk/tests/trace/test_span_processor.py
@@ -1,32 +1,16 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import abc
import gc
import multiprocessing
import os
import time
-import typing
import unittest
import weakref
-from platform import python_implementation, system
from threading import Event
-from typing import Optional
from unittest import mock
-from pytest import mark
-
from opentelemetry import trace as trace_api
from opentelemetry.context import Context
from opentelemetry.sdk import trace
@@ -54,7 +38,7 @@ class MySpanProcessor(trace.SpanProcessor):
self.span_list = span_list
def on_start(
- self, span: "trace.Span", parent_context: Optional[Context] = None
+ self, span: "trace.Span", parent_context: Context | None = None
) -> None:
self.span_list.append(span_event_start_fmt(self.name, span.name))
@@ -276,9 +260,10 @@ class MultiSpanProcessorTestBase(abc.ABC):
@abc.abstractmethod
def create_multi_span_processor(
self,
- ) -> typing.Union[
- trace.SynchronousMultiSpanProcessor, trace.ConcurrentMultiSpanProcessor
- ]:
+ ) -> (
+ trace.SynchronousMultiSpanProcessor
+ | trace.ConcurrentMultiSpanProcessor
+ ):
pass
@staticmethod
@@ -438,7 +423,35 @@ class TestSynchronousMultiSpanProcessor(
flushed = multi_processor.force_flush(50)
self.assertFalse(flushed)
self.assertEqual(1, mock_processor1.force_flush.call_count)
- self.assertEqual(0, mock_processor2.force_flush.call_count)
+ self.assertEqual(1, mock_processor2.force_flush.call_count)
+
+ def test_force_flush_processor_returns_none(self):
+ multi_processor = trace.SynchronousMultiSpanProcessor()
+
+ mock_processor1 = mock.Mock(spec=trace.SpanProcessor)
+ mock_processor1.force_flush = mock.Mock(return_value=None)
+ multi_processor.add_span_processor(mock_processor1)
+ mock_processor2 = mock.Mock(spec=trace.SpanProcessor)
+ mock_processor2.force_flush = mock.Mock(return_value=True)
+ multi_processor.add_span_processor(mock_processor2)
+
+ flushed = multi_processor.force_flush(50)
+ self.assertTrue(flushed)
+ self.assertEqual(1, mock_processor1.force_flush.call_count)
+ self.assertEqual(1, mock_processor2.force_flush.call_count)
+
+ def test_force_flush_default_processor(self):
+ multi_processor = trace.SynchronousMultiSpanProcessor()
+
+ default_processor = trace.SpanProcessor()
+ multi_processor.add_span_processor(default_processor)
+ mock_processor = mock.Mock(spec=trace.SpanProcessor)
+ mock_processor.force_flush = mock.Mock(return_value=True)
+ multi_processor.add_span_processor(mock_processor)
+
+ flushed = multi_processor.force_flush(50)
+ self.assertTrue(flushed)
+ self.assertEqual(1, mock_processor.force_flush.call_count)
class TestConcurrentMultiSpanProcessor(
@@ -449,10 +462,6 @@ class TestConcurrentMultiSpanProcessor(
) -> trace.ConcurrentMultiSpanProcessor:
return trace.ConcurrentMultiSpanProcessor(3)
- @mark.skipif(
- python_implementation() == "PyPy" and system() == "Windows",
- reason="This test randomly fails in Windows with PyPy",
- )
def test_force_flush_late_by_timeout(self):
multi_processor = trace.ConcurrentMultiSpanProcessor(5)
wait_event = Event()
@@ -468,7 +477,7 @@ class TestConcurrentMultiSpanProcessor(
for mock_processor in mocks:
multi_processor.add_span_processor(mock_processor)
- flushed = multi_processor.force_flush(timeout_millis=10)
+ flushed = multi_processor.force_flush(timeout_millis=25)
# let the thread executing the late_mock continue
wait_event.set()
@@ -495,6 +504,24 @@ class TestConcurrentMultiSpanProcessor(
self.assertEqual(1, mock_processor.force_flush.call_count)
multi_processor.shutdown()
+ def test_force_flush_processor_returns_none(self):
+ multi_processor = trace.ConcurrentMultiSpanProcessor(5)
+
+ none_mock = mock.Mock(spec=trace.SpanProcessor)
+ none_mock.force_flush = mock.Mock(return_value=None)
+ mocks = [mock.Mock(spec=trace.SpanProcessor) for _ in range(0, 4)]
+ mocks.insert(0, none_mock)
+
+ for mock_processor in mocks:
+ multi_processor.add_span_processor(mock_processor)
+
+ flushed = multi_processor.force_flush()
+
+ self.assertTrue(flushed)
+ for mock_processor in mocks:
+ self.assertEqual(1, mock_processor.force_flush.call_count)
+ multi_processor.shutdown()
+
def test_processor_gc(self):
multi_processor = trace.ConcurrentMultiSpanProcessor(5)
weak_ref = weakref.ref(multi_processor)
diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py
index d4ee490..0b07908 100644
--- a/opentelemetry-sdk/tests/trace/test_trace.py
+++ b/opentelemetry-sdk/tests/trace/test_trace.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-lines
# pylint: disable=no-member
@@ -24,7 +13,6 @@ from importlib import reload
from logging import ERROR, WARNING
from random import randint
from time import time_ns
-from typing import Optional
from unittest import mock
from unittest.mock import Mock, patch
@@ -278,15 +266,8 @@ class TestTracerSampling(unittest.TestCase):
self.assertIsInstance(root_span, trace.Span)
child_span = tracer.start_span(name="child span", context=ctx)
self.assertIsInstance(child_span, trace.Span)
- self.assertTrue(root_span.context.trace_flags.sampled)
- self.assertEqual(
- root_span.get_span_context().trace_flags,
- trace_api.TraceFlags.SAMPLED,
- )
- self.assertEqual(
- child_span.get_span_context().trace_flags,
- trace_api.TraceFlags.SAMPLED,
- )
+ self.assertTrue(root_span.get_span_context().trace_flags.sampled)
+ self.assertTrue(child_span.get_span_context().trace_flags.sampled)
def test_default_sampler_type(self):
tracer_provider = trace.TracerProvider()
@@ -304,14 +285,8 @@ class TestTracerSampling(unittest.TestCase):
self.assertIsInstance(root_span, trace_api.NonRecordingSpan)
child_span = tracer.start_span(name="child span", context=ctx)
self.assertIsInstance(child_span, trace_api.NonRecordingSpan)
- self.assertEqual(
- root_span.get_span_context().trace_flags,
- trace_api.TraceFlags.DEFAULT,
- )
- self.assertEqual(
- child_span.get_span_context().trace_flags,
- trace_api.TraceFlags.DEFAULT,
- )
+ self.assertFalse(root_span.get_span_context().trace_flags.sampled)
+ self.assertFalse(child_span.get_span_context().trace_flags.sampled)
self.assertFalse(_get_from_env_or_default.called)
@mock.patch.dict("os.environ", {OTEL_TRACES_SAMPLER: "always_off"})
@@ -531,9 +506,8 @@ class TestSpanCreation(unittest.TestCase):
other_parent.get_span_context().trace_state,
child_context.trace_state,
)
- self.assertEqual(
- other_parent.get_span_context().trace_flags,
- child_context.trace_flags,
+ self.assertTrue(
+ other_parent.get_span_context().trace_flags.sampled
)
# Verify start_span() did not set the current span.
@@ -543,6 +517,35 @@ class TestSpanCreation(unittest.TestCase):
self.assertIs(trace_api.get_current_span(), root)
self.assertIsNotNone(child.end_time)
+ def test_start_span_preserves_parent_random_trace_id_flag(self):
+ tracer = new_tracer()
+
+ for parent_trace_flags in (
+ trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED),
+ trace_api.TraceFlags(
+ trace_api.TraceFlags.SAMPLED
+ | trace_api.TraceFlags.RANDOM_TRACE_ID
+ ),
+ ):
+ with self.subTest(parent_trace_flags=parent_trace_flags):
+ parent_context = trace_api.SpanContext(
+ trace_id=0x000000000000000000000000DEADBEEF,
+ span_id=0x00000000DEADBEF0,
+ is_remote=True,
+ trace_flags=parent_trace_flags,
+ )
+ context = trace_api.set_span_in_context(
+ trace_api.NonRecordingSpan(parent_context)
+ )
+
+ child = tracer.start_span("child", context)
+ child_trace_flags = child.get_span_context().trace_flags
+
+ self.assertEqual(
+ parent_trace_flags.random_trace_id,
+ child_trace_flags.random_trace_id,
+ )
+
def test_start_as_current_span_implicit(self):
tracer = new_tracer()
@@ -957,10 +960,7 @@ class TestSpan(unittest.TestCase):
self.assertEqual(len(root.attributes), 2)
self.assertEqual(root.attributes["sampler-attr"], "sample-val")
self.assertEqual(root.attributes["attr-in-both"], "decision-attr")
- self.assertEqual(
- root.get_span_context().trace_flags,
- trace_api.TraceFlags.SAMPLED,
- )
+ self.assertTrue(root.get_span_context().trace_flags.sampled)
def test_events(self):
self.assertEqual(trace_api.get_current_span(), trace_api.INVALID_SPAN)
@@ -1572,7 +1572,7 @@ class MySpanProcessor(trace.SpanProcessor):
self.span_list = span_list
def on_start(
- self, span: "trace.Span", parent_context: Optional[Context] = None
+ self, span: "trace.Span", parent_context: Context | None = None
) -> None:
self.span_list.append(span_event_start_fmt(self.name, span.name))
@@ -2197,6 +2197,9 @@ class TestTraceFlags(unittest.TestCase):
def test_constant_sampled(self):
self.assertEqual(trace_api.TraceFlags.SAMPLED, 1)
+ def test_constant_random_trace_id(self):
+ self.assertEqual(trace_api.TraceFlags.RANDOM_TRACE_ID, 2)
+
def test_get_default(self):
self.assertEqual(
trace_api.TraceFlags.get_default(), trace_api.TraceFlags.DEFAULT
@@ -2208,6 +2211,14 @@ class TestTraceFlags(unittest.TestCase):
def test_sampled_false(self):
self.assertFalse(trace_api.TraceFlags(0xF0).sampled)
+ def test_random_trace_id_true(self):
+ self.assertTrue(trace_api.TraceFlags(0xF2).random_trace_id)
+ self.assertTrue(trace_api.TraceFlags(0xF3).random_trace_id)
+
+ def test_random_trace_id_false(self):
+ self.assertFalse(trace_api.TraceFlags(0xF0).random_trace_id)
+ self.assertFalse(trace_api.TraceFlags(0xF1).random_trace_id)
+
def test_constant_default_trace_options(self):
self.assertEqual(
trace_api.DEFAULT_TRACE_OPTIONS, trace_api.TraceFlags.DEFAULT
@@ -2231,6 +2242,7 @@ class TestParentChildSpanException(unittest.TestCase):
exception_type = exception.__class__.__name__
exception_message = exception.args[0]
+ child_span = None
try:
with tracer.start_as_current_span(
"parent",
@@ -2245,6 +2257,8 @@ class TestParentChildSpanException(unittest.TestCase):
except Exception: # pylint: disable=broad-exception-caught
pass
+ self.assertIsNotNone(child_span)
+
self.assertTrue(child_span.status.is_ok)
self.assertIsNone(child_span.status.description)
self.assertTupleEqual(child_span.events, ())
@@ -2275,6 +2289,7 @@ class TestParentChildSpanException(unittest.TestCase):
exception = Exception("exception")
+ child_span = None
try:
with tracer.start_as_current_span(
"parent",
@@ -2290,6 +2305,8 @@ class TestParentChildSpanException(unittest.TestCase):
except Exception: # pylint: disable=broad-exception-caught
pass
+ self.assertIsNotNone(child_span)
+
self.assertTrue(child_span.status.is_ok)
self.assertIsNone(child_span.status.description)
self.assertTupleEqual(child_span.events, ())
@@ -2491,3 +2508,7 @@ class TestRandomIdGenerator(unittest.TestCase):
self.assertNotEqual(trace_id, trace_api.INVALID_TRACE_ID)
mock_getrandbits.assert_any_call(128)
self.assertEqual(mock_getrandbits.call_count, 2)
+
+ def test_is_trace_id_random_returns_true(self):
+ generator = RandomIdGenerator()
+ self.assertTrue(generator.is_trace_id_random())
diff --git a/opentelemetry-semantic-conventions/.pylintrc b/opentelemetry-semantic-conventions/.pylintrc
index 1ac1d17..a918ba2 100644
--- a/opentelemetry-semantic-conventions/.pylintrc
+++ b/opentelemetry-semantic-conventions/.pylintrc
@@ -45,7 +45,7 @@ suggestion-mode=yes
unsafe-load-any-extension=no
# Run python dependant checks considering the baseline version
-py-version=3.9
+py-version=3.10
[MESSAGES CONTROL]
diff --git a/opentelemetry-semantic-conventions/pyproject.toml b/opentelemetry-semantic-conventions/pyproject.toml
index d762faa..b6127e6 100644
--- a/opentelemetry-semantic-conventions/pyproject.toml
+++ b/opentelemetry-semantic-conventions/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenTelemetry Semantic Conventions"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -18,7 +18,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -27,7 +26,7 @@ classifiers = [
]
dependencies = [
- "opentelemetry-api == 1.41.0",
+ "opentelemetry-api == 1.42.1",
"typing-extensions >= 4.5.0",
]
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/app_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/app_attributes.py
index 6500e67..a833a5f 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/app_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/app_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/artifact_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/artifact_attributes.py
index 4f06234..7b2afcd 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/artifact_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/artifact_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/aws_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/aws_attributes.py
index da42769..464de70 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/aws_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/aws_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
@@ -209,7 +198,7 @@ Note: This may be different from `cloud.resource_id` if an alias is involved.
AWS_LAMBDA_RESOURCE_MAPPING_ID: Final = "aws.lambda.resource_mapping.id"
"""
-The UUID of the [AWS Lambda EvenSource Mapping](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html). An event source is mapped to a lambda function. It's contents are read by Lambda and used to trigger a function. This isn't available in the lambda execution context or the lambda runtime environtment. This is going to be populated by the AWS SDK for each language when that UUID is present. Some of these operations are Create/Delete/Get/List/Update EventSourceMapping.
+The UUID of the [AWS Lambda EvenSource Mapping](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html). An event source is mapped to a lambda function. It's contents are read by Lambda and used to trigger a function. This isn't available in the lambda execution context or the lambda runtime environment. This is going to be populated by the AWS SDK for each language when that UUID is present. Some of these operations are Create/Delete/Get/List/Update EventSourceMapping.
"""
AWS_LOG_GROUP_ARNS: Final = "aws.log.group.arns"
@@ -312,7 +301,7 @@ This applies in particular to the following operations:
AWS_SECRETSMANAGER_SECRET_ARN: Final = "aws.secretsmanager.secret.arn"
"""
-The ARN of the Secret stored in the Secrets Mangger.
+The ARN of the Secret stored in the Secrets Manager.
"""
AWS_SNS_TOPIC_ARN: Final = "aws.sns.topic.arn"
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/az_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/az_attributes.py
index 7e3813b..7595d9a 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/az_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/az_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/azure_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/azure_attributes.py
index d0ec046..9aad1bd 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/azure_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/azure_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/browser_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/browser_attributes.py
index 7cb1408..3bf59e3 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/browser_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/browser_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cassandra_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cassandra_attributes.py
index 96aae6d..5084407 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cassandra_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cassandra_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cicd_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cicd_attributes.py
index af012bb..38e684d 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cicd_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cicd_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
@@ -53,6 +42,7 @@ The human readable name of a task within a pipeline. Task here most closely alig
CICD_PIPELINE_TASK_RUN_ID: Final = "cicd.pipeline.task.run.id"
"""
The unique identifier of a task run within a pipeline.
+Note: For a given pipeline run and task, the `cicd.pipeline.task.run.id` MUST be unique within that run. For the same task across different runs of the same pipeline, the `cicd.pipeline.task.run.id` MAY remain the same, enabling correlation of `cicd.pipeline.task.run.result` values across multiple pipeline runs.
"""
CICD_PIPELINE_TASK_RUN_RESULT: Final = "cicd.pipeline.task.run.result"
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/client_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/client_attributes.py
index a6511e7..b68dea7 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/client_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/client_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloud_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloud_attributes.py
index 983a804..6191a21 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloud_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloud_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloudevents_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloudevents_attributes.py
index ca13ee9..fe3226a 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloudevents_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloudevents_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloudfoundry_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloudfoundry_attributes.py
index 31b2d85..c1747b0 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloudfoundry_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cloudfoundry_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/code_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/code_attributes.py
index e033b1f..130aebc 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/code_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/code_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/container_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/container_attributes.py
index 1ecacdd..8270445 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/container_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/container_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
@@ -102,7 +91,7 @@ Deprecated: Replaced by `container.runtime.name`.
CONTAINER_RUNTIME_DESCRIPTION: Final = "container.runtime.description"
"""
-A description about the runtime which could include, for example details about the CRI/API version being used or other customisations.
+A description about the runtime which could include, for example details about the CRI/API version being used or other customizations.
"""
CONTAINER_RUNTIME_NAME: Final = "container.runtime.name"
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cpu_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cpu_attributes.py
index e550c56..4cc8bd4 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cpu_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cpu_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cpython_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cpython_attributes.py
index 1f6659a..90ef205 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cpython_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/cpython_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/db_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/db_attributes.py
index e6d5335..39097bc 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/db_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/db_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/deployment_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/deployment_attributes.py
index 1461a89..19a4a1d 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/deployment_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/deployment_attributes.py
@@ -1,20 +1,11 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
+from typing_extensions import deprecated
+
DEPLOYMENT_ENVIRONMENT: Final = "deployment.environment"
"""
Deprecated: Replaced by `deployment.environment.name`.
@@ -22,14 +13,7 @@ Deprecated: Replaced by `deployment.environment.name`.
DEPLOYMENT_ENVIRONMENT_NAME: Final = "deployment.environment.name"
"""
-Name of the [deployment environment](https://wikipedia.org/wiki/Deployment_environment) (aka deployment tier).
-Note: `deployment.environment.name` does not affect the uniqueness constraints defined through
-the `service.namespace`, `service.name` and `service.instance.id` resource attributes.
-This implies that resources carrying the following attribute combinations MUST be
-considered to be identifying the same service:
-
-- `service.name=frontend`, `deployment.environment.name=production`
-- `service.name=frontend`, `deployment.environment.name=staging`.
+Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.deployment_attributes.DEPLOYMENT_ENVIRONMENT_NAME`.
"""
DEPLOYMENT_ID: Final = "deployment.id"
@@ -48,6 +32,20 @@ The status of the deployment.
"""
+@deprecated(
+ "Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.deployment_attributes.DeploymentEnvironmentNameValues`."
+)
+class DeploymentEnvironmentNameValues(Enum):
+ PRODUCTION = "production"
+ """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.deployment_attributes.DeploymentEnvironmentNameValues.PRODUCTION`."""
+ STAGING = "staging"
+ """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.deployment_attributes.DeploymentEnvironmentNameValues.STAGING`."""
+ TEST = "test"
+ """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.deployment_attributes.DeploymentEnvironmentNameValues.TEST`."""
+ DEVELOPMENT = "development"
+ """Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.deployment_attributes.DeploymentEnvironmentNameValues.DEVELOPMENT`."""
+
+
class DeploymentStatusValues(Enum):
FAILED = "failed"
"""failed."""
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/destination_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/destination_attributes.py
index 8fa4949..b9269b7 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/destination_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/destination_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/device_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/device_attributes.py
index 4af3f95..87dcaaa 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/device_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/device_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/disk_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/disk_attributes.py
index e100f1a..1e08f41 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/disk_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/disk_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/dns_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/dns_attributes.py
index bcb744c..83def12 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/dns_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/dns_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/elasticsearch_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/elasticsearch_attributes.py
index 2424374..8674875 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/elasticsearch_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/elasticsearch_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/enduser_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/enduser_attributes.py
index 6813a00..121a2c0 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/enduser_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/enduser_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/error_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/error_attributes.py
index c06ebd2..53e2212 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/error_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/error_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/event_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/event_attributes.py
index 8db935d..05f481b 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/event_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/event_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/exception_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/exception_attributes.py
index 37e2214..efec936 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/exception_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/exception_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/faas_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/faas_attributes.py
index 98ab0a4..21a0b6f 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/faas_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/faas_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/feature_flag_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/feature_flag_attributes.py
index 3ee2e63..f6a79f0 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/feature_flag_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/feature_flag_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/file_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/file_attributes.py
index 97ac01e..625c8e7 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/file_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/file_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/gcp_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/gcp_attributes.py
index b1bd472..74396ff 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/gcp_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/gcp_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/gen_ai_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/gen_ai_attributes.py
index c7d734c..3bfd0f0 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/gen_ai_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/gen_ai_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
@@ -243,6 +232,11 @@ GEN_AI_REQUEST_STOP_SEQUENCES: Final = "gen_ai.request.stop_sequences"
List of sequences that the model will use to stop generating further tokens.
"""
+GEN_AI_REQUEST_STREAM: Final = "gen_ai.request.stream"
+"""
+Indicates whether the GenAI request was made in streaming mode.
+"""
+
GEN_AI_REQUEST_TEMPERATURE: Final = "gen_ai.request.temperature"
"""
The temperature setting for the GenAI request.
@@ -273,6 +267,13 @@ GEN_AI_RESPONSE_MODEL: Final = "gen_ai.response.model"
The name of the model that generated the response.
"""
+GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK: Final = (
+ "gen_ai.response.time_to_first_chunk"
+)
+"""
+Time to first chunk in a streaming response, measured from request issuance, in seconds. The value is measured from when the client issues the generation request to when the first chunk is received in the response stream.
+"""
+
GEN_AI_RETRIEVAL_DOCUMENTS: Final = "gen_ai.retrieval.documents"
"""
The documents retrieved.
@@ -356,16 +357,16 @@ deserialize it to an object. When recorded on spans, it MAY be recorded as a JSO
GEN_AI_TOOL_DEFINITIONS: Final = "gen_ai.tool.definitions"
"""
-The list of source system tool definitions available to the GenAI agent or model.
-Note: The value of this attribute matches source system tool definition format.
+The list of tool definitions available to the GenAI agent or model.
+Note: Instrumentations MUST follow [Tool Definitions JSON Schema](/docs/gen-ai/gen-ai-tool-definitions.json).
-It's expected to be an array of objects where each object represents a tool definition. In case a serialized string is available
-to the instrumentation, the instrumentation SHOULD do the best effort to
-deserialize it to an array. When recorded on spans, it MAY be recorded as a JSON string if structured format is not supported and SHOULD be recorded in structured form otherwise.
+When the attribute is recorded on events, it MUST be recorded in structured
+form. When recorded on spans, it MAY be recorded as a JSON string if structured
+format is not supported and SHOULD be recorded in structured form otherwise.
Since this attribute could be large, it's NOT RECOMMENDED to populate
-it by default. Instrumentations MAY provide a way to enable
-populating this attribute.
+non-required properties by default. Instrumentations MAY provide a way
+to enable populating optional properties.
"""
GEN_AI_TOOL_DESCRIPTION: Final = "gen_ai.tool.description"
@@ -428,6 +429,20 @@ GEN_AI_USAGE_PROMPT_TOKENS: Final = "gen_ai.usage.prompt_tokens"
Deprecated: Replaced by `gen_ai.usage.input_tokens`.
"""
+GEN_AI_USAGE_REASONING_OUTPUT_TOKENS: Final = (
+ "gen_ai.usage.reasoning.output_tokens"
+)
+"""
+The number of output tokens used for reasoning (e.g. chain-of-thought, extended thinking).
+Note: The value SHOULD be included in `gen_ai.usage.output_tokens`.
+"""
+
+GEN_AI_WORKFLOW_NAME: Final = "gen_ai.workflow.name"
+"""
+Human-readable name of the GenAI workflow provided by the application.
+Note: This attribute can be populated in different frameworks eg: name of the first chain in LangChain OR name of the crew in CrewAI.
+"""
+
@deprecated(
"The attribute gen_ai.openai.request.response_format is deprecated - Replaced by `gen_ai.output.type`"
@@ -468,6 +483,8 @@ class GenAiOperationNameValues(Enum):
"""Invoke GenAI agent."""
EXECUTE_TOOL = "execute_tool"
"""Execute a tool."""
+ INVOKE_WORKFLOW = "invoke_workflow"
+ """Invoke GenAI workflow."""
class GenAiOutputTypeValues(Enum):
@@ -497,7 +514,7 @@ class GenAiProviderNameValues(Enum):
AZURE_AI_INFERENCE = "azure.ai.inference"
"""Azure AI Inference."""
AZURE_AI_OPENAI = "azure.ai.openai"
- """[Azure OpenAI](https://azure.microsoft.com/products/ai-services/openai-service/)."""
+ """[Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview)."""
IBM_WATSONX_AI = "ibm.watsonx.ai"
"""[IBM Watsonx AI](https://www.ibm.com/products/watsonx-ai)."""
AWS_BEDROCK = "aws.bedrock"
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/geo_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/geo_attributes.py
index 573e523..a32cfdd 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/geo_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/geo_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/graphql_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/graphql_attributes.py
index c467771..83c0d67 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/graphql_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/graphql_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
@@ -18,7 +7,7 @@ from typing import Final
GRAPHQL_DOCUMENT: Final = "graphql.document"
"""
The GraphQL document being executed.
-Note: The value may be sanitized to exclude sensitive information.
+Note: If instrumentation can reliably identify and redact sensitive information it SHOULD do it.
"""
GRAPHQL_OPERATION_NAME: Final = "graphql.operation.name"
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/heroku_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/heroku_attributes.py
index 83ba66b..2de0654 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/heroku_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/heroku_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/host_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/host_attributes.py
index 72847e6..b0ad4d9 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/host_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/host_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/http_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/http_attributes.py
index 13491c0..7b27c36 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/http_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/http_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/hw_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/hw_attributes.py
index d16f157..548e6fd 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/hw_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/hw_attributes.py
@@ -1,23 +1,12 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
HW_BATTERY_CAPACITY: Final = "hw.battery.capacity"
"""
-Design capacity in Watts-hours or Amper-hours.
+Design capacity in Watts-hours or Ampere-hours.
"""
HW_BATTERY_CHEMISTRY: Final = "hw.battery.chemistry"
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/jsonrpc_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/jsonrpc_attributes.py
index d3ae2ee..fcea823 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/jsonrpc_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/jsonrpc_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/k8s_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/k8s_attributes.py
index 5a3ddfe..2422918 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/k8s_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/k8s_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
@@ -325,11 +314,111 @@ K8S_NODE_NAME: Final = "k8s.node.name"
The name of the Node.
"""
+K8S_NODE_SYSTEM_CONTAINER_NAME: Final = "k8s.node.system_container.name"
+"""
+The name of the system container running on the K8s Node.
+"""
+
K8S_NODE_UID: Final = "k8s.node.uid"
"""
The UID of the Node.
"""
+K8S_PERSISTENTVOLUME_ANNOTATION_TEMPLATE: Final = (
+ "k8s.persistentvolume.annotation"
+)
+"""
+The annotation placed on the PersistentVolume, the `<key>` being the annotation name, the value being the annotation value, even if the value is empty.
+Note: Examples:
+
+- An annotation `pv.kubernetes.io/provisioned-by` with value `kubernetes.io/aws-ebs` SHOULD be recorded as
+ the `k8s.persistentvolume.annotation.pv.kubernetes.io/provisioned-by` attribute with value `"kubernetes.io/aws-ebs"`.
+- An annotation `data` with empty string value SHOULD be recorded as
+ the `k8s.persistentvolume.annotation.data` attribute with value `""`.
+"""
+
+K8S_PERSISTENTVOLUME_LABEL_TEMPLATE: Final = "k8s.persistentvolume.label"
+"""
+The label placed on the PersistentVolume, the `<key>` being the label name, the value being the label value, even if the value is empty.
+Note: Examples:
+
+- A label `type` with value `ssd` SHOULD be recorded as
+ the `k8s.persistentvolume.label.type` attribute with value `"ssd"`.
+- A label `data` with empty string value SHOULD be recorded as
+ the `k8s.persistentvolume.label.data` attribute with value `""`.
+"""
+
+K8S_PERSISTENTVOLUME_NAME: Final = "k8s.persistentvolume.name"
+"""
+The name of the PersistentVolume.
+"""
+
+K8S_PERSISTENTVOLUME_RECLAIM_POLICY: Final = (
+ "k8s.persistentvolume.reclaim_policy"
+)
+"""
+The reclaim policy of the PersistentVolume.
+Note: This attribute aligns with the `persistentVolumeReclaimPolicy` field of the
+[K8s PersistentVolumeSpec](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-v1/#PersistentVolumeSpec).
+"""
+
+K8S_PERSISTENTVOLUME_STATUS_PHASE: Final = "k8s.persistentvolume.status.phase"
+"""
+The phase of the PersistentVolume.
+Note: This attribute aligns with the `phase` field of the
+[K8s PersistentVolumeStatus](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-v1/#PersistentVolumeStatus).
+"""
+
+K8S_PERSISTENTVOLUME_UID: Final = "k8s.persistentvolume.uid"
+"""
+The UID of the PersistentVolume.
+"""
+
+K8S_PERSISTENTVOLUMECLAIM_ANNOTATION_TEMPLATE: Final = (
+ "k8s.persistentvolumeclaim.annotation"
+)
+"""
+The annotation placed on the PersistentVolumeClaim, the `<key>` being the annotation name, the value being the annotation value, even if the value is empty.
+Note: Examples:
+
+- An annotation `volume.beta.kubernetes.io/storage-provisioner` with value `kubernetes.io/aws-ebs` SHOULD be recorded as
+ the `k8s.persistentvolumeclaim.annotation.volume.beta.kubernetes.io/storage-provisioner` attribute with value `"kubernetes.io/aws-ebs"`.
+- An annotation `data` with empty string value SHOULD be recorded as
+ the `k8s.persistentvolumeclaim.annotation.data` attribute with value `""`.
+"""
+
+K8S_PERSISTENTVOLUMECLAIM_LABEL_TEMPLATE: Final = (
+ "k8s.persistentvolumeclaim.label"
+)
+"""
+The label placed on the PersistentVolumeClaim, the `<key>` being the label name, the value being the label value, even if the value is empty.
+Note: Examples:
+
+- A label `app` with value `my-app` SHOULD be recorded as
+ the `k8s.persistentvolumeclaim.label.app` attribute with value `"my-app"`.
+- A label `data` with empty string value SHOULD be recorded as
+ the `k8s.persistentvolumeclaim.label.data` attribute with value `""`.
+"""
+
+K8S_PERSISTENTVOLUMECLAIM_NAME: Final = "k8s.persistentvolumeclaim.name"
+"""
+The name of the PersistentVolumeClaim.
+"""
+
+K8S_PERSISTENTVOLUMECLAIM_STATUS_PHASE: Final = (
+ "k8s.persistentvolumeclaim.status.phase"
+)
+"""
+The phase of the PersistentVolumeClaim.
+Note: This attribute aligns with the `phase` field of the
+[K8s PersistentVolumeClaimStatus](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-claim-v1/#PersistentVolumeClaimStatus).
+"""
+
+K8S_PERSISTENTVOLUMECLAIM_UID: Final = "k8s.persistentvolumeclaim.uid"
+"""
+The UID of the PersistentVolumeClaim.
+"""
+
K8S_POD_ANNOTATION_TEMPLATE: Final = "k8s.pod.annotation"
"""
The annotation placed on the Pod, the `<key>` being the annotation name, the value being the annotation value.
@@ -682,6 +771,37 @@ class K8sNodeConditionTypeValues(Enum):
"""The network for the node is not correctly configured."""
+class K8sPersistentvolumeReclaimPolicyValues(Enum):
+ DELETE = "Delete"
+ """The volume will be deleted when released from its claim."""
+ RECYCLE = "Recycle"
+ """The volume will be recycled (basic scrub) when released from its claim."""
+ RETAIN = "Retain"
+ """The volume will be retained when released from its claim."""
+
+
+class K8sPersistentvolumeStatusPhaseValues(Enum):
+ AVAILABLE = "Available"
+ """The volume is available and not yet bound to a claim."""
+ BOUND = "Bound"
+ """The volume is bound to a claim."""
+ FAILED = "Failed"
+ """The volume has failed its automatic reclamation."""
+ PENDING = "Pending"
+ """The volume is being provisioned."""
+ RELEASED = "Released"
+ """The claim has been deleted but the volume is not yet available."""
+
+
+class K8sPersistentvolumeclaimStatusPhaseValues(Enum):
+ BOUND = "Bound"
+ """The claim is bound to a volume."""
+ LOST = "Lost"
+ """The claim has lost its underlying volume (the volume does not exist anymore)."""
+ PENDING = "Pending"
+ """The claim has not yet been bound to a volume."""
+
+
class K8sPodStatusPhaseValues(Enum):
PENDING = "Pending"
"""The pod has been accepted by the system, but one or more of the containers has not been started. This includes time before being bound to a node, as well as time spent pulling images onto the host."""
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/linux_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/linux_attributes.py
index 0e66940..2c651cb 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/linux_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/linux_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/log_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/log_attributes.py
index cd1fbbc..f50c910 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/log_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/log_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/mainframe_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/mainframe_attributes.py
index 96df480..bfa8d19 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/mainframe_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/mainframe_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/mcp_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/mcp_attributes.py
index beaa714..7474aea 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/mcp_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/mcp_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/message_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/message_attributes.py
index 6ec83f3..31741dc 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/message_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/message_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/messaging_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/messaging_attributes.py
index 8791bc8..032638f 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/messaging_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/messaging_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/net_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/net_attributes.py
index 3488d0e..7c34ac3 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/net_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/net_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/network_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/network_attributes.py
index f9bf30b..15c456e 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/network_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/network_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/nfs_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/nfs_attributes.py
index aed8983..3ef1dd5 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/nfs_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/nfs_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oci_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oci_attributes.py
index ba721df..c3342c0 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oci_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oci_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/onc_rpc_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/onc_rpc_attributes.py
index d8dd4db..eecf84c 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/onc_rpc_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/onc_rpc_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/openai_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/openai_attributes.py
index 2460e4f..5e2cb46 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/openai_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/openai_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/openshift_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/openshift_attributes.py
index 4a9afc8..db1abf0 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/openshift_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/openshift_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/opentracing_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/opentracing_attributes.py
index 0c1ae08..5237de9 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/opentracing_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/opentracing_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oracle_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oracle_attributes.py
index 911c3c8..766ef30 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oracle_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oracle_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oracle_cloud_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oracle_cloud_attributes.py
index e094b76..a829590 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oracle_cloud_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/oracle_cloud_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/os_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/os_attributes.py
index cebfe19..df490b2 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/os_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/os_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/otel_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/otel_attributes.py
index d6c872e..6973d4b 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/otel_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/otel_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
@@ -44,8 +33,7 @@ E.g. for Java the fully qualified classname SHOULD be used in this case.
OTEL_EVENT_NAME: Final = "otel.event.name"
"""
-Identifies the class / type of event.
-Note: This attribute SHOULD be used by non-OTLP exporters when destination does not support `EventName` or equivalent field. This attribute MAY be used by applications using existing logging libraries so that it can be used to set the `EventName` field by Collector or SDK components.
+Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.otel_attributes.OTEL_EVENT_NAME`.
"""
OTEL_LIBRARY_NAME: Final = "otel.library.name"
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/other_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/other_attributes.py
index 4515701..754dd8e 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/other_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/other_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/peer_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/peer_attributes.py
index 495523c..27ff455 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/peer_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/peer_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/pool_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/pool_attributes.py
index 6e0d70f..cd3fa90 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/pool_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/pool_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/pprof_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/pprof_attributes.py
index c2d95a7..b651a1e 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/pprof_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/pprof_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/process_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/process_attributes.py
index 8212e8c..4dea13d 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/process_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/process_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
@@ -80,7 +69,20 @@ PROCESS_EXECUTABLE_BUILD_ID_HTLHASH: Final = (
"process.executable.build_id.htlhash"
)
"""
-Profiling specific build ID for executables. See the OTel specification for Profiles for more information.
+Deterministic build ID for executables.
+Note: GNU and Go build IDs may be stripped or unavailable in some environments
+(e.g., Alpine Linux, Docker images). This attribute provides a deterministic
+build ID computed by hashing the first and last 4096 bytes of the file
+along with its length:
+
+```
+Input ← Concat(File[:4096], File[-4096:], BigEndianUInt64(Len(File)))
+Digest ← SHA256(Input)
+BuildID ← Digest[:16]
+```
+
+The result is the first 16 bytes (128 bits) of the SHA256 digest,
+represented as a hex string.
"""
PROCESS_EXECUTABLE_BUILD_ID_PROFILING: Final = (
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/profile_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/profile_attributes.py
index 21c5dc1..974a069 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/profile_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/profile_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/rpc_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/rpc_attributes.py
index 9656eae..e05dd09 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/rpc_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/rpc_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/security_rule_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/security_rule_attributes.py
index f6fbd0e..fa4ec42 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/security_rule_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/security_rule_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/server_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/server_attributes.py
index a9e3ab4..865dcde 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/server_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/server_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/service_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/service_attributes.py
index 36ce043..eb2dcd6 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/service_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/service_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/session_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/session_attributes.py
index 1d5ff34..573591d 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/session_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/session_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/source_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/source_attributes.py
index ea49387..abd1ae2 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/source_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/source_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/system_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/system_attributes.py
index 578d93d..fe5cec3 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/system_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/system_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
@@ -52,6 +41,13 @@ SYSTEM_FILESYSTEM_TYPE: Final = "system.filesystem.type"
The filesystem type.
"""
+SYSTEM_MEMORY_LINUX_HUGEPAGES_STATE: Final = (
+ "system.memory.linux.hugepages.state"
+)
+"""
+The Linux HugePages memory state.
+"""
+
SYSTEM_MEMORY_LINUX_SLAB_STATE: Final = "system.memory.linux.slab.state"
"""
The Linux Slab memory state.
@@ -142,6 +138,13 @@ class SystemFilesystemTypeValues(Enum):
"""ext4."""
+class SystemMemoryLinuxHugepagesStateValues(Enum):
+ FREE = "free"
+ """free."""
+ USED = "used"
+ """used."""
+
+
class SystemMemoryLinuxSlabStateValues(Enum):
RECLAIMABLE = "reclaimable"
"""reclaimable."""
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/telemetry_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/telemetry_attributes.py
index cd5df9b..6c3085c 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/telemetry_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/telemetry_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
@@ -19,14 +8,12 @@ from typing_extensions import deprecated
TELEMETRY_DISTRO_NAME: Final = "telemetry.distro.name"
"""
-The name of the auto instrumentation agent or distribution, if used.
-Note: Official auto instrumentation agents and distributions SHOULD set the `telemetry.distro.name` attribute to
-a string starting with `opentelemetry-`, e.g. `opentelemetry-java-instrumentation`.
+Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TELEMETRY_DISTRO_NAME`.
"""
TELEMETRY_DISTRO_VERSION: Final = "telemetry.distro.version"
"""
-The version string of the auto instrumentation agent or distribution, if used.
+Deprecated in favor of stable :py:const:`opentelemetry.semconv.attributes.telemetry_attributes.TELEMETRY_DISTRO_VERSION`.
"""
TELEMETRY_SDK_LANGUAGE: Final = "telemetry.sdk.language"
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/test_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/test_attributes.py
index 201c9bd..4a3ef39 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/test_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/test_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/thread_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/thread_attributes.py
index fcf6831..29c4143 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/thread_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/thread_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/tls_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/tls_attributes.py
index fa2b916..7ade1e1 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/tls_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/tls_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/url_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/url_attributes.py
index 57d1de8..8ed5ee4 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/url_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/url_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/user_agent_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/user_agent_attributes.py
index 4974aab..bccceba 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/user_agent_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/user_agent_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/user_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/user_attributes.py
index 4d3e8a2..91d0dbc 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/user_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/user_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/vcs_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/vcs_attributes.py
index 8b7426f..0180d06 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/vcs_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/vcs_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/webengine_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/webengine_attributes.py
index 1517542..0d0bf87 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/webengine_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/webengine_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/zos_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/zos_attributes.py
index 195177f..3c65710 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/zos_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/attributes/zos_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/azure_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/azure_metrics.py
index 2273ac8..80fa43f 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/azure_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/azure_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cicd_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cicd_metrics.py
index 53fbfac..dd888ca 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cicd_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cicd_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/container_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/container_metrics.py
index 496d0c5..3a732ed 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/container_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/container_metrics.py
@@ -1,27 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 (
- Callable,
- Final,
- Generator,
- Iterable,
- Optional,
- Sequence,
- Union,
-)
+# SPDX-License-Identifier: Apache-2.0
+
+
+from collections.abc import Callable, Generator, Iterable, Sequence
+from typing import Final
from opentelemetry.metrics import (
CallbackOptions,
@@ -33,10 +15,11 @@ from opentelemetry.metrics import (
)
# pylint: disable=invalid-name
-CallbackT = Union[
- Callable[[CallbackOptions], Iterable[Observation]],
- Generator[Iterable[Observation], CallbackOptions, None],
-]
+CallbackT = (
+ Callable[[CallbackOptions], Iterable[Observation]]
+ | Generator[Iterable[Observation], CallbackOptions, None]
+)
+
CONTAINER_CPU_TIME: Final = "container.cpu.time"
"""
@@ -66,7 +49,7 @@ Note: CPU usage of the specific container on all available CPU cores, averaged o
def create_container_cpu_usage(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Container's CPU usage, measured in cpus. Range from 0 to the number of allocatable CPUs"""
return meter.create_observable_gauge(
@@ -284,7 +267,7 @@ The actual accuracy would depend on the instrumentation and operating system.
def create_container_uptime(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""The time the container has been running"""
return meter.create_observable_gauge(
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cpu_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cpu_metrics.py
index 9d388c8..a1cb372 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cpu_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cpu_metrics.py
@@ -1,27 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 (
- Callable,
- Final,
- Generator,
- Iterable,
- Optional,
- Sequence,
- Union,
-)
+# SPDX-License-Identifier: Apache-2.0
+
+
+from collections.abc import Callable, Generator, Iterable, Sequence
+from typing import Final
from opentelemetry.metrics import (
CallbackOptions,
@@ -32,10 +14,11 @@ from opentelemetry.metrics import (
)
# pylint: disable=invalid-name
-CallbackT = Union[
- Callable[[CallbackOptions], Iterable[Observation]],
- Generator[Iterable[Observation], CallbackOptions, None],
-]
+CallbackT = (
+ Callable[[CallbackOptions], Iterable[Observation]]
+ | Generator[Iterable[Observation], CallbackOptions, None]
+)
+
CPU_FREQUENCY: Final = "cpu.frequency"
"""
@@ -44,7 +27,7 @@ Deprecated: Replaced by `system.cpu.frequency`.
def create_cpu_frequency(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Deprecated. Use `system.cpu.frequency` instead"""
return meter.create_observable_gauge(
@@ -77,7 +60,7 @@ Deprecated: Replaced by `system.cpu.utilization`.
def create_cpu_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Deprecated. Use `system.cpu.utilization` instead"""
return meter.create_observable_gauge(
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cpython_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cpython_metrics.py
index 2c480f5..64670af 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cpython_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/cpython_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/db_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/db_metrics.py
index 4df9d1e..020360d 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/db_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/db_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/dns_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/dns_metrics.py
index 53fb3d2..1ff3bdf 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/dns_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/dns_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/faas_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/faas_metrics.py
index 8d64c82..894e778 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/faas_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/faas_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/gen_ai_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/gen_ai_metrics.py
index 7a7afa3..899dd4e 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/gen_ai_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/gen_ai_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
@@ -34,6 +23,50 @@ def create_gen_ai_client_operation_duration(meter: Meter) -> Histogram:
)
+GEN_AI_CLIENT_OPERATION_TIME_PER_OUTPUT_CHUNK: Final = (
+ "gen_ai.client.operation.time_per_output_chunk"
+)
+"""
+Time per output chunk, recorded for each chunk received after the first one, measured as the time elapsed from the end of the previous chunk to the end of the current chunk
+Instrument: histogram
+Unit: s
+Note: This metrics SHOULD be reported for streaming calls and SHOULD NOT be reported otherwise.
+"""
+
+
+def create_gen_ai_client_operation_time_per_output_chunk(
+ meter: Meter,
+) -> Histogram:
+ """Time per output chunk, recorded for each chunk received after the first one, measured as the time elapsed from the end of the previous chunk to the end of the current chunk"""
+ return meter.create_histogram(
+ name=GEN_AI_CLIENT_OPERATION_TIME_PER_OUTPUT_CHUNK,
+ description="Time per output chunk, recorded for each chunk received after the first one, measured as the time elapsed from the end of the previous chunk to the end of the current chunk.",
+ unit="s",
+ )
+
+
+GEN_AI_CLIENT_OPERATION_TIME_TO_FIRST_CHUNK: Final = (
+ "gen_ai.client.operation.time_to_first_chunk"
+)
+"""
+Time to receive the first chunk, measured from when the client issues the generation request to when the first chunk is received in the response stream
+Instrument: histogram
+Unit: s
+Note: This metrics SHOULD be reported for streaming calls and SHOULD NOT be reported otherwise.
+"""
+
+
+def create_gen_ai_client_operation_time_to_first_chunk(
+ meter: Meter,
+) -> Histogram:
+ """Time to receive the first chunk, measured from when the client issues the generation request to when the first chunk is received in the response stream"""
+ return meter.create_histogram(
+ name=GEN_AI_CLIENT_OPERATION_TIME_TO_FIRST_CHUNK,
+ description="Time to receive the first chunk, measured from when the client issues the generation request to when the first chunk is received in the response stream.",
+ unit="s",
+ )
+
+
GEN_AI_CLIENT_TOKEN_USAGE: Final = "gen_ai.client.token.usage"
"""
Number of input and output tokens used
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/http_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/http_metrics.py
index 86d0317..4ea7198 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/http_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/http_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/hw_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/hw_metrics.py
index 6e47186..9bebb26 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/hw_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/hw_metrics.py
@@ -1,27 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 (
- Callable,
- Final,
- Generator,
- Iterable,
- Optional,
- Sequence,
- Union,
-)
+# SPDX-License-Identifier: Apache-2.0
+
+
+from collections.abc import Callable, Generator, Iterable, Sequence
+from typing import Final
from opentelemetry.metrics import (
CallbackOptions,
@@ -33,10 +15,11 @@ from opentelemetry.metrics import (
)
# pylint: disable=invalid-name
-CallbackT = Union[
- Callable[[CallbackOptions], Iterable[Observation]],
- Generator[Iterable[Observation], CallbackOptions, None],
-]
+CallbackT = (
+ Callable[[CallbackOptions], Iterable[Observation]]
+ | Generator[Iterable[Observation], CallbackOptions, None]
+)
+
HW_BATTERY_CHARGE: Final = "hw.battery.charge"
"""
@@ -47,7 +30,7 @@ Unit: 1
def create_hw_battery_charge(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Remaining fraction of battery charge"""
return meter.create_observable_gauge(
@@ -67,7 +50,7 @@ Unit: 1
def create_hw_battery_charge_limit(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Lower limit of battery charge fraction to ensure proper operation"""
return meter.create_observable_gauge(
@@ -87,7 +70,7 @@ Unit: s
def create_hw_battery_time_left(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Time left before battery is completely charged or discharged"""
return meter.create_observable_gauge(
@@ -107,7 +90,7 @@ Unit: Hz
def create_hw_cpu_speed(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""CPU current frequency"""
return meter.create_observable_gauge(
@@ -127,7 +110,7 @@ Unit: Hz
def create_hw_cpu_speed_limit(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""CPU maximum frequency"""
return meter.create_observable_gauge(
@@ -181,7 +164,7 @@ Unit: rpm
def create_hw_fan_speed(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Fan speed in revolutions per minute"""
return meter.create_observable_gauge(
@@ -201,7 +184,7 @@ Unit: rpm
def create_hw_fan_speed_limit(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Speed limit in rpm"""
return meter.create_observable_gauge(
@@ -221,7 +204,7 @@ Unit: 1
def create_hw_fan_speed_ratio(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Fan speed expressed as a fraction of its maximum speed"""
return meter.create_observable_gauge(
@@ -292,7 +275,7 @@ Unit: 1
def create_hw_gpu_memory_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Fraction of GPU memory used"""
return meter.create_observable_gauge(
@@ -312,7 +295,7 @@ Unit: 1
def create_hw_gpu_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Fraction of time spent in a specific task"""
return meter.create_observable_gauge(
@@ -332,7 +315,7 @@ Unit: Cel
def create_hw_host_ambient_temperature(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Ambient (external) temperature of the physical host"""
return meter.create_observable_gauge(
@@ -370,7 +353,7 @@ Unit: Cel
def create_hw_host_heating_margin(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""By how many degrees Celsius the temperature of the physical host can be increased, before reaching a warning threshold on one of the internal sensors"""
return meter.create_observable_gauge(
@@ -391,7 +374,7 @@ Note: The overall energy usage of a host MUST be reported using the specific `hw
def create_hw_host_power(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Instantaneous power consumed by the entire physical host in Watts (`hw.host.energy` is preferred)"""
return meter.create_observable_gauge(
@@ -445,7 +428,7 @@ Unit: 1
def create_hw_logical_disk_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Logical disk space utilization as a fraction"""
return meter.create_observable_gauge(
@@ -499,7 +482,7 @@ Unit: 1
def create_hw_network_bandwidth_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Utilization of the network bandwidth as a fraction"""
return meter.create_observable_gauge(
@@ -572,7 +555,7 @@ Unit: 1
def create_hw_physical_disk_endurance_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Endurance remaining for this SSD disk"""
return meter.create_observable_gauge(
@@ -609,7 +592,7 @@ Unit: 1
def create_hw_physical_disk_smart(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Value of the corresponding [S.M.A.R.T.](https://wikipedia.org/wiki/S.M.A.R.T.) (Self-Monitoring, Analysis, and Reporting Technology) attribute"""
return meter.create_observable_gauge(
@@ -630,7 +613,7 @@ Note: It is recommended to report `hw.energy` instead of `hw.power` when possibl
def create_hw_power(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Instantaneous power consumed by the component"""
return meter.create_observable_gauge(
@@ -684,7 +667,7 @@ Unit: 1
def create_hw_power_supply_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Utilization of the power supply as a fraction of its maximum output"""
return meter.create_observable_gauge(
@@ -739,7 +722,7 @@ Unit: Cel
def create_hw_temperature(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Temperature in degrees Celsius"""
return meter.create_observable_gauge(
@@ -759,7 +742,7 @@ Unit: Cel
def create_hw_temperature_limit(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Temperature limit in degrees Celsius"""
return meter.create_observable_gauge(
@@ -779,7 +762,7 @@ Unit: V
def create_hw_voltage(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Voltage measured by the sensor"""
return meter.create_observable_gauge(
@@ -799,7 +782,7 @@ Unit: V
def create_hw_voltage_limit(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Voltage limit in Volts"""
return meter.create_observable_gauge(
@@ -819,7 +802,7 @@ Unit: V
def create_hw_voltage_nominal(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Nominal (expected) voltage"""
return meter.create_observable_gauge(
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/k8s_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/k8s_metrics.py
index 06b6143..a74461b 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/k8s_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/k8s_metrics.py
@@ -1,27 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 (
- Callable,
- Final,
- Generator,
- Iterable,
- Optional,
- Sequence,
- Union,
-)
+# SPDX-License-Identifier: Apache-2.0
+
+
+from collections.abc import Callable, Generator, Iterable, Sequence
+from typing import Final
from opentelemetry.metrics import (
CallbackOptions,
@@ -33,88 +15,178 @@ from opentelemetry.metrics import (
)
# pylint: disable=invalid-name
-CallbackT = Union[
- Callable[[CallbackOptions], Iterable[Observation]],
- Generator[Iterable[Observation], CallbackOptions, None],
-]
+CallbackT = (
+ Callable[[CallbackOptions], Iterable[Observation]]
+ | Generator[Iterable[Observation], CallbackOptions, None]
+)
+
K8S_CONTAINER_CPU_LIMIT: Final = "k8s.container.cpu.limit"
"""
-Maximum CPU resource limit set for the container
-Instrument: updowncounter
-Unit: {cpu}
-Note: See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core for details.
+Deprecated: Replaced by `k8s.container.cpu.limit.desired`.
"""
def create_k8s_container_cpu_limit(meter: Meter) -> UpDownCounter:
- """Maximum CPU resource limit set for the container"""
+ """Deprecated, use `k8s.container.cpu.limit.desired` and `k8s.container.cpu.limit.current` instead"""
return meter.create_up_down_counter(
name=K8S_CONTAINER_CPU_LIMIT,
- description="Maximum CPU resource limit set for the container.",
+ description="Deprecated, use `k8s.container.cpu.limit.desired` and `k8s.container.cpu.limit.current` instead.",
+ unit="{cpu}",
+ )
+
+
+K8S_CONTAINER_CPU_LIMIT_CURRENT: Final = "k8s.container.cpu.limit.current"
+"""
+Maximum CPU resource limit currently configured for a running container
+Instrument: updowncounter
+Unit: {cpu}
+Note: This metric aligns with the limit in the
+[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of
+[K8s ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core)
+(status.containerStatuses[*].resources). Also see `Actual Resources` in
+<https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/> for more details.
+"""
+
+
+def create_k8s_container_cpu_limit_current(meter: Meter) -> UpDownCounter:
+ """Maximum CPU resource limit currently configured for a running container"""
+ return meter.create_up_down_counter(
+ name=K8S_CONTAINER_CPU_LIMIT_CURRENT,
+ description="Maximum CPU resource limit currently configured for a running container.",
+ unit="{cpu}",
+ )
+
+
+K8S_CONTAINER_CPU_LIMIT_DESIRED: Final = "k8s.container.cpu.limit.desired"
+"""
+Maximum CPU resource limit as defined by the container spec
+Instrument: updowncounter
+Unit: {cpu}
+Note: This metric aligns with the limit in the
+[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of
+[K8s Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#container-v1-core)
+(spec.containers[*].resources). Also see `Desired Resources` in
+<https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/> for more details.
+"""
+
+
+def create_k8s_container_cpu_limit_desired(meter: Meter) -> UpDownCounter:
+ """Maximum CPU resource limit as defined by the container spec"""
+ return meter.create_up_down_counter(
+ name=K8S_CONTAINER_CPU_LIMIT_DESIRED,
+ description="Maximum CPU resource limit as defined by the container spec.",
unit="{cpu}",
)
K8S_CONTAINER_CPU_LIMIT_UTILIZATION: Final = (
- "k8s.container.cpu.limit_utilization"
+ "k8s.container.cpu.limit.utilization"
)
"""
-The ratio of container CPU usage to its CPU limit
+The ratio of container CPU usage to its current CPU limit
Instrument: gauge
Unit: 1
-Note: The value range is [0.0,1.0]. A value of 1.0 means the container is using 100% of its CPU limit. If the CPU limit is not set, this metric SHOULD NOT be emitted for that container.
+Note: The current CPU limit reflects the actual resources applied to the container, as reported by
+[ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core).
+The value range is [0.0,1.0]. A value of 1.0 means the container is using 100% of its actual CPU limit.
+If the CPU limit is not set, this metric SHOULD NOT be emitted for that container.
"""
def create_k8s_container_cpu_limit_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
- """The ratio of container CPU usage to its CPU limit"""
+ """The ratio of container CPU usage to its current CPU limit"""
return meter.create_observable_gauge(
name=K8S_CONTAINER_CPU_LIMIT_UTILIZATION,
callbacks=callbacks,
- description="The ratio of container CPU usage to its CPU limit.",
+ description="The ratio of container CPU usage to its current CPU limit.",
unit="1",
)
K8S_CONTAINER_CPU_REQUEST: Final = "k8s.container.cpu.request"
"""
-CPU resource requested for the container
-Instrument: updowncounter
-Unit: {cpu}
-Note: See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core for details.
+Deprecated: Replaced by `k8s.container.cpu.request.desired`.
"""
def create_k8s_container_cpu_request(meter: Meter) -> UpDownCounter:
- """CPU resource requested for the container"""
+ """Deprecated, use `k8s.container.cpu.request.desired` and `k8s.container.cpu.request.current` instead"""
return meter.create_up_down_counter(
name=K8S_CONTAINER_CPU_REQUEST,
- description="CPU resource requested for the container.",
+ description="Deprecated, use `k8s.container.cpu.request.desired` and `k8s.container.cpu.request.current` instead.",
+ unit="{cpu}",
+ )
+
+
+K8S_CONTAINER_CPU_REQUEST_CURRENT: Final = "k8s.container.cpu.request.current"
+"""
+CPU resource requested currently configured for a running container
+Instrument: updowncounter
+Unit: {cpu}
+Note: This metric aligns with the request in the
+[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of
+[K8s ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core)
+(status.containerStatuses[*].resources). Also see `Actual Resources` in
+<https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/> for more details.
+"""
+
+
+def create_k8s_container_cpu_request_current(meter: Meter) -> UpDownCounter:
+ """CPU resource requested currently configured for a running container"""
+ return meter.create_up_down_counter(
+ name=K8S_CONTAINER_CPU_REQUEST_CURRENT,
+ description="CPU resource requested currently configured for a running container.",
+ unit="{cpu}",
+ )
+
+
+K8S_CONTAINER_CPU_REQUEST_DESIRED: Final = "k8s.container.cpu.request.desired"
+"""
+CPU resource requested as defined by the container spec
+Instrument: updowncounter
+Unit: {cpu}
+Note: This metric aligns with the request in the
+[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of
+[K8s Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#container-v1-core)
+(spec.containers[*].resources). Also see `Desired Resources` in
+<https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/> for more details.
+"""
+
+
+def create_k8s_container_cpu_request_desired(meter: Meter) -> UpDownCounter:
+ """CPU resource requested as defined by the container spec"""
+ return meter.create_up_down_counter(
+ name=K8S_CONTAINER_CPU_REQUEST_DESIRED,
+ description="CPU resource requested as defined by the container spec.",
unit="{cpu}",
)
K8S_CONTAINER_CPU_REQUEST_UTILIZATION: Final = (
- "k8s.container.cpu.request_utilization"
+ "k8s.container.cpu.request.utilization"
)
"""
-The ratio of container CPU usage to its CPU request
+The ratio of container CPU usage to its current CPU request
Instrument: gauge
Unit: 1
+Note: The current CPU request reflects the request applied to the running container, as reported by
+[ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core).
+The value range is [0.0,1.0]. A value of 1.0 means the container is using 100% of its actual CPU request.
+If the CPU request is not set, this metric SHOULD NOT be emitted for that container.
"""
def create_k8s_container_cpu_request_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
- """The ratio of container CPU usage to its CPU request"""
+ """The ratio of container CPU usage to its current CPU request"""
return meter.create_observable_gauge(
name=K8S_CONTAINER_CPU_REQUEST_UTILIZATION,
callbacks=callbacks,
- description="The ratio of container CPU usage to its CPU request.",
+ description="The ratio of container CPU usage to its current CPU request.",
unit="1",
)
@@ -165,36 +237,126 @@ def create_k8s_container_ephemeral_storage_request(
K8S_CONTAINER_MEMORY_LIMIT: Final = "k8s.container.memory.limit"
"""
-Maximum memory resource limit set for the container
-Instrument: updowncounter
-Unit: By
-Note: See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core for details.
+Deprecated: Replaced by `k8s.container.memory.limit.desired`.
"""
def create_k8s_container_memory_limit(meter: Meter) -> UpDownCounter:
- """Maximum memory resource limit set for the container"""
+ """Deprecated, use `k8s.container.memory.limit.desired` and `k8s.container.memory.limit.current` instead"""
return meter.create_up_down_counter(
name=K8S_CONTAINER_MEMORY_LIMIT,
- description="Maximum memory resource limit set for the container.",
+ description="Deprecated, use `k8s.container.memory.limit.desired` and `k8s.container.memory.limit.current` instead.",
unit="By",
)
-K8S_CONTAINER_MEMORY_REQUEST: Final = "k8s.container.memory.request"
+K8S_CONTAINER_MEMORY_LIMIT_CURRENT: Final = (
+ "k8s.container.memory.limit.current"
+)
"""
-Memory resource requested for the container
+Maximum memory resource limit currently configured for a running container
Instrument: updowncounter
Unit: By
-Note: See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core for details.
+Note: This metric aligns with the limit in the
+[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of
+[K8s ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core)
+(status.containerStatuses[*].resources). Also see `Actual Resources` in
+<https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/> for more details.
+"""
+
+
+def create_k8s_container_memory_limit_current(meter: Meter) -> UpDownCounter:
+ """Maximum memory resource limit currently configured for a running container"""
+ return meter.create_up_down_counter(
+ name=K8S_CONTAINER_MEMORY_LIMIT_CURRENT,
+ description="Maximum memory resource limit currently configured for a running container.",
+ unit="By",
+ )
+
+
+K8S_CONTAINER_MEMORY_LIMIT_DESIRED: Final = (
+ "k8s.container.memory.limit.desired"
+)
+"""
+Maximum memory resource limit as defined by the container spec
+Instrument: updowncounter
+Unit: By
+Note: This metric aligns with the limit in the
+[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of
+[K8s Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#container-v1-core)
+(spec.containers[*].resources). Also see `Desired Resources` in
+<https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/> for more details.
+"""
+
+
+def create_k8s_container_memory_limit_desired(meter: Meter) -> UpDownCounter:
+ """Maximum memory resource limit as defined by the container spec"""
+ return meter.create_up_down_counter(
+ name=K8S_CONTAINER_MEMORY_LIMIT_DESIRED,
+ description="Maximum memory resource limit as defined by the container spec.",
+ unit="By",
+ )
+
+
+K8S_CONTAINER_MEMORY_REQUEST: Final = "k8s.container.memory.request"
+"""
+Deprecated: Replaced by `k8s.container.memory.request.desired`.
"""
def create_k8s_container_memory_request(meter: Meter) -> UpDownCounter:
- """Memory resource requested for the container"""
+ """Deprecated, use `k8s.container.memory.request.desired` and `k8s.container.memory.request.current` instead"""
return meter.create_up_down_counter(
name=K8S_CONTAINER_MEMORY_REQUEST,
- description="Memory resource requested for the container.",
+ description="Deprecated, use `k8s.container.memory.request.desired` and `k8s.container.memory.request.current` instead.",
+ unit="By",
+ )
+
+
+K8S_CONTAINER_MEMORY_REQUEST_CURRENT: Final = (
+ "k8s.container.memory.request.current"
+)
+"""
+Memory resource request currently configured for a running container
+Instrument: updowncounter
+Unit: By
+Note: This metric aligns with the request in the
+[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of
+[K8s ContainerStatus](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#containerstatus-v1-core)
+(status.containerStatuses[*].resources). Also see `Actual Resources` in
+<https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/> for more details.
+"""
+
+
+def create_k8s_container_memory_request_current(meter: Meter) -> UpDownCounter:
+ """Memory resource request currently configured for a running container"""
+ return meter.create_up_down_counter(
+ name=K8S_CONTAINER_MEMORY_REQUEST_CURRENT,
+ description="Memory resource request currently configured for a running container.",
+ unit="By",
+ )
+
+
+K8S_CONTAINER_MEMORY_REQUEST_DESIRED: Final = (
+ "k8s.container.memory.request.desired"
+)
+"""
+Memory resource requested as defined by the container spec
+Instrument: updowncounter
+Unit: By
+Note: This metric aligns with the request in the
+[`resources`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#resourcerequirements-v1-core) field of
+[K8s Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#container-v1-core)
+(spec.containers[*].resources). Also see `Desired Resources` in
+<https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/> for more details.
+"""
+
+
+def create_k8s_container_memory_request_desired(meter: Meter) -> UpDownCounter:
+ """Memory resource requested as defined by the container spec"""
+ return meter.create_up_down_counter(
+ name=K8S_CONTAINER_MEMORY_REQUEST_DESIRED,
+ description="Memory resource requested as defined by the container spec.",
unit="By",
)
@@ -624,7 +786,7 @@ the `k8s.container.name` attribute MUST be set to identify the specific containe
def create_k8s_hpa_metric_target_cpu_average_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Target average utilization, in percentage, for CPU resource in HPA config"""
return meter.create_observable_gauge(
@@ -650,7 +812,7 @@ the `k8s.container.name` attribute MUST be set to identify the specific containe
def create_k8s_hpa_metric_target_cpu_average_value(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Target average value for CPU resource in HPA config"""
return meter.create_observable_gauge(
@@ -674,7 +836,7 @@ the `k8s.container.name` attribute MUST be set to identify the specific containe
def create_k8s_hpa_metric_target_cpu_value(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Target value for CPU resource in HPA config"""
return meter.create_observable_gauge(
@@ -1090,7 +1252,7 @@ Note: CPU usage of the specific Node on all available CPU cores, averaged over t
def create_k8s_node_cpu_usage(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Node's CPU usage, measured in cpus. Range from 0 to the number of allocatable CPUs"""
return meter.create_observable_gauge(
@@ -1271,7 +1433,7 @@ Note: Total memory usage of the Node.
def create_k8s_node_memory_usage(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Memory usage of the Node"""
return meter.create_observable_gauge(
@@ -1352,6 +1514,93 @@ def create_k8s_node_pod_allocatable(meter: Meter) -> UpDownCounter:
)
+K8S_NODE_SYSTEM_CONTAINER_CPU_TIME: Final = (
+ "k8s.node.system_container.cpu.time"
+)
+"""
+Node's system container CPU time
+Instrument: counter
+Unit: s
+Note: This metric is derived from the [CPUStats.UsageCoreNanoSeconds](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L236) field of the [ContainerStats](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L157C6-L157C20) of [Node.SystemContainers](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L40) of the Kubelet's stats API.
+"""
+
+
+def create_k8s_node_system_container_cpu_time(meter: Meter) -> Counter:
+ """Node's system container CPU time"""
+ return meter.create_counter(
+ name=K8S_NODE_SYSTEM_CONTAINER_CPU_TIME,
+ description="Node's system container CPU time.",
+ unit="s",
+ )
+
+
+K8S_NODE_SYSTEM_CONTAINER_CPU_USAGE: Final = (
+ "k8s.node.system_container.cpu.usage"
+)
+"""
+Node's system container CPU usage, measured in cpus
+Instrument: gauge
+Unit: {cpu}
+Note: This metric is derived from the [CPUStats.UsageNanoCores](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L233) field of the [ContainerStats](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L157C6-L157C20) of [Node.SystemContainers](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L40) of the Kubelet's stats API.
+"""
+
+
+def create_k8s_node_system_container_cpu_usage(
+ meter: Meter, callbacks: Sequence[CallbackT] | None
+) -> ObservableGauge:
+ """Node's system container CPU usage, measured in cpus"""
+ return meter.create_observable_gauge(
+ name=K8S_NODE_SYSTEM_CONTAINER_CPU_USAGE,
+ callbacks=callbacks,
+ description="Node's system container CPU usage, measured in cpus.",
+ unit="{cpu}",
+ )
+
+
+K8S_NODE_SYSTEM_CONTAINER_MEMORY_USAGE: Final = (
+ "k8s.node.system_container.memory.usage"
+)
+"""
+Node's system container memory usage
+Instrument: updowncounter
+Unit: By
+Note: This metric is derived from the [MemoryStats.UsageBytes](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L252) field of the [ContainerStats](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L157C6-L157C20) of [Node.SystemContainers](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L40) of the Kubelet's stats API.
+"""
+
+
+def create_k8s_node_system_container_memory_usage(
+ meter: Meter,
+) -> UpDownCounter:
+ """Node's system container memory usage"""
+ return meter.create_up_down_counter(
+ name=K8S_NODE_SYSTEM_CONTAINER_MEMORY_USAGE,
+ description="Node's system container memory usage.",
+ unit="By",
+ )
+
+
+K8S_NODE_SYSTEM_CONTAINER_MEMORY_WORKING_SET: Final = (
+ "k8s.node.system_container.memory.working_set"
+)
+"""
+The amount of working set memory
+Instrument: updowncounter
+Unit: By
+Note: This metric is derived from the [MemoryStats.WorkingSetBytes](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L256) field of the [ContainerStats](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L157C6-L157C20) of [Node.SystemContainers](https://github.com/kubernetes/kubelet/blob/v0.35.2/pkg/apis/stats/v1alpha1/types.go#L40) of the Kubelet's stats API.
+"""
+
+
+def create_k8s_node_system_container_memory_working_set(
+ meter: Meter,
+) -> UpDownCounter:
+ """The amount of working set memory"""
+ return meter.create_up_down_counter(
+ name=K8S_NODE_SYSTEM_CONTAINER_MEMORY_WORKING_SET,
+ description="The amount of working set memory.",
+ unit="By",
+ )
+
+
K8S_NODE_UPTIME: Final = "k8s.node.uptime"
"""
The time the Node has been running
@@ -1363,7 +1612,7 @@ The actual accuracy would depend on the instrumentation and operating system.
def create_k8s_node_uptime(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""The time the Node has been running"""
return meter.create_observable_gauge(
@@ -1374,6 +1623,118 @@ def create_k8s_node_uptime(
)
+K8S_PERSISTENTVOLUME_STATUS_PHASE: Final = "k8s.persistentvolume.status.phase"
+"""
+Number of PersistentVolumes in a given phase
+Instrument: updowncounter
+Unit: {persistentvolume}
+Note: All possible phases should be reported at each interval to avoid gaps in the time series.
+This metric is derived from the `.status.phase` field of the
+[K8s PersistentVolumeStatus](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-v1/#PersistentVolumeStatus).
+"""
+
+
+def create_k8s_persistentvolume_status_phase(meter: Meter) -> UpDownCounter:
+ """Number of PersistentVolumes in a given phase"""
+ return meter.create_up_down_counter(
+ name=K8S_PERSISTENTVOLUME_STATUS_PHASE,
+ description="Number of PersistentVolumes in a given phase.",
+ unit="{persistentvolume}",
+ )
+
+
+K8S_PERSISTENTVOLUME_STORAGE_CAPACITY: Final = (
+ "k8s.persistentvolume.storage.capacity"
+)
+"""
+The storage capacity of the PersistentVolume
+Instrument: updowncounter
+Unit: By
+Note: This metric is derived from the `.spec.capacity.storage` field of the [K8s PersistentVolumeSpec](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-v1/#PersistentVolumeSpec).
+"""
+
+
+def create_k8s_persistentvolume_storage_capacity(
+ meter: Meter,
+) -> UpDownCounter:
+ """The storage capacity of the PersistentVolume"""
+ return meter.create_up_down_counter(
+ name=K8S_PERSISTENTVOLUME_STORAGE_CAPACITY,
+ description="The storage capacity of the PersistentVolume.",
+ unit="By",
+ )
+
+
+K8S_PERSISTENTVOLUMECLAIM_STATUS_PHASE: Final = (
+ "k8s.persistentvolumeclaim.status.phase"
+)
+"""
+Number of PersistentVolumeClaims in a given phase
+Instrument: updowncounter
+Unit: {persistentvolumeclaim}
+Note: All possible phases should be reported at each interval to avoid gaps in the time series.
+This metric is derived from the `.status.phase` field of the
+[K8s PersistentVolumeClaimStatus](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-claim-v1/#PersistentVolumeClaimStatus).
+"""
+
+
+def create_k8s_persistentvolumeclaim_status_phase(
+ meter: Meter,
+) -> UpDownCounter:
+ """Number of PersistentVolumeClaims in a given phase"""
+ return meter.create_up_down_counter(
+ name=K8S_PERSISTENTVOLUMECLAIM_STATUS_PHASE,
+ description="Number of PersistentVolumeClaims in a given phase.",
+ unit="{persistentvolumeclaim}",
+ )
+
+
+K8S_PERSISTENTVOLUMECLAIM_STORAGE_CAPACITY: Final = (
+ "k8s.persistentvolumeclaim.storage.capacity"
+)
+"""
+The actual storage capacity provisioned for the PersistentVolumeClaim
+Instrument: updowncounter
+Unit: By
+Note: Only available when the PVC is bound. May differ from the requested capacity due to provisioner rounding.
+This metric is derived from the `.status.capacity.storage` field of the
+[K8s PersistentVolumeClaimStatus](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-claim-v1/#PersistentVolumeClaimStatus).
+"""
+
+
+def create_k8s_persistentvolumeclaim_storage_capacity(
+ meter: Meter,
+) -> UpDownCounter:
+ """The actual storage capacity provisioned for the PersistentVolumeClaim"""
+ return meter.create_up_down_counter(
+ name=K8S_PERSISTENTVOLUMECLAIM_STORAGE_CAPACITY,
+ description="The actual storage capacity provisioned for the PersistentVolumeClaim.",
+ unit="By",
+ )
+
+
+K8S_PERSISTENTVOLUMECLAIM_STORAGE_REQUEST: Final = (
+ "k8s.persistentvolumeclaim.storage.request"
+)
+"""
+The storage requested by the PersistentVolumeClaim
+Instrument: updowncounter
+Unit: By
+Note: This metric is derived from the `.spec.resources.requests.storage` field of the [K8s PersistentVolumeClaimSpec](https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-claim-v1/#PersistentVolumeClaimSpec).
+"""
+
+
+def create_k8s_persistentvolumeclaim_storage_request(
+ meter: Meter,
+) -> UpDownCounter:
+ """The storage requested by the PersistentVolumeClaim"""
+ return meter.create_up_down_counter(
+ name=K8S_PERSISTENTVOLUMECLAIM_STORAGE_REQUEST,
+ description="The storage requested by the PersistentVolumeClaim.",
+ unit="By",
+ )
+
+
K8S_POD_CPU_TIME: Final = "k8s.pod.cpu.time"
"""
Total CPU time consumed
@@ -1402,7 +1763,7 @@ Note: CPU usage of the specific Pod on all available CPU cores, averaged over th
def create_k8s_pod_cpu_usage(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Pod's CPU usage, measured in cpus. Range from 0 to the number of allocatable CPUs"""
return meter.create_observable_gauge(
@@ -1545,7 +1906,7 @@ Note: Total memory usage of the Pod.
def create_k8s_pod_memory_usage(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Memory usage of the Pod"""
return meter.create_observable_gauge(
@@ -1658,7 +2019,7 @@ The actual accuracy would depend on the instrumentation and operating system.
def create_k8s_pod_uptime(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""The time the Pod has been running"""
return meter.create_observable_gauge(
@@ -2508,7 +2869,7 @@ The `k8s.service.publish_not_ready_addresses` resource attribute indicates this
def create_k8s_service_endpoint_count(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Number of endpoints for a service by condition and address type"""
return meter.create_observable_gauge(
@@ -2542,7 +2903,7 @@ guarantee that the load balancer is healthy.
def create_k8s_service_load_balancer_ingress_count(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Number of load balancer ingress points (external IPs/hostnames) assigned to the service"""
return meter.create_observable_gauge(
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/mcp_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/mcp_metrics.py
index 345735c..1c8834c 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/mcp_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/mcp_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/messaging_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/messaging_metrics.py
index 32023a7..d367135 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/messaging_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/messaging_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/nfs_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/nfs_metrics.py
index e23b049..a3b7091 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/nfs_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/nfs_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/openshift_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/openshift_metrics.py
index 08f6343..b0f70ea 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/openshift_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/openshift_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/otel_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/otel_metrics.py
index a3f24d2..199308d 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/otel_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/otel_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/process_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/process_metrics.py
index 3509f7e..d851bae 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/process_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/process_metrics.py
@@ -1,27 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 (
- Callable,
- Final,
- Generator,
- Iterable,
- Optional,
- Sequence,
- Union,
-)
+# SPDX-License-Identifier: Apache-2.0
+
+
+from collections.abc import Callable, Generator, Iterable, Sequence
+from typing import Final
from opentelemetry.metrics import (
CallbackOptions,
@@ -33,10 +15,11 @@ from opentelemetry.metrics import (
)
# pylint: disable=invalid-name
-CallbackT = Union[
- Callable[[CallbackOptions], Iterable[Observation]],
- Generator[Iterable[Observation], CallbackOptions, None],
-]
+CallbackT = (
+ Callable[[CallbackOptions], Iterable[Observation]]
+ | Generator[Iterable[Observation], CallbackOptions, None]
+)
+
PROCESS_CONTEXT_SWITCHES: Final = "process.context_switches"
"""
@@ -57,17 +40,17 @@ def create_process_context_switches(meter: Meter) -> Counter:
PROCESS_CPU_TIME: Final = "process.cpu.time"
"""
-Total CPU seconds broken down by different states
+Total CPU seconds broken down by different CPU modes
Instrument: counter
Unit: s
"""
def create_process_cpu_time(meter: Meter) -> Counter:
- """Total CPU seconds broken down by different states"""
+ """Total CPU seconds broken down by different CPU modes"""
return meter.create_counter(
name=PROCESS_CPU_TIME,
- description="Total CPU seconds broken down by different states.",
+ description="Total CPU seconds broken down by different CPU modes.",
unit="s",
)
@@ -81,7 +64,7 @@ Unit: 1
def create_process_cpu_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Difference in process.cpu.time since the last measurement, divided by the elapsed time and number of CPUs available to the process"""
return meter.create_observable_gauge(
@@ -241,7 +224,7 @@ The actual accuracy would depend on the instrumentation and operating system.
def create_process_uptime(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""The time the process has been running"""
return meter.create_observable_gauge(
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/rpc_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/rpc_metrics.py
index bf85274..47d1f5d 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/rpc_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/rpc_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/system_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/system_metrics.py
index edbca89..194230e 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/system_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/system_metrics.py
@@ -1,27 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 (
- Callable,
- Final,
- Generator,
- Iterable,
- Optional,
- Sequence,
- Union,
-)
+# SPDX-License-Identifier: Apache-2.0
+
+
+from collections.abc import Callable, Generator, Iterable, Sequence
+from typing import Final
from opentelemetry.metrics import (
CallbackOptions,
@@ -33,10 +15,11 @@ from opentelemetry.metrics import (
)
# pylint: disable=invalid-name
-CallbackT = Union[
- Callable[[CallbackOptions], Iterable[Observation]],
- Generator[Iterable[Observation], CallbackOptions, None],
-]
+CallbackT = (
+ Callable[[CallbackOptions], Iterable[Observation]]
+ | Generator[Iterable[Observation], CallbackOptions, None]
+)
+
SYSTEM_CPU_FREQUENCY: Final = "system.cpu.frequency"
"""
@@ -47,7 +30,7 @@ Unit: Hz
def create_system_cpu_frequency(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Operating frequency of the logical CPU in Hertz"""
return meter.create_observable_gauge(
@@ -120,7 +103,7 @@ Unit: 1
def create_system_cpu_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""For each logical CPU, the utilization is calculated as the change in cumulative CPU time (cpu.time) over a measurement interval, divided by the elapsed time"""
return meter.create_observable_gauge(
@@ -288,7 +271,7 @@ Unit: 1
def create_system_filesystem_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Fraction of filesystem bytes used"""
return meter.create_observable_gauge(
@@ -368,6 +351,135 @@ def create_system_memory_linux_available(meter: Meter) -> UpDownCounter:
)
+SYSTEM_MEMORY_LINUX_HUGEPAGES_LIMIT: Final = (
+ "system.memory.linux.hugepages.limit"
+)
+"""
+Total number of hugepages available
+Instrument: updowncounter
+Unit: {page}
+"""
+
+
+def create_system_memory_linux_hugepages_limit(meter: Meter) -> UpDownCounter:
+ """Total number of hugepages available"""
+ return meter.create_up_down_counter(
+ name=SYSTEM_MEMORY_LINUX_HUGEPAGES_LIMIT,
+ description="Total number of hugepages available.",
+ unit="{page}",
+ )
+
+
+SYSTEM_MEMORY_LINUX_HUGEPAGES_PAGE_SIZE: Final = (
+ "system.memory.linux.hugepages.page_size"
+)
+"""
+System hugepage size in bytes
+Instrument: updowncounter
+Unit: By
+"""
+
+
+def create_system_memory_linux_hugepages_page_size(
+ meter: Meter,
+) -> UpDownCounter:
+ """System hugepage size in bytes"""
+ return meter.create_up_down_counter(
+ name=SYSTEM_MEMORY_LINUX_HUGEPAGES_PAGE_SIZE,
+ description="System hugepage size in bytes.",
+ unit="By",
+ )
+
+
+SYSTEM_MEMORY_LINUX_HUGEPAGES_RESERVED: Final = (
+ "system.memory.linux.hugepages.reserved"
+)
+"""
+Number of reserved hugepages
+Instrument: updowncounter
+Unit: {page}
+Note: Hugepages for which a commitment to allocate has been made, but no allocation has yet been made.
+This is reported as a separate metric rather than a `usage` state because reserved pages are already counted in `free` pages.
+They represent a subset of free pages that cannot be used for non-reserved allocations.
+"""
+
+
+def create_system_memory_linux_hugepages_reserved(
+ meter: Meter,
+) -> UpDownCounter:
+ """Number of reserved hugepages"""
+ return meter.create_up_down_counter(
+ name=SYSTEM_MEMORY_LINUX_HUGEPAGES_RESERVED,
+ description="Number of reserved hugepages.",
+ unit="{page}",
+ )
+
+
+SYSTEM_MEMORY_LINUX_HUGEPAGES_SURPLUS: Final = (
+ "system.memory.linux.hugepages.surplus"
+)
+"""
+Number of surplus hugepages
+Instrument: updowncounter
+Unit: {page}
+Note: Overcommitted hugepages beyond the persistent pool.
+This is reported as a separate metric rather than a `usage` state because surplus pages can be in either `used` or `free` state.
+Including them in `usage` would break the convention that `usage` states sum to the `limit`.
+"""
+
+
+def create_system_memory_linux_hugepages_surplus(
+ meter: Meter,
+) -> UpDownCounter:
+ """Number of surplus hugepages"""
+ return meter.create_up_down_counter(
+ name=SYSTEM_MEMORY_LINUX_HUGEPAGES_SURPLUS,
+ description="Number of surplus hugepages.",
+ unit="{page}",
+ )
+
+
+SYSTEM_MEMORY_LINUX_HUGEPAGES_USAGE: Final = (
+ "system.memory.linux.hugepages.usage"
+)
+"""
+Number of hugepages in use by state
+Instrument: updowncounter
+Unit: {page}
+"""
+
+
+def create_system_memory_linux_hugepages_usage(meter: Meter) -> UpDownCounter:
+ """Number of hugepages in use by state"""
+ return meter.create_up_down_counter(
+ name=SYSTEM_MEMORY_LINUX_HUGEPAGES_USAGE,
+ description="Number of hugepages in use by state.",
+ unit="{page}",
+ )
+
+
+SYSTEM_MEMORY_LINUX_HUGEPAGES_UTILIZATION: Final = (
+ "system.memory.linux.hugepages.utilization"
+)
+"""
+Percentage of hugepages in use by state
+Instrument: gauge
+Unit: 1
+"""
+
+
+def create_system_memory_linux_hugepages_utilization(
+ meter: Meter, callbacks: Sequence[CallbackT] | None
+) -> ObservableGauge:
+ """Percentage of hugepages in use by state"""
+ return meter.create_observable_gauge(
+ name=SYSTEM_MEMORY_LINUX_HUGEPAGES_UTILIZATION,
+ callbacks=callbacks,
+ description="Percentage of hugepages in use by state.",
+ unit="1",
+ )
+
+
SYSTEM_MEMORY_LINUX_SHARED: Final = "system.memory.linux.shared"
"""
Shared memory used (mostly by tmpfs)
@@ -448,7 +560,7 @@ Unit: 1
def create_system_memory_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Percentage of memory bytes in use"""
return meter.create_observable_gauge(
@@ -659,7 +771,7 @@ Unit: 1
def create_system_paging_utilization(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Swap (unix) or pagefile (windows) utilization"""
return meter.create_observable_gauge(
@@ -715,7 +827,7 @@ The actual accuracy would depend on the instrumentation and operating system.
def create_system_uptime(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""The time the system has been running"""
return meter.create_observable_gauge(
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/vcs_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/vcs_metrics.py
index f3737ff..1539763 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/vcs_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/_incubating/metrics/vcs_metrics.py
@@ -1,27 +1,9 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 (
- Callable,
- Final,
- Generator,
- Iterable,
- Optional,
- Sequence,
- Union,
-)
+# SPDX-License-Identifier: Apache-2.0
+
+
+from collections.abc import Callable, Generator, Iterable, Sequence
+from typing import Final
from opentelemetry.metrics import (
CallbackOptions,
@@ -32,10 +14,11 @@ from opentelemetry.metrics import (
)
# pylint: disable=invalid-name
-CallbackT = Union[
- Callable[[CallbackOptions], Iterable[Observation]],
- Generator[Iterable[Observation], CallbackOptions, None],
-]
+CallbackT = (
+ Callable[[CallbackOptions], Iterable[Observation]]
+ | Generator[Iterable[Observation], CallbackOptions, None]
+)
+
VCS_CHANGE_COUNT: Final = "vcs.change.count"
"""
@@ -63,7 +46,7 @@ Unit: s
def create_vcs_change_duration(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""The time duration a change (pull request/merge request/changelist) has been in a given state"""
return meter.create_observable_gauge(
@@ -83,7 +66,7 @@ Unit: s
def create_vcs_change_time_to_approval(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""The amount of time since its creation it took a change (pull request/merge request/changelist) to get the first approval"""
return meter.create_observable_gauge(
@@ -103,7 +86,7 @@ Unit: s
def create_vcs_change_time_to_merge(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""The amount of time since its creation it took a change (pull request/merge request/changelist) to get merged into the target(base) ref"""
return meter.create_observable_gauge(
@@ -123,7 +106,7 @@ Unit: {contributor}
def create_vcs_contributor_count(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""The number of unique contributors to a repository"""
return meter.create_observable_gauge(
@@ -163,7 +146,7 @@ If number of lines added/removed should be calculated from the start of time, th
def create_vcs_ref_lines_delta(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""The number of lines added/removed in a ref (branch) relative to the ref from the `vcs.ref.base.name` attribute"""
return meter.create_observable_gauge(
@@ -185,7 +168,7 @@ instrumentation SHOULD report two measurements: 3 and 2 (both positive numbers)
def create_vcs_ref_revisions_delta(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""The number of revisions (commits) a ref (branch) is ahead/behind the branch from the `vcs.ref.base.name` attribute"""
return meter.create_observable_gauge(
@@ -205,7 +188,7 @@ Unit: s
def create_vcs_ref_time(
- meter: Meter, callbacks: Optional[Sequence[CallbackT]]
+ meter: Meter, callbacks: Sequence[CallbackT] | None
) -> ObservableGauge:
"""Time a ref (branch) created from the default branch (trunk) has existed. The `ref.type` attribute will always be `branch`"""
return meter.create_observable_gauge(
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/client_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/client_attributes.py
index d6dd88b..98ebcdd 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/client_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/client_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/code_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/code_attributes.py
index 8a33c1a..370a4dc 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/code_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/code_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/db_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/db_attributes.py
index 2653f54..e64d178 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/db_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/db_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/deployment_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/deployment_attributes.py
new file mode 100644
index 0000000..338f935
--- /dev/null
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/deployment_attributes.py
@@ -0,0 +1,28 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+from enum import Enum
+from typing import Final
+
+DEPLOYMENT_ENVIRONMENT_NAME: Final = "deployment.environment.name"
+"""
+Name of the [deployment environment](https://wikipedia.org/wiki/Deployment_environment) (aka deployment tier).
+Note: `deployment.environment.name` does not affect the uniqueness constraints defined through
+the `service.namespace`, `service.name` and `service.instance.id` resource attributes.
+This implies that resources carrying the following attribute combinations MUST be
+considered to be identifying the same service:
+
+- `service.name=frontend`, `deployment.environment.name=production`
+- `service.name=frontend`, `deployment.environment.name=staging`.
+"""
+
+
+class DeploymentEnvironmentNameValues(Enum):
+ PRODUCTION = "production"
+ """Production environment."""
+ STAGING = "staging"
+ """Staging environment."""
+ TEST = "test"
+ """Testing environment."""
+ DEVELOPMENT = "development"
+ """Development environment."""
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/error_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/error_attributes.py
index 13ef66e..9c4efd7 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/error_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/error_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
@@ -23,6 +12,12 @@ Note: The `error.type` SHOULD be predictable, and SHOULD have low cardinality.
When `error.type` is set to a type (e.g., an exception type), its
canonical class name identifying the type within the artifact SHOULD be used.
+If the recorded error type is a wrapper that is not meaningful for
+failure classification, instrumentation MAY use the type of the inner
+error instead. For example, in Go, errors created with `fmt.Errorf`
+using `%w` MAY be unwrapped when the wrapper type does not help
+classify the failure.
+
Instrumentations SHOULD document the list of errors they report.
The cardinality of `error.type` within one instrumentation library SHOULD be low.
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/exception_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/exception_attributes.py
index 1c4374e..2d7e748 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/exception_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/exception_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
@@ -35,4 +24,9 @@ A stacktrace as a string in the natural representation for the language runtime.
EXCEPTION_TYPE: Final = "exception.type"
"""
The type of the exception (its fully-qualified class name, if applicable). The dynamic type of the exception should be preferred over the static type in languages that support it.
+Note: If the recorded exception type is a wrapper that is not meaningful for
+failure classification, instrumentation MAY use the type of the inner
+exception instead. For example, in Go, errors created with `fmt.Errorf`
+using `%w` MAY be unwrapped when the wrapper type does not help
+classify the failure.
"""
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/http_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/http_attributes.py
index 4d688aa..5dbad61 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/http_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/http_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/network_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/network_attributes.py
index c09fe2e..67e399e 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/network_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/network_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/otel_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/otel_attributes.py
index 134e246..3bb5747 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/otel_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/otel_attributes.py
@@ -1,20 +1,15 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
+OTEL_EVENT_NAME: Final = "otel.event.name"
+"""
+Identifies the class / type of event.
+Note: This attribute SHOULD be used by non-OTLP exporters when destination does not support `EventName` or equivalent field. This attribute MAY be used by applications using existing logging libraries so that it can be used to set the `EventName` field by Collector or SDK components.
+"""
+
OTEL_SCOPE_NAME: Final = "otel.scope.name"
"""
The name of the instrumentation scope - (`InstrumentationScope.Name` in OTLP).
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/server_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/server_attributes.py
index 6b2658d..a67312e 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/server_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/server_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/service_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/service_attributes.py
index e8b5da8..1ee79b9 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/service_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/service_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
@@ -48,7 +37,8 @@ port.
SERVICE_NAME: Final = "service.name"
"""
Logical name of the service.
-Note: MUST be the same for all instances of horizontally scaled services. If the value was not specified, SDKs MUST fallback to `unknown_service:` concatenated with [`process.executable.name`](process.md), e.g. `unknown_service:bash`. If `process.executable.name` is not available, the value MUST be set to `unknown_service`.
+Note: MUST be the same for all instances of horizontally scaled services. If the value was not specified, SDKs MUST fallback to `unknown_service:` concatenated with the process executable name, e.g. `unknown_service:bash`. If the process executable name is not available, the value MUST be set to `unknown_service`.
+The process executable name is the name of the process executable, the same value as described by the [`process.executable.name`](process.md) resource attribute.
"""
SERVICE_NAMESPACE: Final = "service.namespace"
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/telemetry_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/telemetry_attributes.py
index 29aadeb..aa811a4 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/telemetry_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/telemetry_attributes.py
@@ -1,20 +1,21 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
from typing import Final
+TELEMETRY_DISTRO_NAME: Final = "telemetry.distro.name"
+"""
+The name of the auto instrumentation agent or distribution, if used.
+Note: Official auto instrumentation agents and distributions SHOULD set the `telemetry.distro.name` attribute to
+a string starting with `opentelemetry-`, e.g. `opentelemetry-java-instrumentation`.
+"""
+
+TELEMETRY_DISTRO_VERSION: Final = "telemetry.distro.version"
+"""
+The version string of the auto instrumentation agent or distribution, if used.
+"""
+
TELEMETRY_SDK_LANGUAGE: Final = "telemetry.sdk.language"
"""
The language of the telemetry SDK.
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/url_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/url_attributes.py
index 0d8d9cb..60970e0 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/url_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/url_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/user_agent_attributes.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/user_agent_attributes.py
index af5002e..6d7e44f 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/user_agent_attributes.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/attributes/user_agent_attributes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/__init__.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/__init__.py
index db53aad..736c6b4 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/__init__.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing_extensions import deprecated
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/db_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/db_metrics.py
index 13c9e50..6031728 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/db_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/db_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/http_metrics.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/http_metrics.py
index d0e0db6..924d41d 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/http_metrics.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/metrics/http_metrics.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/resource/__init__.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/resource/__init__.py
index 6e4adfe..59c2fd5 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/resource/__init__.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/resource/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-lines
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/schemas.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/schemas.py
index edfb939..5438b1b 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/schemas.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/schemas.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from enum import Enum
@@ -100,5 +89,15 @@ class Schemas(Enum):
The URL of the OpenTelemetry schema version 1.40.0.
"""
+ V1_41_0 = "https://opentelemetry.io/schemas/1.41.0"
+ """
+ The URL of the OpenTelemetry schema version 1.41.0.
+ """
+
+ V1_41_1 = "https://opentelemetry.io/schemas/1.41.1"
+ """
+ The URL of the OpenTelemetry schema version 1.41.1.
+ """
+
# when generating new semantic conventions,
# make sure to add new versions version here.
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/trace/__init__.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/trace/__init__.py
index c03ca55..ceb562d 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/trace/__init__.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/trace/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-lines
diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/version/__init__.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/version/__init__.py
index 766799c..23888c2 100644
--- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/version/__init__.py
+++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "0.62b0"
+__version__ = "0.63b1"
diff --git a/opentelemetry-semantic-conventions/test-requirements.txt b/opentelemetry-semantic-conventions/test-requirements.txt
index 7f3c6da..47ec2c1 100644
--- a/opentelemetry-semantic-conventions/test-requirements.txt
+++ b/opentelemetry-semantic-conventions/test-requirements.txt
@@ -1,5 +1,4 @@
asgiref==3.7.2
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
diff --git a/opentelemetry-semantic-conventions/tests/test_semconv.py b/opentelemetry-semantic-conventions/tests/test_semconv.py
index 18fe3f0..1594d61 100644
--- a/opentelemetry-semantic-conventions/tests/test_semconv.py
+++ b/opentelemetry-semantic-conventions/tests/test_semconv.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
from importlib.util import find_spec
diff --git a/propagator/opentelemetry-propagator-b3/benchmarks/trace/propagation/test_benchmark_b3_format.py b/propagator/opentelemetry-propagator-b3/benchmarks/trace/propagation/test_benchmark_b3_format.py
index 26fdb65..d5ecbe7 100644
--- a/propagator/opentelemetry-propagator-b3/benchmarks/trace/propagation/test_benchmark_b3_format.py
+++ b/propagator/opentelemetry-propagator-b3/benchmarks/trace/propagation/test_benchmark_b3_format.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import opentelemetry.propagators.b3 as b3_format
from opentelemetry.sdk.trace import TracerProvider
diff --git a/propagator/opentelemetry-propagator-b3/pyproject.toml b/propagator/opentelemetry-propagator-b3/pyproject.toml
index 5240f2e..8c78019 100644
--- a/propagator/opentelemetry-propagator-b3/pyproject.toml
+++ b/propagator/opentelemetry-propagator-b3/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenTelemetry B3 Propagator"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -18,7 +18,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
diff --git a/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/__init__.py b/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/__init__.py
index 9bcce9b..3c4de0c 100644
--- a/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/__init__.py
+++ b/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import typing
from re import compile as re_compile
@@ -49,7 +38,7 @@ class B3MultiFormat(TextMapPropagator):
def extract(
self,
carrier: CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
getter: Getter = default_getter,
) -> Context:
if context is None:
@@ -130,7 +119,7 @@ class B3MultiFormat(TextMapPropagator):
def inject(
self,
carrier: CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
setter: Setter = default_setter,
) -> None:
span = trace.get_current_span(context=context)
@@ -151,7 +140,7 @@ class B3MultiFormat(TextMapPropagator):
setter.set(carrier, self.SAMPLED_KEY, "1" if sampled else "0")
@property
- def fields(self) -> typing.Set[str]:
+ def fields(self) -> set[str]:
return {
self.TRACE_ID_KEY,
self.SPAN_ID_KEY,
@@ -169,7 +158,7 @@ class B3SingleFormat(B3MultiFormat):
def inject(
self,
carrier: CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
setter: Setter = default_setter,
) -> None:
span = trace.get_current_span(context=context)
@@ -189,7 +178,7 @@ class B3SingleFormat(B3MultiFormat):
setter.set(carrier, self.SINGLE_HEADER_KEY, "-".join(fields))
@property
- def fields(self) -> typing.Set[str]:
+ def fields(self) -> set[str]:
return {self.SINGLE_HEADER_KEY}
@@ -203,7 +192,7 @@ class B3Format(B3MultiFormat):
def _extract_first_element(
items: typing.Iterable[CarrierT],
-) -> typing.Optional[CarrierT]:
+) -> CarrierT | None:
if items is None:
return None
return next(iter(items), None)
diff --git a/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/version/__init__.py b/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/version/__init__.py
index 49e1254..a400b9b 100644
--- a/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/version/__init__.py
+++ b/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.0"
+__version__ = "1.42.1"
diff --git a/propagator/opentelemetry-propagator-b3/test-requirements.txt b/propagator/opentelemetry-propagator-b3/test-requirements.txt
index 1cd6c8d..b350703 100644
--- a/propagator/opentelemetry-propagator-b3/test-requirements.txt
+++ b/propagator/opentelemetry-propagator-b3/test-requirements.txt
@@ -1,5 +1,4 @@
asgiref==3.7.2
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
@@ -8,7 +7,6 @@ pytest==7.4.4
tomli==2.0.1
typing_extensions==4.10.0
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e opentelemetry-sdk
-e opentelemetry-semantic-conventions
diff --git a/propagator/opentelemetry-propagator-b3/tests/__init__.py b/propagator/opentelemetry-propagator-b3/tests/__init__.py
index b0a6f42..e57cf4a 100644
--- a/propagator/opentelemetry-propagator-b3/tests/__init__.py
+++ b/propagator/opentelemetry-propagator-b3/tests/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/propagator/opentelemetry-propagator-b3/tests/test_b3_format.py b/propagator/opentelemetry-propagator-b3/tests/test_b3_format.py
index 6625712..172ba2b 100644
--- a/propagator/opentelemetry-propagator-b3/tests/test_b3_format.py
+++ b/propagator/opentelemetry-propagator-b3/tests/test_b3_format.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
from abc import abstractmethod
diff --git a/propagator/opentelemetry-propagator-jaeger/pyproject.toml b/propagator/opentelemetry-propagator-jaeger/pyproject.toml
index 91440f6..2e6fef6 100644
--- a/propagator/opentelemetry-propagator-jaeger/pyproject.toml
+++ b/propagator/opentelemetry-propagator-jaeger/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenTelemetry Jaeger Propagator"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -18,7 +18,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
diff --git a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py
index 6b6eb8f..5255169 100644
--- a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py
+++ b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import typing
import urllib.parse
@@ -41,7 +30,7 @@ class JaegerPropagator(TextMapPropagator):
def extract(
self,
carrier: CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
getter: Getter = default_getter,
) -> Context:
if context is None:
@@ -72,7 +61,7 @@ class JaegerPropagator(TextMapPropagator):
def inject(
self,
carrier: CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
setter: Setter = default_setter,
) -> None:
span = trace.get_current_span(context=context)
@@ -109,7 +98,7 @@ class JaegerPropagator(TextMapPropagator):
setter.set(carrier, baggage_key, urllib.parse.quote(str(value)))
@property
- def fields(self) -> typing.Set[str]:
+ def fields(self) -> set[str]:
return {self.TRACE_ID_KEY}
def _extract_baggage(self, getter, carrier, context):
@@ -134,7 +123,7 @@ def _format_uber_trace_id(trace_id, span_id, parent_span_id, flags):
def _extract_first_element(
items: typing.Iterable[CarrierT],
-) -> typing.Optional[CarrierT]:
+) -> CarrierT | None:
if items is None:
return None
return next(iter(items), None)
@@ -142,7 +131,7 @@ def _extract_first_element(
def _parse_trace_id_header(
items: typing.Iterable[CarrierT],
-) -> typing.Tuple[int]:
+) -> tuple[int]:
invalid_header_result = (trace.INVALID_TRACE_ID, trace.INVALID_SPAN_ID, 0)
header = _extract_first_element(items)
@@ -163,9 +152,7 @@ def _parse_trace_id_header(
return trace_id, span_id, flags
-def _int_from_hex_str(
- identifier: str, default: typing.Optional[int]
-) -> typing.Optional[int]:
+def _int_from_hex_str(identifier: str, default: int | None) -> int | None:
try:
return int(identifier, 16)
except ValueError:
diff --git a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/version/__init__.py b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/version/__init__.py
index 49e1254..a400b9b 100644
--- a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/version/__init__.py
+++ b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.0"
+__version__ = "1.42.1"
diff --git a/propagator/opentelemetry-propagator-jaeger/test-requirements.txt b/propagator/opentelemetry-propagator-jaeger/test-requirements.txt
index c57dc8f..499b901 100644
--- a/propagator/opentelemetry-propagator-jaeger/test-requirements.txt
+++ b/propagator/opentelemetry-propagator-jaeger/test-requirements.txt
@@ -1,5 +1,4 @@
asgiref==3.7.2
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
@@ -8,7 +7,6 @@ pytest==7.4.4
tomli==2.0.1
typing_extensions==4.10.0
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e opentelemetry-sdk
-e opentelemetry-semantic-conventions
diff --git a/propagator/opentelemetry-propagator-jaeger/tests/__init__.py b/propagator/opentelemetry-propagator-jaeger/tests/__init__.py
index b0a6f42..e57cf4a 100644
--- a/propagator/opentelemetry-propagator-jaeger/tests/__init__.py
+++ b/propagator/opentelemetry-propagator-jaeger/tests/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py
index 8e7519b..481b56b 100644
--- a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py
+++ b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from unittest.mock import Mock
diff --git a/pyproject.toml b/pyproject.toml
index 294a7c4..28295af 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "opentelemetry-python"
version = "0.0.0" # This is not used.
-requires-python = ">=3.9"
+requires-python = ">=3.10"
dependencies = [
"opentelemetry-api",
"opentelemetry-sdk",
@@ -12,6 +12,7 @@ dependencies = [
"opentelemetry-exporter-otlp-proto-grpc",
"opentelemetry-exporter-otlp-proto-http",
"opentelemetry-exporter-otlp-proto-common",
+ "opentelemetry-exporter-otlp-json-common",
"opentelemetry-exporter-zipkin-json",
"opentelemetry-exporter-prometheus",
"opentelemetry-propagator-jaeger",
@@ -34,6 +35,7 @@ opentelemetry-test-utils = { workspace = true }
opentelemetry-exporter-otlp-proto-grpc = { workspace = true }
opentelemetry-exporter-otlp-proto-http = { workspace = true }
opentelemetry-exporter-otlp-proto-common = { workspace = true }
+opentelemetry-exporter-otlp-json-common = { workspace = true }
opentelemetry-exporter-zipkin-json = { workspace = true }
opentelemetry-exporter-prometheus = {workspace = true }
opentelemetry-propagator-jaeger = { workspace = true }
@@ -65,7 +67,7 @@ log_cli = true
[tool.ruff]
# https://docs.astral.sh/ruff/configuration/
-target-version = "py39"
+target-version = "py310"
line-length = 79
extend-exclude = [
"*_pb2*.py*",
@@ -77,20 +79,16 @@ output-format = "concise"
# https://docs.astral.sh/ruff/linter/#rule-selection
# pylint: https://github.com/astral-sh/ruff/issues/970
select = [
- "I", # isort
- "F", # pyflakes
- "E", # pycodestyle errors
- "W", # pycodestyle warnings
- "PLC", # pylint convention
- "PLE", # pylint error
- "Q", # flake8-quotes
+ "I", # https://docs.astral.sh/ruff/rules/#isort-i
+ "F", # https://docs.astral.sh/ruff/rules/#pyflakes-f
+ "E", # https://docs.astral.sh/ruff/rules/#error-e
+ "W", # https://docs.astral.sh/ruff/rules/#warning-w
+ "PLC", # https://docs.astral.sh/ruff/rules/#convention-plc
+ "PLE", # https://docs.astral.sh/ruff/rules/#error-ple
+ "Q", # https://docs.astral.sh/ruff/rules/#flake8-quotes-q
"G", # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g
- "TID", # flake8-tidy-imports
- "UP011", # lru-cache-without-parameters
- "UP015", # redundant-open-modes
- "UP032", # f-string
- "UP034", # extraneous-parentheses
- "UP037", # quoted-annotation
+ "TID", # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid
+ "UP", # https://docs.astral.sh/ruff/rules/#pyupgrade-up
]
ignore = [
@@ -114,7 +112,7 @@ known-first-party = ["opentelemetry", "opentelemetry_example_app"]
[tool.pyright]
typeCheckingMode = "standard"
-pythonVersion = "3.9"
+pythonVersion = "3.10"
include = [
"opentelemetry-semantic-conventions",
@@ -123,6 +121,7 @@ include = [
"opentelemetry-proto-json",
"exporter/opentelemetry-exporter-otlp-proto-grpc",
"exporter/opentelemetry-exporter-otlp-proto-http",
+ "exporter/opentelemetry-exporter-otlp-json-common",
"codegen/opentelemetry-codegen-json"
]
@@ -130,19 +129,18 @@ exclude = [
"opentelemetry-sdk/tests",
"opentelemetry-sdk/src/opentelemetry/sdk/_events",
"opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/__init__.py",
- "opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py",
"opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/aggregation.py",
"opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exemplar/exemplar_reservoir.py",
- "opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/exponential_histogram/",
"opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/",
"opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/instrument.py",
"opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/measurement_consumer.py",
"opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/metric_reader_storage.py",
- "opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py",
"opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/view.py",
"opentelemetry-sdk/benchmarks",
"exporter/opentelemetry-exporter-otlp-proto-grpc/tests",
"exporter/opentelemetry-exporter-otlp-proto-http/tests",
+ "exporter/opentelemetry-exporter-otlp-json-common/tests",
+ "exporter/opentelemetry-exporter-otlp-json-common/benchmarks",
]
# When packages are correct typed add them to the strict list
@@ -162,8 +160,45 @@ formatters = ["ruff-format", "ruff-check"]
use-standard-collections = true
use-schema-description = true
use-title-as-name = true
-use-union-operator = false # set to true when Python 3.9 support is dropped
+use-union-operator = true
target-python-version = "3.10"
+custom-template-dir = "opentelemetry-sdk/codegen"
+additional-imports = "typing.ClassVar,opentelemetry.sdk._configuration._common._additional_properties"
+
+[tool.towncrier]
+directory = ".changelog"
+filename = "CHANGELOG.md"
+start_string = "<!-- changelog start -->\n"
+template = "scripts/changelog_template.j2"
+issue_format = "[#{issue}](https://github.com/open-telemetry/opentelemetry-python/pull/{issue})"
+wrap = true # wrap fragments to 79 char line length
+issue_pattern = "^(\\d+)" # only PR numbers as fragment prefix (e.g. 1234.fixed)
+
+# NOTE: remember to update the changelog workflow if these types change
+[[tool.towncrier.type]]
+directory = "added"
+name = "Added"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "changed"
+name = "Changed"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "deprecated"
+name = "Deprecated"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "removed"
+name = "Removed"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "fixed"
+name = "Fixed"
+showcontent = true
[dependency-groups]
dev = [
@@ -172,4 +207,5 @@ dev = [
"pre-commit",
"datamodel-code-generator[http]",
"datamodel-code-generator[ruff]",
+ "towncrier",
]
diff --git a/scripts/add_required_checks.py b/scripts/add_required_checks.py
index ee7d38d..9c0db3c 100644
--- a/scripts/add_required_checks.py
+++ b/scripts/add_required_checks.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
# This script is to be used by maintainers by running it locally.
from json import dumps
diff --git a/scripts/changelog_template.j2 b/scripts/changelog_template.j2
new file mode 100644
index 0000000..55ac56f
--- /dev/null
+++ b/scripts/changelog_template.j2
@@ -0,0 +1,24 @@
+## Version {{ versiondata.version }} ({{ versiondata.date }})
+
+{% for section, _ in sections.items() %}
+{%- if section %}{{ section }}{% endif -%}
+
+{% if sections[section] %}
+{% for category, val in definitions.items() if category in sections[section] %}
+### {{ definitions[category]['name'] }}
+
+{% for text, values in sections[section][category].items() %}
+{% if "\n - " in text or '\n * ' in text %}
+{%- set main_text, sub_items = text.split('\n', 1) %}
+- {{ main_text }} ({{ values|join(', ') }})
+{% if sub_items %}
+ {{- sub_items }}
+{% endif %}
+{% else %}
+- {{ text }} ({{ values|join(', ') }})
+{% endif %}
+{% endfor %}
+
+{% endfor %}
+{% endif %}
+{% endfor %}
diff --git a/scripts/check_for_valid_readme.py b/scripts/check_for_valid_readme.py
index d133ae3..4b1e11f 100644
--- a/scripts/check_for_valid_readme.py
+++ b/scripts/check_for_valid_readme.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
"""Test script to check given paths for valid README.rst files."""
import argparse
diff --git a/scripts/check_license_header.py b/scripts/check_license_header.py
new file mode 100644
index 0000000..8a2f03a
--- /dev/null
+++ b/scripts/check_license_header.py
@@ -0,0 +1,102 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+import os
+import sys
+from pathlib import Path
+
+EXPECTED_LINES = (
+ "# Copyright The OpenTelemetry Authors\n",
+ "# SPDX-License-Identifier: Apache-2.0\n",
+)
+
+SKIP_SUFFIXES = (
+ "_pb2.py",
+ "_pb2_grpc.py",
+)
+
+SKIP_DIRS = (
+ "target",
+ ".tox",
+ ".venv",
+ "__pycache__",
+ "node_modules",
+)
+
+GENERATED_MARKERS = (
+ "# generated by datamodel-codegen",
+ "# Generated by the protocol buffer compiler",
+ "# Generated by the gRPC Python protocol compiler",
+)
+
+
+def should_skip(relpath):
+ parts = relpath.split(os.sep)
+ if any(d in parts for d in SKIP_DIRS):
+ return True
+ basename = os.path.basename(relpath)
+ return any(basename.endswith(s) for s in SKIP_SUFFIXES)
+
+
+def check_file(path):
+ with open(path) as f:
+ lines = f.readlines()
+
+ # Skip empty files (e.g. namespace __init__.py)
+ if not lines or all(line.strip() == "" for line in lines):
+ return True
+
+ # Skip generated files
+ if any(lines[0].startswith(m) for m in GENERATED_MARKERS):
+ return True
+
+ # If file starts with a shebang, check lines after it
+ start = 0
+ if lines[0].startswith("#!"):
+ start = 1
+ # Skip optional blank line after shebang
+ if start < len(lines) and lines[start].strip() == "":
+ start += 1
+
+ if len(lines) < start + 2:
+ return False
+
+ return (
+ lines[start] == EXPECTED_LINES[0]
+ and lines[start + 1] == EXPECTED_LINES[1]
+ )
+
+
+def main():
+ root = str(Path(__file__).resolve().parents[1])
+ failures = []
+
+ for dirpath, dirnames, filenames in os.walk(root):
+ dirnames[:] = [d for d in dirnames if not d.startswith(".")]
+
+ for filename in filenames:
+ if not filename.endswith(".py"):
+ continue
+ path = os.path.join(dirpath, filename)
+ relpath = os.path.relpath(path, root)
+ if should_skip(relpath):
+ continue
+ if not check_file(path):
+ failures.append(relpath)
+
+ if failures:
+ print("Files missing the SPDX license header:")
+ print()
+ print(" Expected first two lines:")
+ print(" # Copyright The OpenTelemetry Authors")
+ print(" # SPDX-License-Identifier: Apache-2.0")
+ print()
+ for f in sorted(failures):
+ print(f" {f}")
+ sys.exit(1)
+
+ print("All files have the correct license header.")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/eachdist.py b/scripts/eachdist.py
index b01d073..c5f5e98 100755
--- a/scripts/eachdist.py
+++ b/scripts/eachdist.py
@@ -1,4 +1,6 @@
#!/usr/bin/env python3
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
import argparse
import os
diff --git a/scripts/fix_typealias.py b/scripts/fix_typealias.py
deleted file mode 100644
index 5e0b4fb..0000000
--- a/scripts/fix_typealias.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env python3
-"""Fix TypeAlias import in generated models.py for Python 3.9 compatibility."""
-
-import re
-import sys
-from pathlib import Path
-
-
-def fix_typealias_import(file_path: str) -> None:
- """Fix TypeAlias import for Python 3.9 compatibility.
-
- TypeAlias is only available in the typing module from Python 3.10+.
- For Python 3.9, it must be imported from typing_extensions.
- """
- path = Path(file_path)
- content = path.read_text()
-
- if "from typing import" not in content or "TypeAlias" not in content:
- print(f"No TypeAlias import found in {file_path}")
- return
-
- # Find the typing import line
- pattern = r"from typing import ([^\n]+)"
- match = re.search(pattern, content)
-
- if not match or "TypeAlias" not in match.group(1):
- print(f"TypeAlias not in typing import in {file_path}")
- return
-
- # Remove TypeAlias from typing import
- imports = [
- imp.strip()
- for imp in match.group(1).split(", ")
- if "TypeAlias" not in imp
- ]
- new_typing_import = f"from typing import {', '.join(imports)}"
-
- # Replace the old import and add typing_extensions import
- old_import = match.group(0)
- new_imports = (
- f"{new_typing_import}\n\nfrom typing_extensions import TypeAlias"
- )
-
- content = content.replace(old_import, new_imports, 1)
- path.write_text(content)
- print(f"Fixed TypeAlias import for Python 3.9 in {file_path}")
-
-
-if __name__ == "__main__":
- if len(sys.argv) != 2:
- print("Usage: fix_typealias.py <path_to_models.py>", file=sys.stderr)
- sys.exit(1)
-
- fix_typealias_import(sys.argv[1])
diff --git a/scripts/griffe_check.py b/scripts/griffe_check.py
index e30b82d..db24dec 100644
--- a/scripts/griffe_check.py
+++ b/scripts/griffe_check.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
import argparse
import sys
diff --git a/scripts/public_symbols_checker.py b/scripts/public_symbols_checker.py
index 538e29a..1066fc3 100644
--- a/scripts/public_symbols_checker.py
+++ b/scripts/public_symbols_checker.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from collections import defaultdict
from difflib import unified_diff
diff --git a/scripts/semconv/generate.sh b/scripts/semconv/generate.sh
index 841f7cd..d140e5f 100755
--- a/scripts/semconv/generate.sh
+++ b/scripts/semconv/generate.sh
@@ -5,9 +5,9 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="${SCRIPT_DIR}/../.."
# freeze the spec version to make SemanticAttributes generation reproducible
-SEMCONV_VERSION=1.40.0
+SEMCONV_VERSION=1.41.1
SEMCONV_VERSION_TAG=v$SEMCONV_VERSION
-OTEL_WEAVER_IMG_VERSION=v0.21.2
+OTEL_WEAVER_IMG_VERSION=v0.23.0
INCUBATING_DIR=_incubating
cd ${SCRIPT_DIR}
diff --git a/scripts/semconv/templates/registry/common.j2 b/scripts/semconv/templates/registry/common.j2
index b5426ca..28c7655 100644
--- a/scripts/semconv/templates/registry/common.j2
+++ b/scripts/semconv/templates/registry/common.j2
@@ -1,17 +1,6 @@
{%- macro file_header() -%}
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
{% endmacro -%}
diff --git a/scripts/semconv/templates/registry/semantic_attributes.j2 b/scripts/semconv/templates/registry/semantic_attributes.j2
index e9ffeeb..70956dc 100644
--- a/scripts/semconv/templates/registry/semantic_attributes.j2
+++ b/scripts/semconv/templates/registry/semantic_attributes.j2
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from typing import Final
diff --git a/scripts/semconv/templates/registry/semantic_metrics.j2 b/scripts/semconv/templates/registry/semantic_metrics.j2
index 0724f33..c28b55d 100644
--- a/scripts/semconv/templates/registry/semantic_metrics.j2
+++ b/scripts/semconv/templates/registry/semantic_metrics.j2
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
{% set file_name = ctx.output + ctx.root_namespace | snake_case ~ "_metrics.py" -%}
{{- template.set_file_name(file_name) -}}
@@ -50,14 +39,14 @@ from opentelemetry.metrics import {{i | map_text("py_instrument_to_type")}}
{%- endfor-%}
{%- if ctx.metrics | selectattr("instrument", "equalto", "gauge") | list | count > 0 %}
-from typing import Callable, Generator, Iterable, Optional, Sequence, Union
+from collections.abc import Callable, Generator, Iterable, Sequence
from opentelemetry.metrics import CallbackOptions, ObservableGauge, Observation
# pylint: disable=invalid-name
-CallbackT = Union[
- Callable[[CallbackOptions], Iterable[Observation]],
- Generator[Iterable[Observation], CallbackOptions, None],
-]
+CallbackT = (
+ Callable[[CallbackOptions], Iterable[Observation]]
+ | Generator[Iterable[Observation], CallbackOptions, None]
+)
{%- endif %}
{%- endif -%}
@@ -67,6 +56,7 @@ from typing import Final
{{ import_instrument_classes(filtered_metrics) }}
{%- for metric in ctx.metrics %}
+{% if metric.metric_name not in ctx.excluded_metrics %}
{% set const_name = metric.metric_name | screaming_snake_case %}
{{const_name}}: Final = "{{metric.metric_name}}"
{%- set doc_string=write_docstring(metric, const_name, "")-%}{%- if doc_string %}
@@ -76,7 +66,7 @@ from typing import Final
{% if ctx.filter == "any" %}
{% set metric_name = metric.metric_name | replace(".", "_") %}
{%- if metric.instrument == "gauge" %}
-def create_{{ metric_name }}(meter: Meter, callbacks: Optional[Sequence[CallbackT]]) -> {{metric.instrument | map_text("py_instrument_to_type")}}:
+def create_{{ metric_name }}(meter: Meter, callbacks: Sequence[CallbackT] | None) -> {{metric.instrument | map_text("py_instrument_to_type")}}:
{%- else %}
def create_{{ metric_name }}(meter: Meter) -> {{metric.instrument | map_text("py_instrument_to_type")}}:
{%- endif %}
@@ -93,4 +83,5 @@ def create_{{ metric_name }}(meter: Meter) -> {{metric.instrument | map_text("py
)
{%- endif -%}
+{% endif %}
{% endfor %}
diff --git a/scripts/semconv/templates/registry/weaver.yaml b/scripts/semconv/templates/registry/weaver.yaml
index 168d2b2..1fd635a 100644
--- a/scripts/semconv/templates/registry/weaver.yaml
+++ b/scripts/semconv/templates/registry/weaver.yaml
@@ -6,6 +6,9 @@ params:
# this behavior is fully controlled by jinja templates
excluded_attributes: ["messaging.client_id"]
+ # excluded metrics will be excluded from the generated code in jinja templates
+ excluded_metrics: []
+
stable_package_name: opentelemetry.semconv
templates:
@@ -35,7 +38,8 @@ templates:
metrics: .metrics,
output: $output + "metrics/",
stable_package_name: $stable_package_name + ".metrics",
- filter: $filter
+ filter: $filter,
+ excluded_metrics: $excluded_metrics
})
application_mode: each
text_maps:
diff --git a/scripts/tracecontext-integration-test.sh b/scripts/tracecontext-integration-test.sh
index 1195e7f..009a840 100755
--- a/scripts/tracecontext-integration-test.sh
+++ b/scripts/tracecontext-integration-test.sh
@@ -7,15 +7,11 @@ mkdir -p target
rm -rf ./target/trace-context
git clone https://github.com/w3c/trace-context ./target/trace-context
cd ./target/trace-context && git checkout $TRACECONTEXT_GIT_TAG && cd -
-# start example opentelemetry service, which propagates trace-context by
+# start example opentelemetry service, which propagates trace-context by
# default.
python ./tests/w3c_tracecontext_validation_server.py 1>&2 &
EXAMPLE_SERVER_PID=$!
-# give the app server a little time to start up. Not adding some sort
-# of delay would cause many of the tracecontext tests to fail being
-# unable to connect.
-sleep 1
-onshutdown()
+onshutdown()
{
# send a sigint, to ensure
# it is caught as a KeyboardInterrupt in the
@@ -23,6 +19,30 @@ onshutdown()
kill $EXAMPLE_SERVER_PID
}
trap onshutdown EXIT
+# Wait for the example server to accept connections on 127.0.0.1:5000
+# before running the W3C tracecontext tests. A fixed `sleep` raced the
+# Flask startup on slow CI runners and produced intermittent connection
+# errors (see issue #5104).
+wait_for_server() {
+ host=127.0.0.1
+ port=5000
+ deadline=$(( $(date +%s) + 30 ))
+ while [ "$(date +%s)" -lt "$deadline" ]; do
+ # Bail out early if the server process died.
+ if ! kill -0 "$EXAMPLE_SERVER_PID" 2>/dev/null; then
+ echo "tracecontext example server exited before becoming ready" >&2
+ return 1
+ fi
+ # Use python so we don't depend on extra tools (nc, curl, etc.).
+ if python -c "import socket,sys; s=socket.socket(); s.settimeout(1); sys.exit(0 if s.connect_ex(('$host', $port)) == 0 else 1)" 2>/dev/null; then
+ return 0
+ fi
+ sleep 0.5
+ done
+ echo "tracecontext example server did not become ready within 30s" >&2
+ return 1
+}
+wait_for_server
cd ./target/trace-context/test
# The disabled test is not compatible with an optional part of the W3C
diff --git a/scripts/update_sha.py b/scripts/update_sha.py
deleted file mode 100644
index 9bcde6f..0000000
--- a/scripts/update_sha.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright The OpenTelemetry Authors
-#
-# 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.
-
-# pylint: disable=import-error,unspecified-encoding
-
-import argparse
-
-import requests
-from ruamel.yaml import YAML
-
-API_URL = "https://api.github.com/repos/open-telemetry/opentelemetry-python-contrib/commits/"
-workflow_files = [
- ".github/workflows/test_0.yml"
- ".github/workflows/test_1.yml"
- ".github/workflows/misc_0.yml"
- ".github/workflows/contrib_0.yml"
- ".github/workflows/lint_0.yml"
-]
-
-
-def get_sha(branch):
- url = API_URL + branch
- response = requests.get(url, timeout=15)
- response.raise_for_status()
- return response.json()["sha"]
-
-
-def update_sha(sha):
- yaml = YAML()
- yaml.preserve_quotes = True
- for workflow_file in workflow_files:
- with open(workflow_file) as file:
- workflow = yaml.load(file)
- workflow["env"]["CONTRIB_REPO_SHA"] = sha
- with open(workflow_file, "w") as file:
- yaml.dump(workflow, file)
-
-
-def main():
- args = parse_args()
- sha = get_sha(args.branch)
- update_sha(sha)
-
-
-def parse_args():
- parser = argparse.ArgumentParser(
- description="Updates the SHA in the workflow file"
- )
- parser.add_argument("-b", "--branch", help="branch to use")
- return parser.parse_args()
-
-
-if __name__ == "__main__":
- main()
diff --git a/shim/opentelemetry-opencensus-shim/pyproject.toml b/shim/opentelemetry-opencensus-shim/pyproject.toml
index e6a7010..022e353 100644
--- a/shim/opentelemetry-opencensus-shim/pyproject.toml
+++ b/shim/opentelemetry-opencensus-shim/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenCensus Shim for OpenTelemetry"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -18,7 +18,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
diff --git a/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/__init__.py b/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/__init__.py
index bd49fd1..4d2eb19 100644
--- a/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/__init__.py
+++ b/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
The OpenTelemetry OpenCensus shim is a library which allows an easy migration from OpenCensus
diff --git a/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_patch.py b/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_patch.py
index 71d0948..c4d66e1 100644
--- a/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_patch.py
+++ b/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_patch.py
@@ -1,20 +1,8 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from functools import lru_cache
from logging import getLogger
-from typing import Optional
from opencensus.trace.span_context import SpanContext
from opencensus.trace.tracer import Tracer
@@ -28,7 +16,7 @@ _logger = getLogger(__name__)
def install_shim(
- tracer_provider: Optional[trace.TracerProvider] = None,
+ tracer_provider: trace.TracerProvider | None = None,
) -> None:
otel_tracer = trace.get_tracer(
"opentelemetry-opencensus-shim",
diff --git a/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_shim_span.py b/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_shim_span.py
index 2012035..c8602b0 100644
--- a/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_shim_span.py
+++ b/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_shim_span.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import logging
from datetime import datetime
diff --git a/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_shim_tracer.py b/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_shim_tracer.py
index 35386d4..b7bc55f 100644
--- a/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_shim_tracer.py
+++ b/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/_shim_tracer.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import logging
diff --git a/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/version/__init__.py b/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/version/__init__.py
index 766799c..23888c2 100644
--- a/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/version/__init__.py
+++ b/shim/opentelemetry-opencensus-shim/src/opentelemetry/shim/opencensus/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "0.62b0"
+__version__ = "0.63b1"
diff --git a/shim/opentelemetry-opencensus-shim/test-requirements.txt b/shim/opentelemetry-opencensus-shim/test-requirements.txt
index b8b6ff5..1f151ed 100644
--- a/shim/opentelemetry-opencensus-shim/test-requirements.txt
+++ b/shim/opentelemetry-opencensus-shim/test-requirements.txt
@@ -7,7 +7,6 @@ google-auth==2.28.1
googleapis-common-protos==1.63.2
grpcio==1.75.1
idna==3.7
-importlib-metadata==6.11.0
iniconfig==2.0.0
opencensus==0.11.1
opencensus-context==0.1.3
@@ -26,7 +25,6 @@ tomli==2.0.1
typing_extensions==4.12.0
urllib3==2.2.2
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e opentelemetry-sdk
-e tests/opentelemetry-test-utils
diff --git a/shim/opentelemetry-opencensus-shim/tests/test_patch.py b/shim/opentelemetry-opencensus-shim/tests/test_patch.py
index 697ddfc..ae5d9b2 100644
--- a/shim/opentelemetry-opencensus-shim/tests/test_patch.py
+++ b/shim/opentelemetry-opencensus-shim/tests/test_patch.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
diff --git a/shim/opentelemetry-opencensus-shim/tests/test_shim.py b/shim/opentelemetry-opencensus-shim/tests/test_shim.py
index 74a9edd..ef0ddc2 100644
--- a/shim/opentelemetry-opencensus-shim/tests/test_shim.py
+++ b/shim/opentelemetry-opencensus-shim/tests/test_shim.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import logging
import unittest
diff --git a/shim/opentelemetry-opencensus-shim/tests/test_shim_with_sdk.py b/shim/opentelemetry-opencensus-shim/tests/test_shim_with_sdk.py
index db993d4..2f116f4 100644
--- a/shim/opentelemetry-opencensus-shim/tests/test_shim_with_sdk.py
+++ b/shim/opentelemetry-opencensus-shim/tests/test_shim_with_sdk.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import logging
import unittest
diff --git a/shim/opentelemetry-opentracing-shim/pyproject.toml b/shim/opentelemetry-opentracing-shim/pyproject.toml
index cc9c1d0..450ef46 100644
--- a/shim/opentelemetry-opentracing-shim/pyproject.toml
+++ b/shim/opentelemetry-opentracing-shim/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "OpenTracing Shim for OpenTelemetry"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -18,7 +18,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
diff --git a/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/__init__.py b/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/__init__.py
index 7a3804a..3a3ab12 100644
--- a/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/__init__.py
+++ b/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
The OpenTelemetry OpenTracing shim is a library which allows an easy migration
@@ -35,11 +24,11 @@ following example::
# Define which OpenTelemetry Tracer provider implementation to use.
trace.set_tracer_provider(TracerProvider())
- # Create an OpenTelemetry Tracer.
- otel_tracer = trace.get_tracer(__name__)
+ # Get the OpenTelemetry TracerProvider.
+ tracer_provider = trace.get_tracer_provider()
# Create an OpenTracing shim.
- shim = create_tracer(otel_tracer)
+ shim = create_tracer(tracer_provider)
with shim.start_active_span("ProcessHTTPRequest"):
print("Processing HTTP request")
@@ -88,7 +77,7 @@ from __future__ import annotations
import logging
from types import TracebackType
-from typing import Type, TypeVar
+from typing import TypeVar
from opentracing import (
Format,
@@ -425,7 +414,7 @@ class ScopeShim(Scope):
def _end_span_scope(
self,
- exc_type: Type[BaseException] | None,
+ exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: TracebackType | None,
) -> None:
diff --git a/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/util.py b/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/util.py
index eb7d3d9..a144d4b 100644
--- a/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/util.py
+++ b/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/util.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# A default event name to be used for logging events when a better event name
# can't be derived from the event's key-value pairs.
diff --git a/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/version/__init__.py b/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/version/__init__.py
index 766799c..23888c2 100644
--- a/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/version/__init__.py
+++ b/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/version/__init__.py
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "0.62b0"
+__version__ = "0.63b1"
diff --git a/shim/opentelemetry-opentracing-shim/test-requirements.txt b/shim/opentelemetry-opentracing-shim/test-requirements.txt
index e5449e7..6844bf6 100644
--- a/shim/opentelemetry-opentracing-shim/test-requirements.txt
+++ b/shim/opentelemetry-opentracing-shim/test-requirements.txt
@@ -1,5 +1,4 @@
asgiref==3.7.2
-importlib-metadata==6.11.0
iniconfig==2.0.0
opentracing==2.4.0
packaging==24.0
@@ -9,7 +8,6 @@ pytest==7.4.4
tomli==2.0.1
typing_extensions==4.10.0
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e opentelemetry-sdk
-e tests/opentelemetry-test-utils
diff --git a/shim/opentelemetry-opentracing-shim/tests/test_shim.py b/shim/opentelemetry-opentracing-shim/tests/test_shim.py
index 796a4e0..1687b0d 100644
--- a/shim/opentelemetry-opentracing-shim/tests/test_shim.py
+++ b/shim/opentelemetry-opentracing-shim/tests/test_shim.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# TODO: make pylint use 3p opentracing module for type inference
# pylint:disable=no-member
diff --git a/shim/opentelemetry-opentracing-shim/tests/test_util.py b/shim/opentelemetry-opentracing-shim/tests/test_util.py
index c8f7571..f29d88c 100644
--- a/shim/opentelemetry-opentracing-shim/tests/test_util.py
+++ b/shim/opentelemetry-opentracing-shim/tests/test_util.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from time import time, time_ns
from unittest import TestCase
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/otel_ot_shim_tracer.py b/shim/opentelemetry-opentracing-shim/tests/testbed/otel_ot_shim_tracer.py
index 6c0a904..5e85f8a 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/otel_ot_shim_tracer.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/otel_ot_shim_tracer.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
import opentelemetry.shim.opentracing_shim as opentracingshim
from opentelemetry.sdk import trace
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_active_span_replacement/test_asyncio.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_active_span_replacement/test_asyncio.py
index f658d2b..9bdc666 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_active_span_replacement/test_asyncio.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_active_span_replacement/test_asyncio.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_active_span_replacement/test_threads.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_active_span_replacement/test_threads.py
index d088de5..aa7f46e 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_active_span_replacement/test_threads.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_active_span_replacement/test_threads.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from concurrent.futures import ( # pylint: disable=no-name-in-module
ThreadPoolExecutor,
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_client_server/test_asyncio.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_client_server/test_asyncio.py
index ddf7626..349c61d 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_client_server/test_asyncio.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_client_server/test_asyncio.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_client_server/test_threads.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_client_server/test_threads.py
index 6fa5974..8f6e795 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_client_server/test_threads.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_client_server/test_threads.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from queue import Queue
from threading import Thread
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/request_handler.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/request_handler.py
index b48a5db..23153ed 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/request_handler.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/request_handler.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentracing.ext import tags
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/test_asyncio.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/test_asyncio.py
index 33ee436..271ac3c 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/test_asyncio.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/test_asyncio.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/test_threads.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/test_threads.py
index 0b62b17..b729139 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/test_threads.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_common_request_handler/test_threads.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from concurrent.futures import ( # pylint: disable=no-name-in-module
ThreadPoolExecutor,
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_late_span_finish/test_asyncio.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_late_span_finish/test_asyncio.py
index 2b3ff16..52d3e1b 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_late_span_finish/test_asyncio.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_late_span_finish/test_asyncio.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_late_span_finish/test_threads.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_late_span_finish/test_threads.py
index 35edf04..31272ca 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_late_span_finish/test_threads.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_late_span_finish/test_threads.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import time
from concurrent.futures import ( # pylint: disable=no-name-in-module
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/response_listener.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/response_listener.py
index dd143c2..ca91b71 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/response_listener.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/response_listener.py
@@ -1,3 +1,7 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+
class ResponseListener:
def __init__(self, span):
self.span = span
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/test_asyncio.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/test_asyncio.py
index d279358..ef52624 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/test_asyncio.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/test_asyncio.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/test_threads.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/test_threads.py
index 498b7d9..d9cab6d 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/test_threads.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_listener_per_request/test_threads.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from concurrent.futures import ( # pylint: disable=no-name-in-module
ThreadPoolExecutor,
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_multiple_callbacks/test_asyncio.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_multiple_callbacks/test_asyncio.py
index 66f7958..22be53d 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_multiple_callbacks/test_asyncio.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_multiple_callbacks/test_asyncio.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
import random
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_multiple_callbacks/test_threads.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_multiple_callbacks/test_threads.py
index c4b9967..a348b45 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_multiple_callbacks/test_threads.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_multiple_callbacks/test_threads.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import random
import time
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_nested_callbacks/test_asyncio.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_nested_callbacks/test_asyncio.py
index 9b08a7d..d70d882 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_nested_callbacks/test_asyncio.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_nested_callbacks/test_asyncio.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_nested_callbacks/test_threads.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_nested_callbacks/test_threads.py
index 634641f..b117a3a 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_nested_callbacks/test_threads.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_nested_callbacks/test_threads.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from concurrent.futures import ( # pylint: disable=no-name-in-module
ThreadPoolExecutor,
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_asyncio.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_asyncio.py
index 9f3cf83..38bf77d 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_asyncio.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_asyncio.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_threads.py b/shim/opentelemetry-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_threads.py
index 8f719a2..c00692d 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_threads.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_threads.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from concurrent.futures import ( # pylint: disable=no-name-in-module
ThreadPoolExecutor,
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/testcase.py b/shim/opentelemetry-opentracing-shim/tests/testbed/testcase.py
index 3c16682..9ed63fa 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/testcase.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/testcase.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
import unittest
import opentelemetry.trace as trace_api
diff --git a/shim/opentelemetry-opentracing-shim/tests/testbed/utils.py b/shim/opentelemetry-opentracing-shim/tests/testbed/utils.py
index 88cc483..a55361f 100644
--- a/shim/opentelemetry-opentracing-shim/tests/testbed/utils.py
+++ b/shim/opentelemetry-opentracing-shim/tests/testbed/utils.py
@@ -1,3 +1,6 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
import logging
import threading
import time
diff --git a/tests/opentelemetry-docker-tests/tests/docker-compose.yml b/tests/opentelemetry-docker-tests/tests/docker-compose.yml
index 17c5388..914bab1 100644
--- a/tests/opentelemetry-docker-tests/tests/docker-compose.yml
+++ b/tests/opentelemetry-docker-tests/tests/docker-compose.yml
@@ -1,5 +1,3 @@
-version: '3'
-
services:
otopencensus:
image: rafaeljesus/opencensus-collector:latest
@@ -8,7 +6,7 @@ services:
- "8888:8888"
- "55678:55678"
otcollector:
- image: otel/opentelemetry-collector:0.31.0
+ image: otel/opentelemetry-collector:0.149.0
ports:
- "4317:4317"
- - "4318:55681"
+ - "4318:4318"
diff --git a/tests/opentelemetry-docker-tests/tests/opencensus/test_opencensusexporter_functional.py b/tests/opentelemetry-docker-tests/tests/opencensus/test_opencensusexporter_functional.py
index a3c1ee2..3e3a8ee 100644
--- a/tests/opentelemetry-docker-tests/tests/opencensus/test_opencensusexporter_functional.py
+++ b/tests/opentelemetry-docker-tests/tests/opencensus/test_opencensusexporter_functional.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry import trace
from opentelemetry.context import attach, detach, set_value
diff --git a/tests/opentelemetry-docker-tests/tests/otlpexporter/__init__.py b/tests/opentelemetry-docker-tests/tests/otlpexporter/__init__.py
index d4340fb..cd9a8fd 100644
--- a/tests/opentelemetry-docker-tests/tests/otlpexporter/__init__.py
+++ b/tests/opentelemetry-docker-tests/tests/otlpexporter/__init__.py
@@ -1,21 +1,32 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
+import time
from abc import ABC, abstractmethod
-from opentelemetry.context import attach, detach, set_value
+from opentelemetry.context import (
+ _SUPPRESS_INSTRUMENTATION_KEY,
+ attach,
+ detach,
+ set_value,
+)
+from opentelemetry.sdk.metrics._internal.export import (
+ MetricExportResult,
+ PeriodicExportingMetricReader,
+)
+from opentelemetry.sdk.metrics._internal.point import (
+ Metric,
+ NumberDataPoint,
+ Sum,
+)
+from opentelemetry.sdk.metrics.export import (
+ MetricsData,
+ ResourceMetrics,
+ ScopeMetrics,
+)
+from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.sdk.util.instrumentation import InstrumentationScope
class ExportStatusSpanProcessor(SimpleSpanProcessor):
@@ -29,13 +40,37 @@ class ExportStatusSpanProcessor(SimpleSpanProcessor):
detach(token)
+class ExportStatusMetricReader(PeriodicExportingMetricReader):
+ def __init__(self, exporter, **kwargs):
+ # Very short export interval for testing
+ super().__init__(exporter, export_interval_millis=1, **kwargs)
+ self.export_status = []
+
+ def _receive_metrics(self, metrics_data, timeout_millis=10_000, **kwargs):
+ token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True))
+ try:
+ export_result = self._exporter.export(
+ metrics_data, timeout_millis=timeout_millis
+ )
+ self.export_status.append(export_result)
+ except Exception:
+ self.export_status.append(MetricExportResult.FAILURE)
+ finally:
+ detach(token)
+
+
class BaseTestOTLPExporter(ABC):
@abstractmethod
def get_span_processor(self):
pass
+ @abstractmethod
+ def get_metric_reader(self):
+ pass
+
# pylint: disable=no-member
def test_export(self):
+ """Test span export"""
with self.tracer.start_as_current_span("foo"):
with self.tracer.start_as_current_span("bar"):
with self.tracer.start_as_current_span("baz"):
@@ -46,3 +81,86 @@ class BaseTestOTLPExporter(ABC):
for export_status in self.span_processor.export_status:
self.assertEqual(export_status.name, "SUCCESS")
self.assertEqual(export_status.value, 0)
+
+ def test_metrics_export(self):
+ """Test metrics export from full metrics SDK pipeline"""
+ counter = self.meter.create_counter("test_counter")
+ histogram = self.meter.create_histogram("test_histogram")
+ up_down_counter = self.meter.create_up_down_counter(
+ "test_up_down_counter"
+ )
+
+ counter.add(1, {"key1": "value1"})
+ counter.add(2, {"key2": "value2"})
+ histogram.record(1.5, {"key3": "value3"})
+ histogram.record(2.5, {"key4": "value4"})
+ up_down_counter.add(3, {"key5": "value5"})
+ up_down_counter.add(-1, {"key6": "value6"})
+ self.metric_reader.force_flush(timeout_millis=5000)
+ time.sleep(0.1)
+
+ # Verify at least one export happened
+ self.assertTrue(len(self.metric_reader.export_status) >= 1)
+ # Verify all exports succeeded
+ for export_status in self.metric_reader.export_status:
+ self.assertEqual(export_status.name, "SUCCESS")
+ self.assertEqual(export_status.value, 0)
+
+ @abstractmethod
+ def test_metrics_export_batch_size_two(self):
+ """Test metrics max_export_batch_size=2 directly through exporter"""
+
+ def _create_test_metrics_data(self, num_data_points=6):
+ """Create test metrics data with specified number of data points."""
+ data_points = [
+ NumberDataPoint(
+ attributes={"key": f"value{i}"},
+ start_time_unix_nano=1000000 + i,
+ time_unix_nano=2000000 + i,
+ value=i + 1.0,
+ )
+ for i in range(num_data_points)
+ ]
+ metric = Metric(
+ name="otel_test_counter_foobar",
+ description="Test counter metric for batch verification",
+ unit="1",
+ data=Sum(
+ data_points=data_points,
+ aggregation_temporality=1, # CUMULATIVE
+ is_monotonic=True,
+ ),
+ )
+ scope_metrics = ScopeMetrics(
+ scope=InstrumentationScope(name="test_scope"),
+ metrics=[metric],
+ schema_url=None,
+ )
+ resource_metrics = ResourceMetrics(
+ resource=Resource.create({"service.name": "test-service"}),
+ scope_metrics=[scope_metrics],
+ schema_url=None,
+ )
+
+ return MetricsData(resource_metrics=[resource_metrics]), data_points
+
+ def _verify_batch_export_result(
+ self, result, data_points, batch_counter, max_batch_size=2
+ ):
+ """Verify export result and batch count for export batching tests."""
+ self.assertEqual(
+ result.name, "SUCCESS", f"Expected SUCCESS, got: {result}"
+ )
+ self.assertEqual(
+ result.value, 0, f"Expected result code 0, got: {result.value}"
+ )
+
+ expected_batches = (
+ len(data_points) + max_batch_size - 1
+ ) // max_batch_size
+ self.assertEqual(
+ batch_counter.export_call_count,
+ expected_batches,
+ f"Expected {expected_batches} export calls with max_export_batch_size={max_batch_size} and {len(data_points)} data points, "
+ f"but got {batch_counter.export_call_count} calls",
+ )
diff --git a/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_grpc_exporter_functional.py b/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_grpc_exporter_functional.py
index d48b305..4d6ba4e 100644
--- a/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_grpc_exporter_functional.py
+++ b/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_grpc_exporter_functional.py
@@ -1,25 +1,38 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 opentelemetry import trace
+# SPDX-License-Identifier: Apache-2.0
+
+from opentelemetry import metrics, trace
+from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
+ OTLPMetricExporter,
+)
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
+from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.test.globals_test import (
+ reset_metrics_globals,
+ reset_trace_globals,
+)
from opentelemetry.test.test_base import TestBase
-from . import BaseTestOTLPExporter, ExportStatusSpanProcessor
+from . import (
+ BaseTestOTLPExporter,
+ ExportStatusMetricReader,
+ ExportStatusSpanProcessor,
+)
+
+
+class BatchCountingGRPCExporter(OTLPMetricExporter):
+ """gRPC exporter that counts actual batch export calls for testing."""
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.export_call_count = 0
+
+ def _export(self, *args, **kwargs):
+ self.export_call_count += 1
+ return super()._export(*args, **kwargs)
class TestOTLPGRPCExporter(BaseTestOTLPExporter, TestBase):
@@ -29,11 +42,37 @@ class TestOTLPGRPCExporter(BaseTestOTLPExporter, TestBase):
OTLPSpanExporter(insecure=True, timeout=1)
)
+ def get_metric_reader(self):
+ return ExportStatusMetricReader(
+ OTLPMetricExporter(
+ insecure=True, timeout=1, max_export_batch_size=2
+ )
+ )
+
def setUp(self):
super().setUp()
+ reset_trace_globals()
trace.set_tracer_provider(TracerProvider())
self.tracer = trace.get_tracer(__name__)
self.span_processor = self.get_span_processor()
-
trace.get_tracer_provider().add_span_processor(self.span_processor)
+
+ reset_metrics_globals()
+ self.metric_reader = self.get_metric_reader()
+ meter_provider = MeterProvider(metric_readers=[self.metric_reader])
+ metrics.set_meter_provider(meter_provider)
+ self.meter = metrics.get_meter(__name__)
+
+ def test_metrics_export_batch_size_two(self):
+ """Test metrics max_export_batch_size=2 directly through gRPC exporter"""
+ batch_counter = BatchCountingGRPCExporter(
+ endpoint="localhost:4317", insecure=True, max_export_batch_size=2
+ )
+ metrics_data, data_points = self._create_test_metrics_data(
+ num_data_points=6
+ )
+ result = batch_counter.export(metrics_data)
+ self._verify_batch_export_result(
+ result, data_points, batch_counter, max_batch_size=2
+ )
diff --git a/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_http_exporter_functional.py b/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_http_exporter_functional.py
index 59a333d..95aa772 100644
--- a/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_http_exporter_functional.py
+++ b/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_http_exporter_functional.py
@@ -1,25 +1,38 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 opentelemetry import trace
+# SPDX-License-Identifier: Apache-2.0
+
+from opentelemetry import metrics, trace
+from opentelemetry.exporter.otlp.proto.http.metric_exporter import (
+ OTLPMetricExporter,
+)
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
OTLPSpanExporter,
)
+from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.test.globals_test import (
+ reset_metrics_globals,
+ reset_trace_globals,
+)
from opentelemetry.test.test_base import TestBase
-from . import BaseTestOTLPExporter, ExportStatusSpanProcessor
+from . import (
+ BaseTestOTLPExporter,
+ ExportStatusMetricReader,
+ ExportStatusSpanProcessor,
+)
+
+
+class BatchCountingHTTPExporter(OTLPMetricExporter):
+ """HTTP exporter that counts actual batch export calls for testing."""
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.export_call_count = 0
+
+ def _export(self, *args, **kwargs):
+ self.export_call_count += 1
+ return super()._export(*args, **kwargs)
class TestOTLPHTTPExporter(BaseTestOTLPExporter, TestBase):
@@ -27,11 +40,36 @@ class TestOTLPHTTPExporter(BaseTestOTLPExporter, TestBase):
def get_span_processor(self):
return ExportStatusSpanProcessor(OTLPSpanExporter())
+ def get_metric_reader(self):
+ return ExportStatusMetricReader(
+ OTLPMetricExporter(max_export_batch_size=2)
+ )
+
def setUp(self):
super().setUp()
+ reset_trace_globals()
trace.set_tracer_provider(TracerProvider())
self.tracer = trace.get_tracer(__name__)
self.span_processor = self.get_span_processor()
-
trace.get_tracer_provider().add_span_processor(self.span_processor)
+
+ reset_metrics_globals()
+ self.metric_reader = self.get_metric_reader()
+ meter_provider = MeterProvider(metric_readers=[self.metric_reader])
+ metrics.set_meter_provider(meter_provider)
+ self.meter = metrics.get_meter(__name__)
+
+ def test_metrics_export_batch_size_two(self):
+ """Test metrics max_export_batch_size=2 directly through HTTP exporter"""
+ batch_counter = BatchCountingHTTPExporter(
+ endpoint="http://localhost:4318/v1/metrics",
+ max_export_batch_size=2,
+ )
+ metrics_data, data_points = self._create_test_metrics_data(
+ num_data_points=6
+ )
+ result = batch_counter.export(metrics_data)
+ self._verify_batch_export_result(
+ result, data_points, batch_counter, max_batch_size=2
+ )
diff --git a/tests/opentelemetry-test-utils/pyproject.toml b/tests/opentelemetry-test-utils/pyproject.toml
index 924c708..b3915ba 100644
--- a/tests/opentelemetry-test-utils/pyproject.toml
+++ b/tests/opentelemetry-test-utils/pyproject.toml
@@ -8,7 +8,7 @@ dynamic = ["version"]
description = "Test utilities for OpenTelemetry unit tests"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
]
@@ -18,7 +18,6 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -27,8 +26,9 @@ classifiers = [
]
dependencies = [
"asgiref ~= 3.0",
- "opentelemetry-api == 1.41.0",
- "opentelemetry-sdk == 1.41.0",
+ "opentelemetry-api == 1.42.1",
+ "opentelemetry-sdk == 1.42.1",
+ "requests ~= 2.28",
]
[project.urls]
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/__init__.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/__init__.py
index 8418886..b3de9df 100644
--- a/tests/opentelemetry-test-utils/src/opentelemetry/test/__init__.py
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/__init__.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
# type: ignore
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/asgitestutil.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/asgitestutil.py
index ab0215d..6b680ac 100644
--- a/tests/opentelemetry-test-utils/src/opentelemetry/test/asgitestutil.py
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/asgitestutil.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import asyncio
from unittest import IsolatedAsyncioTestCase
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/concurrency_test.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/concurrency_test.py
index 5d178e2..25f6f35 100644
--- a/tests/opentelemetry-test-utils/src/opentelemetry/test/concurrency_test.py
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/concurrency_test.py
@@ -1,22 +1,12 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import sys
import threading
import unittest
+from collections.abc import Callable
from functools import partial
-from typing import Callable, List, Optional, TypeVar
+from typing import TypeVar
from unittest.mock import Mock
ReturnT = TypeVar("ReturnT")
@@ -66,11 +56,11 @@ class ConcurrencyTestBase(unittest.TestCase):
def run_with_many_threads(
func_to_test: Callable[[], ReturnT],
num_threads: int = 100,
- ) -> List[ReturnT]:
+ ) -> list[ReturnT]:
"""Util to run ``func_to_test`` in ``num_threads`` concurrently"""
barrier = threading.Barrier(num_threads)
- results: List[Optional[ReturnT]] = [None] * num_threads
+ results: list[ReturnT | None] = [None] * num_threads
def thread_start(idx: int) -> None:
nonlocal results
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/globals_test.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/globals_test.py
index aead836..e1cf713 100644
--- a/tests/opentelemetry-test-utils/src/opentelemetry/test/globals_test.py
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/globals_test.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import unittest
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/httptest.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/httptest.py
index 84591ca..5f42a36 100644
--- a/tests/opentelemetry-test-utils/src/opentelemetry/test/httptest.py
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/httptest.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import re
import unittest
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/metrictestutil.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/metrictestutil.py
index 33f1039..c2de0b7 100644
--- a/tests/opentelemetry-test-utils/src/opentelemetry/test/metrictestutil.py
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/metrictestutil.py
@@ -1,20 +1,7 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
-from typing import Optional
-
from opentelemetry.attributes import BoundedAttributes
from opentelemetry.sdk.metrics.export import (
AggregationTemporality,
@@ -108,8 +95,8 @@ def _generate_unsupported_metric(
def _generate_histogram(
name: str,
attributes: Attributes = None,
- description: Optional[str] = None,
- unit: Optional[str] = None,
+ description: str | None = None,
+ unit: str | None = None,
) -> Metric:
if attributes is None:
attributes = BoundedAttributes(attributes={"a": 1, "b": True})
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/mock_test_classes.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/mock_test_classes.py
index 5e7fc52..680c223 100644
--- a/tests/opentelemetry-test-utils/src/opentelemetry/test/mock_test_classes.py
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/mock_test_classes.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
class IterEntryPoint:
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/mock_textmap.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/mock_textmap.py
index c3e901e..09378aa 100644
--- a/tests/opentelemetry-test-utils/src/opentelemetry/test/mock_textmap.py
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/mock_textmap.py
@@ -1,18 +1,6 @@
# Copyright The OpenTelemetry Authors
-#
-# 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 typing
+# SPDX-License-Identifier: Apache-2.0
+
from opentelemetry import trace
from opentelemetry.context import Context
@@ -36,7 +24,7 @@ class NOOPTextMapPropagator(TextMapPropagator):
def extract(
self,
carrier: CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
getter: Getter = default_getter,
) -> Context:
return Context()
@@ -44,7 +32,7 @@ class NOOPTextMapPropagator(TextMapPropagator):
def inject(
self,
carrier: CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
setter: Setter = default_setter,
) -> None:
return None
@@ -63,7 +51,7 @@ class MockTextMapPropagator(TextMapPropagator):
def extract(
self,
carrier: CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
getter: Getter = default_getter,
) -> Context:
if context is None:
@@ -88,7 +76,7 @@ class MockTextMapPropagator(TextMapPropagator):
def inject(
self,
carrier: CarrierT,
- context: typing.Optional[Context] = None,
+ context: Context | None = None,
setter: Setter = default_setter,
) -> None:
span = trace.get_current_span(context)
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/spantestutil.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/spantestutil.py
index 912de9e..24dace2 100644
--- a/tests/opentelemetry-test-utils/src/opentelemetry/test/spantestutil.py
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/spantestutil.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from functools import partial
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py
index 0ec7d59..3cd1b93 100644
--- a/tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py
@@ -1,21 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import logging
import unittest
+from collections.abc import Sequence
from contextlib import contextmanager
-from typing import Optional, Sequence, Tuple
from opentelemetry import metrics as metrics_api
from opentelemetry import trace as trace_api
@@ -118,7 +107,7 @@ class TestBase(unittest.TestCase):
return tracer_provider, memory_exporter
@staticmethod
- def create_meter_provider(**kwargs) -> Tuple[MeterProvider, MetricReader]:
+ def create_meter_provider(**kwargs) -> tuple[MeterProvider, MetricReader]:
"""Helper to create a configured meter provider
Creates a `MeterProvider` and an `InMemoryMetricReader`.
Returns:
@@ -142,7 +131,7 @@ class TestBase(unittest.TestCase):
finally:
logging.disable(logging.NOTSET)
- def get_sorted_metrics(self, scope: Optional[str] = None):
+ def get_sorted_metrics(self, scope: str | None = None):
"""Returns recorded metrics sorted by name.
Args:
@@ -177,7 +166,7 @@ class TestBase(unittest.TestCase):
self,
metric: Metric,
expected_data_points: Sequence[DataPointT],
- est_value_delta: Optional[float] = 0,
+ est_value_delta: float | None = 0,
):
self.assertEqual(
len(expected_data_points), len(metric.data.data_points)
@@ -192,7 +181,7 @@ class TestBase(unittest.TestCase):
def is_data_points_equal(
expected_data_point: DataPointT,
data_point: DataPointT,
- est_value_delta: Optional[float] = 0,
+ est_value_delta: float | None = 0,
):
if type(expected_data_point) != type( # noqa: E721
data_point
@@ -230,7 +219,7 @@ class TestBase(unittest.TestCase):
self,
expected_data_point: DataPointT,
data_points: Sequence[DataPointT],
- est_value_delta: Optional[float] = 0,
+ est_value_delta: float | None = 0,
):
is_data_point_exist = False
for data_point in data_points:
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/version/__init__.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/version/__init__.py
index b4407ca..23888c2 100644
--- a/tests/opentelemetry-test-utils/src/opentelemetry/test/version/__init__.py
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/version/__init__.py
@@ -1 +1,4 @@
-__version__ = "0.62b0"
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+__version__ = "0.63b1"
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/weaver_live_check.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/weaver_live_check.py
new file mode 100644
index 0000000..115fa1c
--- /dev/null
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/weaver_live_check.py
@@ -0,0 +1,496 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+import functools
+import json
+import logging
+import os
+import shutil
+import socket
+import subprocess
+import tempfile
+from collections import defaultdict
+from collections.abc import Sequence
+from itertools import chain
+from typing import Any
+
+from requests import Session, post
+from requests.adapters import HTTPAdapter
+from urllib3.util.retry import Retry
+
+from opentelemetry.semconv.schemas import Schemas
+
+logger = logging.getLogger(__name__)
+
+
+def _find_free_port() -> int:
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
+ sock.bind(("", 0))
+ return sock.getsockname()[1]
+
+
+def _extract_violations(report: dict) -> list:
+ """Extract and deduplicate violations from the full report.
+
+ Get all violations using python version of this jq filter:
+ [ .. | objects | select(has("live_check_result"))
+ | .live_check_result.all_advice[]?
+ | select(.level == "violation") ]
+ | group_by(.id, .message, .context, .signal_name, .signal_type)
+ | map({ ..., count: length })
+ | sort_by(-.count, .message)
+ """
+ raw: list[dict] = []
+
+ def _collect(obj: Any) -> list[dict]:
+ if isinstance(obj, dict):
+ result: list[dict] = []
+ lcr = obj.get("live_check_result")
+ if isinstance(lcr, dict):
+ advices = lcr.get("all_advice")
+ if isinstance(advices, list):
+ result.extend(
+ a for a in advices if a.get("level") == "violation"
+ )
+ for value in obj.values():
+ result.extend(_collect(value))
+ return result
+ if isinstance(obj, list):
+ return list(chain.from_iterable(_collect(item) for item in obj))
+ return []
+
+ raw = _collect(report)
+
+ groups: dict[tuple, list] = defaultdict(list)
+ for violation in raw:
+ ctx = violation.get("context")
+ key = (
+ violation.get("id"),
+ violation.get("message"),
+ json.dumps(ctx, sort_keys=True)
+ if isinstance(ctx, (dict, list))
+ else ctx,
+ violation.get("signal_name"),
+ violation.get("signal_type"),
+ )
+ groups[key].append(violation)
+
+ violations = [
+ {
+ "id": k[0],
+ "message": k[1],
+ "context": vs[0].get(
+ "context"
+ ), # preserve original dict, not JSON string
+ "signal_name": k[3],
+ "signal_type": k[4],
+ "count": len(vs),
+ }
+ for k, vs in groups.items()
+ ]
+ violations.sort(key=lambda v: (-v["count"], v.get("message") or ""))
+ return violations
+
+
+def _format_violations(violations: list) -> str:
+ """Format violations list as human-readable text."""
+ lines = []
+ for violation in violations:
+ signal = ""
+ signal_type = violation.get("signal_type")
+ signal_name = violation.get("signal_name")
+ if signal_type and signal_name:
+ signal = f" on {signal_type} '{signal_name}'"
+ elif signal_type:
+ signal = f" on {signal_type}"
+ elif signal_name:
+ signal = f" on '{signal_name}'"
+ lines.append(
+ f"- [{violation.get('id')}] {violation.get('message')} ({violation['count']} occurrence(s){signal})"
+ )
+ return "\n".join(lines)
+
+
+class LiveCheckError(AssertionError):
+ """Raised by :meth:`WeaverLiveCheck.end_and_check` when semconv violations are found.
+
+ The full :class:`LiveCheckReport` is attached as :attr:`report` for
+ structured inspection beyond the human-readable message::
+
+ with pytest.raises(LiveCheckError) as exc_info:
+ weaver.end_and_check()
+
+ err = exc_info.value
+ assert any(
+ v["id"] == "my_policy_check"
+ and v["context"]["attribute_name"] == "my.attribute"
+ for v in err.report.violations
+ )
+ """
+
+ def __init__(self, message: str, report: "LiveCheckReport") -> None:
+ super().__init__(message)
+ self.report = report
+
+
+class LiveCheckReport:
+ """The result of a weaver live-check run.
+
+ Provides structured access to violations and the full raw JSON report.
+
+ See https://github.com/open-telemetry/weaver/tree/main/crates/weaver_live_check#output
+ for the full report structure.
+
+ Example — asserting on metrics statistics::
+
+ report = weaver.end()
+ seen = report["statistics"]["seen_registry_metrics"]
+ assert seen.get("http.server.request.duration") == 1
+
+ Example — asserting on violations::
+
+ report = weaver.end()
+ assert any(
+ v["id"] == "my_policy_check"
+ and v["context"]["attribute_name"] == "my.attribute"
+ for v in report.violations
+ )
+ """
+
+ def __init__(self, report: dict[str, Any]) -> None:
+ self._report = report
+
+ @functools.cached_property
+ def violations(self) -> list[dict[str, Any]]:
+ """Deduplicated list of semconv violations found in the report.
+
+ Each item is a dict with keys: ``id``, ``message``, ``context``
+ (the raw context dict from weaver, e.g. ``{"attribute_name": "foo"}``),
+ ``signal_name``, ``signal_type``, ``count``.
+ """
+ return _extract_violations(self._report)
+
+ def __getitem__(self, key: str) -> Any:
+ return self._report[key]
+
+ def get(self, key: str, default: Any = None) -> Any:
+ return self._report.get(key, default)
+
+ def __contains__(self, key: object) -> bool:
+ return key in self._report
+
+ def __repr__(self) -> str:
+ num_violations = len(self.violations)
+ return f"LiveCheckReport({num_violations} violation{'s' if num_violations != 1 else ''})"
+
+
+# NOTE: WeaverLiveCheck is experimental and its API is subject to change.
+class WeaverLiveCheck:
+ """Runs ``weaver registry live-check`` as a subprocess and validates
+ OTLP telemetry against OpenTelemetry semantic conventions.
+
+ .. note::
+ This class is experimental and its API is subject to change without notice.
+
+
+ Requires the ``weaver`` binary on PATH:
+ https://github.com/open-telemetry/weaver/releases
+
+ Typical use as a context manager::
+
+ def test_my_telemetry(self):
+ with WeaverLiveCheck() as weaver:
+ exporter = OTLPSpanExporter(
+ endpoint=weaver.otlp_endpoint, insecure=True
+ )
+ # ... configure provider, emit telemetry ...
+ provider.force_flush()
+
+ # Signals weaver to stop, raises LiveCheckError listing violations
+ # if any, or returns a LiveCheckReport on success.
+ report = weaver.end_and_check()
+ # __exit__ calls close(), which is idempotent if end_and_check() was already called
+
+ Use :meth:`end` when you need the full :class:`LiveCheckReport`
+ regardless of whether violations were found — for example, to assert that
+ specific metrics were observed or to inspect violation fields directly::
+
+ with WeaverLiveCheck() as weaver:
+ # ... configure provider, emit telemetry ...
+ provider.force_flush()
+ report = weaver.end()
+
+ seen_metrics = report["statistics"]["seen_registry_metrics"]
+ assert seen_metrics.get("http.server.request.duration") == 1
+
+ Lifecycle:
+ - :meth:`start` — launches weaver and waits for it to become ready.
+ - :attr:`otlp_endpoint` — gRPC OTLP endpoint to point exporters at.
+ - :meth:`end` — signals weaver to stop and always returns a
+ :class:`LiveCheckReport`. Never raises for semconv violations; use
+ this when you want to write your own assertions.
+ - :meth:`end_and_check` — signals weaver to stop and raises
+ :class:`LiveCheckError` with a human-readable violation list and the
+ full report attached if weaver exits non-zero. Returns a
+ :class:`LiveCheckReport` on success.
+ - :meth:`close` — stops weaver if not already stopped and terminates the
+ process. Never raises for semconv violations. Idempotent; safe to
+ call even if :meth:`end_and_check` or :meth:`end` was already called.
+ """
+
+ def __init__(
+ self,
+ registry: str | None = None,
+ schema_version: str | None = None,
+ policies_dir: str | None = None,
+ inactivity_timeout: int = 30,
+ otlp_port: int = 0,
+ admin_port: int = 0,
+ extra_args: Sequence[str] | None = None,
+ ):
+ """Build the ``weaver registry live-check`` command.
+
+ ``extra_args`` is appended verbatim to the weaver command after the
+ managed flags (``--registry``, ``--otlp-grpc-port``, etc.) and lets
+ callers pass additional weaver options — for example ``["--quiet"]``
+ or ``["--skip-policies"]`` — without subclassing.
+ """
+ weaver_bin = shutil.which("weaver")
+ if not weaver_bin:
+ raise RuntimeError(
+ "weaver binary not found on PATH. "
+ "Install it from https://github.com/open-telemetry/weaver/releases"
+ )
+
+ self._otlp_port = otlp_port or _find_free_port()
+ self._admin_port = admin_port or _find_free_port()
+ self._ready = False
+ self._stopped = False
+ self._process: subprocess.Popen[bytes] | None = None
+ self._stdout_path: str | None = None
+ self._stderr_path: str | None = None
+
+ command = [
+ weaver_bin,
+ "registry",
+ "live-check",
+ f"--inactivity-timeout={inactivity_timeout}",
+ f"--otlp-grpc-port={self._otlp_port}",
+ f"--admin-port={self._admin_port}",
+ "--output=http",
+ "--format=json",
+ ]
+
+ if policies_dir:
+ command += ["--advice-policies", os.path.abspath(policies_dir)]
+
+ if registry is None:
+ if schema_version is None:
+ schema_version = list(Schemas)[-1].value.rsplit("/", 1)[-1]
+ registry = f"https://github.com/open-telemetry/semantic-conventions/archive/refs/tags/v{schema_version}.tar.gz[model]"
+ elif os.path.isdir(registry):
+ registry = os.path.abspath(registry)
+
+ command += ["--registry", registry]
+
+ if extra_args:
+ command.extend(extra_args)
+
+ self._command = command
+ logger.debug("Weaver command: %s", command)
+
+ def __enter__(self) -> "WeaverLiveCheck":
+ return self.start()
+
+ def __exit__(self, exc_type: Any, *_: Any) -> None:
+ if exc_type is not None:
+ self._stopped = True
+ self.close()
+
+ def start(self) -> "WeaverLiveCheck":
+ logger.debug("Starting WeaverLiveCheck process...")
+ # Redirect weaver's stdout/stderr to tempfiles
+ stdout_fd, self._stdout_path = tempfile.mkstemp(
+ prefix="weaver-stdout-", suffix=".log"
+ )
+ stderr_fd, self._stderr_path = tempfile.mkstemp(
+ prefix="weaver-stderr-", suffix=".log"
+ )
+ try:
+ self._process = subprocess.Popen( # pylint: disable=consider-using-with
+ self._command,
+ stdout=stdout_fd,
+ stderr=stderr_fd,
+ )
+ finally:
+ os.close(stdout_fd)
+ os.close(stderr_fd)
+ try:
+ self._wait_for_ready()
+ self._ready = True
+ except Exception as exc: # pylint: disable=broad-except
+ logs = self._read_weaver_logs()
+ logger.error(
+ "WeaverLiveCheck did not start: %s, logs: %s", exc, logs
+ )
+ raise
+ return self
+
+ def _wait_for_ready(self) -> None:
+ retry = Retry(
+ total=10,
+ backoff_factor=1,
+ backoff_max=1,
+ # Any non-2xx response from /health means weaver isn't ready yet.
+ status_forcelist=list(range(300, 600)),
+ raise_on_status=True,
+ allowed_methods=["GET"],
+ )
+ session = Session()
+ session.mount("http://", HTTPAdapter(max_retries=retry))
+ try:
+ session.get(
+ f"http://localhost:{self._admin_port}/health", timeout=5
+ )
+ except Exception as exc: # pylint: disable=broad-except
+ if self._process is not None and self._process.poll() is not None:
+ raise RuntimeError(
+ f"WeaverLiveCheck process exited unexpectedly (code {self._process.returncode})"
+ ) from exc
+ raise TimeoutError(
+ "WeaverLiveCheck did not become ready in time"
+ ) from exc
+
+ @property
+ def otlp_endpoint(self) -> str:
+ return f"http://localhost:{self._otlp_port}"
+
+ def _do_stop(self, timeout: int) -> tuple["LiveCheckReport", int]:
+ """POST /stop, wait for the process to exit, return (report, exit_code).
+
+ Raises for infrastructure errors (HTTP failure, process communication).
+ Never raises for semconv violations.
+ """
+ if not self._ready:
+ raise RuntimeError(
+ "WeaverLiveCheck process did not start successfully"
+ )
+ try:
+ response = post(
+ f"http://localhost:{self._admin_port}/stop", timeout=5
+ )
+ response.raise_for_status()
+ report = LiveCheckReport(response.json())
+ assert self._process is not None
+ exit_code = self._process.wait(timeout=timeout)
+ except Exception as exc: # pylint: disable=broad-except
+ logs = self._read_weaver_logs()
+ logger.error(
+ "Error communicating with weaver: %s, logs: %s", exc, logs
+ )
+ raise
+ return report, exit_code
+
+ def end(self, timeout: int = 30) -> "LiveCheckReport":
+ """Signal weaver to stop and return the full :class:`LiveCheckReport`.
+
+ Never raises for semconv violations — use this when you want to write
+ your own assertions against :attr:`LiveCheckReport.violations` or the
+ raw report data.
+
+ Raises :exc:`RuntimeError` for infrastructure problems (weaver failed
+ to start, HTTP communication error, etc.).
+
+ See https://github.com/open-telemetry/weaver/tree/main/crates/weaver_live_check#output
+ for the report structure.
+ """
+ if self._stopped:
+ logger.warning(
+ "end() called after weaver already stopped; returning empty report"
+ )
+ return LiveCheckReport({})
+ self._stopped = True
+ report, _ = self._do_stop(timeout)
+ return report
+
+ def end_and_check(self, timeout: int = 30) -> "LiveCheckReport":
+ """Signal weaver to stop and assert no semconv violations were found.
+
+ Returns the :class:`LiveCheckReport` when weaver exits successfully
+ (exit code 0).
+
+ Does **not** return if weaver exits with a non-zero status — raises
+ :exc:`LiveCheckError` (a subclass of :exc:`AssertionError`) with a
+ human-readable list of violations and the full :class:`LiveCheckReport`
+ attached as :attr:`LiveCheckError.report`.
+ Use :meth:`end` if you need the report regardless of violations.
+
+ Raises :exc:`RuntimeError` for infrastructure problems (weaver failed
+ to start, HTTP communication error, etc.).
+ """
+ if self._stopped:
+ logger.warning(
+ "end_and_check() called after weaver already stopped; returning empty report"
+ )
+ return LiveCheckReport({})
+ self._stopped = True
+ report, exit_code = self._do_stop(timeout)
+ if exit_code == 0:
+ # Success — no violations found, no errors communicating with weaver
+ return report
+ raise LiveCheckError(
+ f"Semconv violations found:\n{_format_violations(report.violations)}",
+ report,
+ )
+
+ def _read_weaver_logs(self) -> str | None:
+ if self._process is None:
+ return None
+ try:
+ if self._process.poll() is None:
+ self._process.kill()
+ try:
+ self._process.wait(timeout=5)
+ except subprocess.TimeoutExpired:
+ pass
+
+ def _read(path: str | None) -> str:
+ if path is None or not os.path.exists(path):
+ return ""
+ with open(path, "rb") as fp:
+ return fp.read().decode(errors="replace")
+
+ return f"{_read(self._stdout_path)}\n{_read(self._stderr_path)}"
+ except Exception as exc: # pylint: disable=broad-except
+ logger.error("Could not get weaver logs: %s", exc)
+ return None
+
+ def close(self) -> None:
+ """Stop weaver and clean up the process.
+
+ If weaver has not been stopped yet, sends the ``/stop`` signal and
+ waits for the process to exit. Never raises for semconv violations.
+ Idempotent — safe to call multiple times or after :meth:`end` /
+ :meth:`end_and_check` has already been called.
+ """
+ if not self._stopped:
+ self._stopped = True
+ if self._ready:
+ try:
+ self._do_stop(timeout=30)
+ except Exception as exc: # pylint: disable=broad-except
+ logger.debug("Error stopping weaver during close: %s", exc)
+ if self._process and self._process.poll() is None:
+ self._process.terminate()
+ try:
+ self._process.wait(timeout=5)
+ except subprocess.TimeoutExpired:
+ self._process.kill()
+ for path in (self._stdout_path, self._stderr_path):
+ if path is not None:
+ try:
+ os.unlink(path)
+ except OSError:
+ pass
+ self._stdout_path = None
+ self._stderr_path = None
diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/wsgitestutil.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/wsgitestutil.py
index 908b1d4..b12482b 100644
--- a/tests/opentelemetry-test-utils/src/opentelemetry/test/wsgitestutil.py
+++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/wsgitestutil.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
import io
import wsgiref.util as wsgiref_util
@@ -48,7 +37,8 @@ class WsgiTestBase(TestBase):
trace_id = trace.format_trace_id(span.get_span_context().trace_id)
span_id = trace.format_span_id(span.get_span_context().span_id)
+ trace_flags = span.get_span_context().trace_flags
self.assertEqual(
- f"00-{trace_id}-{span_id}-01",
+ f"00-{trace_id}-{span_id}-{trace_flags:02x}",
headers["traceresponse"],
)
diff --git a/tests/opentelemetry-test-utils/test-requirements.txt b/tests/opentelemetry-test-utils/test-requirements.txt
index 854e732..7cb9414 100644
--- a/tests/opentelemetry-test-utils/test-requirements.txt
+++ b/tests/opentelemetry-test-utils/test-requirements.txt
@@ -1,15 +1,18 @@
asgiref==3.7.2
-importlib-metadata==6.11.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.6.0
py-cpuinfo==9.0.0
pytest==7.4.4
tomli==2.0.1
-typing_extensions==4.10.0
+typing_extensions==4.12.0
wrapt==1.16.0
-zipp==3.19.2
-e opentelemetry-api
-e opentelemetry-sdk
-e opentelemetry-semantic-conventions
-e tests/opentelemetry-test-utils
+# these are required for weaver integration tests, we're running that only on linux / CPython
+# because of lack of support for gRPC wheels on some platforms
+./opentelemetry-proto ; platform_python_implementation != 'PyPy'
+./exporter/opentelemetry-exporter-otlp-proto-common ; platform_python_implementation != 'PyPy'
+./exporter/opentelemetry-exporter-otlp-proto-grpc ; platform_python_implementation != 'PyPy'
diff --git a/tests/opentelemetry-test-utils/tests/__init__.py b/tests/opentelemetry-test-utils/tests/__init__.py
index b0a6f42..e57cf4a 100644
--- a/tests/opentelemetry-test-utils/tests/__init__.py
+++ b/tests/opentelemetry-test-utils/tests/__init__.py
@@ -1,13 +1,2 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
diff --git a/tests/opentelemetry-test-utils/tests/test_base.py b/tests/opentelemetry-test-utils/tests/test_base.py
index 92b83b9..87dbcc8 100644
--- a/tests/opentelemetry-test-utils/tests/test_base.py
+++ b/tests/opentelemetry-test-utils/tests/test_base.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.test.test_base import TestBase
diff --git a/tests/opentelemetry-test-utils/tests/test_utils.py b/tests/opentelemetry-test-utils/tests/test_utils.py
index dbf0688..bcae388 100644
--- a/tests/opentelemetry-test-utils/tests/test_utils.py
+++ b/tests/opentelemetry-test-utils/tests/test_utils.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
from opentelemetry.test import TestCase
diff --git a/tests/opentelemetry-test-utils/tests/test_weaver_live_check.py b/tests/opentelemetry-test-utils/tests/test_weaver_live_check.py
new file mode 100644
index 0000000..8275d28
--- /dev/null
+++ b/tests/opentelemetry-test-utils/tests/test_weaver_live_check.py
@@ -0,0 +1,192 @@
+# Copyright The OpenTelemetry Authors
+# SPDX-License-Identifier: Apache-2.0
+
+"""Live-check tests using Weaver to validate SDK telemetry against semconv.
+
+Requires the `weaver` binary on PATH:
+ https://github.com/open-telemetry/weaver/releases
+"""
+
+import os
+import shutil
+import unittest
+
+from opentelemetry.sdk.resources import SERVICE_NAME, Resource
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import BatchSpanProcessor
+from opentelemetry.test.weaver_live_check import (
+ LiveCheckError,
+ LiveCheckReport,
+ WeaverLiveCheck,
+)
+
+try:
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import ( # pylint: disable=no-name-in-module
+ OTLPSpanExporter,
+ )
+
+ _HAS_GRPC = True
+except ImportError:
+ _HAS_GRPC = False
+
+_TESTDATA_DIR = os.path.join(os.path.dirname(__file__), "testdata")
+_REGISTRY_DIR = os.path.join(_TESTDATA_DIR, "registry")
+
+
+def _make_provider(otlp_endpoint: str) -> TracerProvider:
+ resource = Resource.create({SERVICE_NAME: "test-service"})
+ exporter = OTLPSpanExporter(endpoint=otlp_endpoint, insecure=True)
+ provider = TracerProvider(resource=resource)
+ provider.add_span_processor(BatchSpanProcessor(exporter))
+ return provider
+
+
+@unittest.skipUnless(
+ _HAS_GRPC,
+ "grpc exporter not installed",
+)
+@unittest.skipUnless(
+ shutil.which("weaver") is not None,
+ "weaver binary not found on PATH — install from https://github.com/open-telemetry/weaver/releases",
+)
+class TestSDKInitLiveCheck(unittest.TestCase):
+ def test_end_and_check_no_violations(self):
+ """end_and_check() returns a LiveCheckReport with no violations on conformant telemetry."""
+ with WeaverLiveCheck(registry=_REGISTRY_DIR) as weaver:
+ provider = _make_provider(weaver.otlp_endpoint)
+ with provider.get_tracer("test-tracer").start_as_current_span(
+ "test-span"
+ ):
+ pass
+ provider.force_flush()
+ report = weaver.end_and_check()
+
+ self.assertIsInstance(report, LiveCheckReport)
+ self.assertEqual(report.violations, [])
+
+ def test_end_and_check_raises_on_violations(self):
+ """end_and_check() raises LiveCheckError with the report attached."""
+ with WeaverLiveCheck(
+ registry=_REGISTRY_DIR, policies_dir=_TESTDATA_DIR
+ ) as weaver:
+ provider = _make_provider(weaver.otlp_endpoint)
+ with provider.get_tracer("test-tracer").start_as_current_span(
+ "test-span"
+ ) as span:
+ span.set_attribute("never.use.this.attribute", "bad value")
+
+ provider.force_flush()
+
+ with self.assertRaises(LiveCheckError) as cm:
+ weaver.end_and_check()
+
+ # Human-readable message lists the violation
+ self.assertIn(
+ "never.use.this.attribute is forbidden by this bogus policy",
+ str(cm.exception),
+ )
+ # Structured report is attached for programmatic inspection
+ self.assertTrue(
+ any(
+ v["id"] == "test_check"
+ and v["context"].get("attribute_name")
+ == "never.use.this.attribute"
+ for v in cm.exception.report.violations
+ )
+ )
+
+ def test_end_no_violations(self):
+ """end() returns a LiveCheckReport with no violations on conformant telemetry."""
+ with WeaverLiveCheck(registry=_REGISTRY_DIR) as weaver:
+ provider = _make_provider(weaver.otlp_endpoint)
+ with provider.get_tracer("test-tracer").start_as_current_span(
+ "test-span"
+ ):
+ pass
+ provider.force_flush()
+ report = weaver.end()
+
+ self.assertIsInstance(report, LiveCheckReport)
+ self.assertEqual(report.violations, [])
+ # LiveCheckReport supports dict-style access to the raw report data
+ self.assertIn("statistics", report)
+ self.assertIsNotNone(report.get("statistics"))
+ self.assertIsNone(report.get("nonexistent"))
+
+ def test_end_with_violations(self):
+ """end() returns a LiveCheckReport with violations without raising."""
+ with WeaverLiveCheck(
+ registry=_REGISTRY_DIR, policies_dir=_TESTDATA_DIR
+ ) as weaver:
+ provider = _make_provider(weaver.otlp_endpoint)
+ with provider.get_tracer("test-tracer").start_as_current_span(
+ "test-span"
+ ) as span:
+ span.set_attribute("never.use.this.attribute", "bad value")
+
+ provider.force_flush()
+ report = weaver.end()
+
+ self.assertIsInstance(report, LiveCheckReport)
+ # Check the violation id (maps to advice_type in the rego policy)
+ self.assertTrue(
+ any(v["id"] == "test_check" for v in report.violations)
+ )
+ # Check the structured context identifies the offending attribute by name
+ self.assertTrue(
+ any(
+ isinstance(v["context"], dict)
+ and v["context"].get("attribute_name")
+ == "never.use.this.attribute"
+ for v in report.violations
+ )
+ )
+
+ def test_report_span_statistics(self):
+ """The full report exposes span counts and individual span samples."""
+ with WeaverLiveCheck(registry=_REGISTRY_DIR) as weaver:
+ provider = _make_provider(weaver.otlp_endpoint)
+ with provider.get_tracer("test-tracer").start_as_current_span(
+ "test-span"
+ ):
+ pass
+ provider.force_flush()
+ report = weaver.end()
+
+ # Individual spans are accessible in report["samples"], each entry
+ # with a "span" key containing the span data.
+ span_samples = [
+ s["span"] for s in report.get("samples", []) if "span" in s
+ ]
+ self.assertTrue(
+ any(s["name"] == "test-span" for s in span_samples),
+ f"Expected 'test-span' in samples, got: {[s['name'] for s in span_samples]}",
+ )
+
+
+@unittest.skipUnless(
+ _HAS_GRPC,
+ "grpc exporter not installed",
+)
+@unittest.skipUnless(
+ shutil.which("weaver") is not None,
+ "weaver binary not found on PATH — install from https://github.com/open-telemetry/weaver/releases",
+)
+class TestOutputCapture(unittest.TestCase):
+ """weaver's stdout/stderr is captured to tempfiles, not PIPE, so large
+ diagnostic output can't deadlock the subprocess."""
+
+ def test_does_not_deadlock_on_large_diagnostic_output(self):
+ """`--debug --debug` makes weaver dump trace logs that exceed the 64KB
+ PIPE buffer; with tempfile capture the subprocess still exits cleanly."""
+ with WeaverLiveCheck(
+ registry=_REGISTRY_DIR, extra_args=["--debug", "--debug"]
+ ) as weaver:
+ provider = _make_provider(weaver.otlp_endpoint)
+ with provider.get_tracer("test-tracer").start_as_current_span(
+ "test-span"
+ ):
+ pass
+ provider.force_flush()
+ report = weaver.end()
+ self.assertIsInstance(report, LiveCheckReport)
diff --git a/tests/opentelemetry-test-utils/tests/testdata/registry/attributes.yaml b/tests/opentelemetry-test-utils/tests/testdata/registry/attributes.yaml
new file mode 100644
index 0000000..d4a5e30
--- /dev/null
+++ b/tests/opentelemetry-test-utils/tests/testdata/registry/attributes.yaml
@@ -0,0 +1,27 @@
+# this is a test registry, used to save up time on weaver loading semconv from GH repo
+# and preventing flaky tests resulting from unpredictable network/performance in CI
+groups:
+ - id: registry.test
+ type: attribute_group
+ brief: Minimal registry for WeaverLiveCheck self-tests.
+ attributes:
+ - id: service.name
+ type: string
+ brief: The name of the service.
+ stability: stable
+ examples: ["test-service"]
+ - id: telemetry.sdk.language
+ type: string
+ brief: The language of the telemetry SDK.
+ stability: stable
+ examples: ["python"]
+ - id: telemetry.sdk.name
+ type: string
+ brief: The name of the telemetry SDK.
+ stability: stable
+ examples: ["opentelemetry"]
+ - id: telemetry.sdk.version
+ type: string
+ brief: The version string of the telemetry SDK.
+ stability: stable
+ examples: ["1.0.0"]
diff --git a/tests/opentelemetry-test-utils/tests/testdata/test-policy.rego b/tests/opentelemetry-test-utils/tests/testdata/test-policy.rego
new file mode 100644
index 0000000..de8e9a2
--- /dev/null
+++ b/tests/opentelemetry-test-utils/tests/testdata/test-policy.rego
@@ -0,0 +1,16 @@
+package live_check_advice
+
+import rego.v1
+
+deny contains result if {
+ input.sample.attribute.name == "never.use.this.attribute"
+ result := {
+ "type": "advice",
+ "advice_type": "test_check",
+ "advice_level": "violation",
+ "context": {
+ "attribute_name": input.sample.attribute.name,
+ },
+ "message": "never.use.this.attribute is forbidden by this bogus policy",
+ }
+}
diff --git a/tests/w3c_tracecontext_validation_server.py b/tests/w3c_tracecontext_validation_server.py
index 732ca1e..4b44d68 100644
--- a/tests/w3c_tracecontext_validation_server.py
+++ b/tests/w3c_tracecontext_validation_server.py
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# 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.
+# SPDX-License-Identifier: Apache-2.0
"""
This server is intended to be used with the W3C tracecontext validation
Service. It implements the APIs needed to be exercised by the test bed.
diff --git a/tox.ini b/tox.ini
index 2060955..5ebf34c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -8,94 +8,101 @@ envlist =
; Environments are organized by individual package, allowing
; for specifying supported Python versions per package.
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-api
+ py3{10,11,12,13,14,14t}-test-opentelemetry-api
pypy3-test-opentelemetry-api
lint-opentelemetry-api
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-proto-gen-{oldest,latest}
+ py3{10,11,12,13,14,14t}-test-opentelemetry-proto-gen-{oldest,latest}
pypy3-test-opentelemetry-proto-gen-{oldest,latest}
lint-opentelemetry-proto-gen-latest
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-protojson-gen-{oldest,latest}
+ py3{10,11,12,13,14,14t}-test-opentelemetry-protojson-gen-{oldest,latest}
pypy3-test-opentelemetry-protojson-gen-{oldest,latest}
lint-opentelemetry-protojson-gen-latest
- py3{9,10,11,12,13,14}-test-opentelemetry-codegen-json
+ py3{10,11,12,13,14}-test-opentelemetry-codegen-json
; intentionally excluded from pypy3
lint-opentelemetry-codegen-json
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-sdk
+ py3{10,11,12,13,14,14t}-test-opentelemetry-sdk
pypy3-test-opentelemetry-sdk
lint-opentelemetry-sdk
benchmark-opentelemetry-sdk
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-semantic-conventions
+ py3{10,11,12,13,14,14t}-test-opentelemetry-semantic-conventions
pypy3-test-opentelemetry-semantic-conventions
lint-opentelemetry-semantic-conventions
- py3{9,10,11,12,13,14}-test-opentelemetry-getting-started
+ py3{10,11,12,13,14}-test-opentelemetry-getting-started
lint-opentelemetry-getting-started
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-opentracing-shim
+ py3{10,11,12,13,14,14t}-test-opentelemetry-opentracing-shim
pypy3-test-opentelemetry-opentracing-shim
lint-opentelemetry-opentracing-shim
- py3{9,10,11,12,13,14}-test-opentelemetry-opencensus-shim
+ py3{10,11,12,13,14}-test-opentelemetry-opencensus-shim
; opencensus-shim intentionally excluded from pypy3 (grpcio install fails)
lint-opentelemetry-opencensus-shim
- py3{9,10,11,12,13,14}-test-opentelemetry-exporter-opencensus
+ py3{10,11,12,13,14}-test-opentelemetry-exporter-opencensus
; exporter-opencensus intentionally excluded from pypy3
lint-opentelemetry-exporter-opencensus
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-exporter-otlp-proto-common
+ py3{10,11,12,13,14,14t}-test-opentelemetry-exporter-otlp-proto-common
pypy3-test-opentelemetry-exporter-otlp-proto-common
lint-opentelemetry-exporter-otlp-proto-common
+ py3{10,11,12,13,14,14t}-test-opentelemetry-exporter-otlp-json-common
+ pypy3-test-opentelemetry-exporter-otlp-json-common
+ lint-opentelemetry-exporter-otlp-json-common
+ benchmark-opentelemetry-exporter-otlp-json-common
+
; opentelemetry-exporter-otlp
- py3{9,10,11,12,13,14}-test-opentelemetry-exporter-otlp-combined
+ py3{10,11,12,13,14}-test-opentelemetry-exporter-otlp-combined
; intentionally excluded from pypy3
lint-opentelemetry-exporter-otlp-combined
- py3{9,10,11,12,13,14}-test-opentelemetry-exporter-otlp-proto-grpc-{oldest,latest}
+ py3{10,11,12,13,14}-test-opentelemetry-exporter-otlp-proto-grpc-{oldest,latest}
; intentionally excluded from pypy3
lint-opentelemetry-exporter-otlp-proto-grpc-latest
benchmark-opentelemetry-exporter-otlp-proto-grpc-latest
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-exporter-otlp-proto-http
+ py3{10,11,12,13,14,14t}-test-opentelemetry-exporter-otlp-proto-http
pypy3-test-opentelemetry-exporter-otlp-proto-http
lint-opentelemetry-exporter-otlp-proto-http
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-exporter-prometheus
+ py3{10,11,12,13,14,14t}-test-opentelemetry-exporter-prometheus
pypy3-test-opentelemetry-exporter-prometheus
lint-opentelemetry-exporter-prometheus
; opentelemetry-exporter-zipkin
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-exporter-zipkin-combined
+ py3{10,11,12,13,14,14t}-test-opentelemetry-exporter-zipkin-combined
pypy3-test-opentelemetry-exporter-zipkin-combined
lint-opentelemetry-exporter-zipkin-combined
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-exporter-zipkin-proto-http
+ py3{10,11,12,13,14,14t}-test-opentelemetry-exporter-zipkin-proto-http
pypy3-test-opentelemetry-exporter-zipkin-proto-http
lint-opentelemetry-exporter-zipkin-proto-http
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-exporter-zipkin-json
+ py3{10,11,12,13,14,14t}-test-opentelemetry-exporter-zipkin-json
pypy3-test-opentelemetry-exporter-zipkin-json
lint-opentelemetry-exporter-zipkin-json
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-propagator-b3
+ py3{10,11,12,13,14,14t}-test-opentelemetry-propagator-b3
pypy3-test-opentelemetry-propagator-b3
lint-opentelemetry-propagator-b3
benchmark-opentelemetry-propagator-b3
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-propagator-jaeger
+ py3{10,11,12,13,14,14t}-test-opentelemetry-propagator-jaeger
pypy3-test-opentelemetry-propagator-jaeger
lint-opentelemetry-propagator-jaeger
- py3{9,10,11,12,13,14,14t}-test-opentelemetry-test-utils
+ ; skip py314t until grpc gets wheels for it or we stop using grpc exporter in weaver tests
+ py3{10,11,12,13,14}-test-opentelemetry-test-utils
pypy3-test-opentelemetry-test-utils
lint-opentelemetry-test-utils
+ lint-license-header-check
spellcheck
tracecontext
typecheck
@@ -133,6 +140,9 @@ deps =
exporter-otlp-proto-common: -r {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-common/test-requirements.txt
+ exporter-otlp-json-common: -r {toxinidir}/exporter/opentelemetry-exporter-otlp-json-common/test-requirements.txt
+ benchmark-exporter-otlp-json-common: -r {toxinidir}/exporter/opentelemetry-exporter-otlp-json-common/benchmark-requirements.txt
+
exporter-otlp-combined: -r {toxinidir}/exporter/opentelemetry-exporter-otlp/test-requirements.txt
opentelemetry-exporter-otlp-proto-grpc-oldest: -r {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.oldest.txt
@@ -226,6 +236,10 @@ commands =
test-opentelemetry-exporter-otlp-proto-common: pytest {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-common/tests {posargs}
lint-opentelemetry-exporter-otlp-proto-common: sh -c "cd exporter && pylint --prefer-stubs yes --rcfile ../.pylintrc {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-common"
+ test-opentelemetry-exporter-otlp-json-common: pytest {toxinidir}/exporter/opentelemetry-exporter-otlp-json-common/tests {posargs}
+ lint-opentelemetry-exporter-otlp-json-common: sh -c "cd exporter && pylint --prefer-stubs yes --rcfile ../.pylintrc {toxinidir}/exporter/opentelemetry-exporter-otlp-json-common"
+ benchmark-opentelemetry-exporter-otlp-json-common: pytest {toxinidir}/exporter/opentelemetry-exporter-otlp-json-common/benchmarks --benchmark-json=exporter-otlp-json-common-benchmark.json {posargs}
+
test-opentelemetry-exporter-otlp-combined: pytest {toxinidir}/exporter/opentelemetry-exporter-otlp/tests {posargs}
lint-opentelemetry-exporter-otlp-combined: sh -c "cd exporter && pylint --rcfile ../.pylintrc {toxinidir}/exporter/opentelemetry-exporter-otlp"
@@ -261,7 +275,6 @@ commands =
coverage: {toxinidir}/scripts/coverage.sh
[testenv:spellcheck]
-basepython: python3
recreate = True
deps =
codespell==2.2.6
@@ -270,7 +283,6 @@ commands =
codespell
[testenv:docs]
-basepython: python3
recreate = True
deps =
-c {toxinidir}/dev-requirements.txt
@@ -343,10 +355,14 @@ commands =
commands_post =
docker-compose down -v
+[testenv:lint-license-header-check]
+commands =
+ python {toxinidir}/scripts/check_license_header.py
+
[testenv:public-symbols-check]
recreate = True
deps =
- GitPython==3.1.40
+ GitPython==3.1.50
griffe==1.7.3
toml
commands =
@@ -369,8 +385,6 @@ allowlist_externals =
python
commands =
datamodel-codegen
- ; Fix TypeAlias import for Python 3.9 compatibility
- python scripts/fix_typealias.py opentelemetry-sdk/src/opentelemetry/sdk/_configuration/models.py
[testenv:shellcheck]
commands_pre =
@@ -379,7 +393,6 @@ commands =
sh -c "find {toxinidir} -name \*.sh | xargs shellcheck --severity=warning"
[testenv:typecheck]
-basepython: python3
deps =
-c {toxinidir}/dev-requirements.txt
pyright
@@ -390,6 +403,7 @@ deps =
-e {toxinidir}/opentelemetry-sdk[file-configuration]
-e {toxinidir}/tests/opentelemetry-test-utils
-e {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-common
+ -e {toxinidir}/exporter/opentelemetry-exporter-otlp-json-common
-e {toxinidir}/exporter/opentelemetry-exporter-otlp
-e {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-grpc
-e {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-http
@@ -402,7 +416,6 @@ commands =
pyright
[testenv:{precommit,ruff}]
-basepython: python3
deps =
-c {toxinidir}/dev-requirements.txt
pre-commit
diff --git a/uv.lock b/uv.lock
index 571ea11..c7eb242 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1,11 +1,10 @@
version = 1
-revision = 1
-requires-python = ">=3.9"
+revision = 3
+requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.14'",
"python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
- "python_full_version < '3.10'",
+ "python_full_version < '3.13'",
]
[manifest]
@@ -13,6 +12,7 @@ members = [
"opentelemetry-api",
"opentelemetry-codegen-json",
"opentelemetry-exporter-otlp",
+ "opentelemetry-exporter-otlp-json-common",
"opentelemetry-exporter-otlp-proto-common",
"opentelemetry-exporter-otlp-proto-grpc",
"opentelemetry-exporter-otlp-proto-http",
@@ -31,885 +31,793 @@ members = [
[[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 }
+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 },
+ { 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.12.1"
-source = { registry = "https://pypi.org/simple" }
+version = "4.13.0"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "idna" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685 }
+sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592 },
+ { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
]
[[package]]
name = "argcomplete"
version = "3.6.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/38/61/0b9ae6399dd4a58d8c1b1dc5a27d6f2808023d0b5dd3104bb99f45a33ff6/argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c", size = 73754 }
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/38/61/0b9ae6399dd4a58d8c1b1dc5a27d6f2808023d0b5dd3104bb99f45a33ff6/argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c", size = 73754, upload-time = "2025-10-20T03:33:34.741Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/74/f5/9373290775639cb67a2fce7f629a1c240dce9f12fe927bc32b2736e16dfc/argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce", size = 43846 },
+ { url = "https://files.pythonhosted.org/packages/74/f5/9373290775639cb67a2fce7f629a1c240dce9f12fe927bc32b2736e16dfc/argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce", size = 43846, upload-time = "2025-10-20T03:33:33.021Z" },
]
[[package]]
name = "asgiref"
-version = "3.11.0"
-source = { registry = "https://pypi.org/simple" }
+version = "3.11.1"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/76/b9/4db2509eabd14b4a8c71d1b24c8d5734c52b8560a7b1e1a8b56c8d25568b/asgiref-3.11.0.tar.gz", hash = "sha256:13acff32519542a1736223fb79a715acdebe24286d98e8b164a73085f40da2c4", size = 37969 }
+sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550, upload-time = "2026-02-03T13:30:14.33Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/91/be/317c2c55b8bbec407257d45f5c8d1b6867abc76d12043f2d3d58c538a4ea/asgiref-3.11.0-py3-none-any.whl", hash = "sha256:1db9021efadb0d9512ce8ffaf72fcef601c7b73a8807a1bb2ef143dc6b14846d", size = 24096 },
+ { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" },
]
[[package]]
name = "attrs"
-version = "25.4.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251 }
+version = "26.1.0"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615 },
+ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" },
]
[[package]]
name = "black"
-version = "25.11.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
+version = "26.3.1"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "mypy-extensions", marker = "python_full_version < '3.10'" },
- { name = "packaging", marker = "python_full_version < '3.10'" },
- { name = "pathspec", marker = "python_full_version < '3.10'" },
- { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "pytokens", marker = "python_full_version < '3.10'" },
- { name = "tomli", marker = "python_full_version < '3.10'" },
- { name = "typing-extensions", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/8c/ad/33adf4708633d047950ff2dfdea2e215d84ac50ef95aff14a614e4b6e9b2/black-25.11.0.tar.gz", hash = "sha256:9a323ac32f5dc75ce7470501b887250be5005a01602e931a15e45593f70f6e08", size = 655669 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b3/d2/6caccbc96f9311e8ec3378c296d4f4809429c43a6cd2394e3c390e86816d/black-25.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ec311e22458eec32a807f029b2646f661e6859c3f61bc6d9ffb67958779f392e", size = 1743501 },
- { url = "https://files.pythonhosted.org/packages/69/35/b986d57828b3f3dccbf922e2864223197ba32e74c5004264b1c62bc9f04d/black-25.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1032639c90208c15711334d681de2e24821af0575573db2810b0763bcd62e0f0", size = 1597308 },
- { url = "https://files.pythonhosted.org/packages/39/8e/8b58ef4b37073f52b64a7b2dd8c9a96c84f45d6f47d878d0aa557e9a2d35/black-25.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c0f7c461df55cf32929b002335883946a4893d759f2df343389c4396f3b6b37", size = 1656194 },
- { url = "https://files.pythonhosted.org/packages/8d/30/9c2267a7955ecc545306534ab88923769a979ac20a27cf618d370091e5dd/black-25.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:f9786c24d8e9bd5f20dc7a7f0cdd742644656987f6ea6947629306f937726c03", size = 1347996 },
- { url = "https://files.pythonhosted.org/packages/c4/62/d304786b75ab0c530b833a89ce7d997924579fb7484ecd9266394903e394/black-25.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:895571922a35434a9d8ca67ef926da6bc9ad464522a5fe0db99b394ef1c0675a", size = 1727891 },
- { url = "https://files.pythonhosted.org/packages/82/5d/ffe8a006aa522c9e3f430e7b93568a7b2163f4b3f16e8feb6d8c3552761a/black-25.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb4f4b65d717062191bdec8e4a442539a8ea065e6af1c4f4d36f0cdb5f71e170", size = 1581875 },
- { url = "https://files.pythonhosted.org/packages/cb/c8/7c8bda3108d0bb57387ac41b4abb5c08782b26da9f9c4421ef6694dac01a/black-25.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d81a44cbc7e4f73a9d6ae449ec2317ad81512d1e7dce7d57f6333fd6259737bc", size = 1642716 },
- { url = "https://files.pythonhosted.org/packages/34/b9/f17dea34eecb7cc2609a89627d480fb6caea7b86190708eaa7eb15ed25e7/black-25.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:7eebd4744dfe92ef1ee349dc532defbf012a88b087bb7ddd688ff59a447b080e", size = 1352904 },
- { url = "https://files.pythonhosted.org/packages/7f/12/5c35e600b515f35ffd737da7febdb2ab66bb8c24d88560d5e3ef3d28c3fd/black-25.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:80e7486ad3535636657aa180ad32a7d67d7c273a80e12f1b4bfa0823d54e8fac", size = 1772831 },
- { url = "https://files.pythonhosted.org/packages/1a/75/b3896bec5a2bb9ed2f989a970ea40e7062f8936f95425879bbe162746fe5/black-25.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6cced12b747c4c76bc09b4db057c319d8545307266f41aaee665540bc0e04e96", size = 1608520 },
- { url = "https://files.pythonhosted.org/packages/f3/b5/2bfc18330eddbcfb5aab8d2d720663cd410f51b2ed01375f5be3751595b0/black-25.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb2d54a39e0ef021d6c5eef442e10fd71fcb491be6413d083a320ee768329dd", size = 1682719 },
- { url = "https://files.pythonhosted.org/packages/96/fb/f7dc2793a22cdf74a72114b5ed77fe3349a2e09ef34565857a2f917abdf2/black-25.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae263af2f496940438e5be1a0c1020e13b09154f3af4df0835ea7f9fe7bfa409", size = 1362684 },
- { url = "https://files.pythonhosted.org/packages/ad/47/3378d6a2ddefe18553d1115e36aea98f4a90de53b6a3017ed861ba1bd3bc/black-25.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a1d40348b6621cc20d3d7530a5b8d67e9714906dfd7346338249ad9c6cedf2b", size = 1772446 },
- { url = "https://files.pythonhosted.org/packages/ba/4b/0f00bfb3d1f7e05e25bfc7c363f54dc523bb6ba502f98f4ad3acf01ab2e4/black-25.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51c65d7d60bb25429ea2bf0731c32b2a2442eb4bd3b2afcb47830f0b13e58bfd", size = 1607983 },
- { url = "https://files.pythonhosted.org/packages/99/fe/49b0768f8c9ae57eb74cc10a1f87b4c70453551d8ad498959721cc345cb7/black-25.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:936c4dd07669269f40b497440159a221ee435e3fddcf668e0c05244a9be71993", size = 1682481 },
- { url = "https://files.pythonhosted.org/packages/55/17/7e10ff1267bfa950cc16f0a411d457cdff79678fbb77a6c73b73a5317904/black-25.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:f42c0ea7f59994490f4dccd64e6b2dd49ac57c7c84f38b8faab50f8759db245c", size = 1363869 },
- { url = "https://files.pythonhosted.org/packages/67/c0/cc865ce594d09e4cd4dfca5e11994ebb51604328489f3ca3ae7bb38a7db5/black-25.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:35690a383f22dd3e468c85dc4b915217f87667ad9cce781d7b42678ce63c4170", size = 1771358 },
- { url = "https://files.pythonhosted.org/packages/37/77/4297114d9e2fd2fc8ab0ab87192643cd49409eb059e2940391e7d2340e57/black-25.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dae49ef7369c6caa1a1833fd5efb7c3024bb7e4499bf64833f65ad27791b1545", size = 1612902 },
- { url = "https://files.pythonhosted.org/packages/de/63/d45ef97ada84111e330b2b2d45e1dd163e90bd116f00ac55927fb6bf8adb/black-25.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bd4a22a0b37401c8e492e994bce79e614f91b14d9ea911f44f36e262195fdda", size = 1680571 },
- { url = "https://files.pythonhosted.org/packages/ff/4b/5604710d61cdff613584028b4cb4607e56e148801ed9b38ee7970799dab6/black-25.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:aa211411e94fdf86519996b7f5f05e71ba34835d8f0c0f03c00a26271da02664", size = 1382599 },
- { url = "https://files.pythonhosted.org/packages/d5/9a/5b2c0e3215fe748fcf515c2dd34658973a1210bf610e24de5ba887e4f1c8/black-25.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3bb5ce32daa9ff0605d73b6f19da0b0e6c1f8f2d75594db539fdfed722f2b06", size = 1743063 },
- { url = "https://files.pythonhosted.org/packages/a1/20/245164c6efc27333409c62ba54dcbfbe866c6d1957c9a6c0647786e950da/black-25.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9815ccee1e55717fe9a4b924cae1646ef7f54e0f990da39a34fc7b264fcf80a2", size = 1596867 },
- { url = "https://files.pythonhosted.org/packages/ca/6f/1a3859a7da205f3d50cf3a8bec6bdc551a91c33ae77a045bb24c1f46ab54/black-25.11.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92285c37b93a1698dcbc34581867b480f1ba3a7b92acf1fe0467b04d7a4da0dc", size = 1655678 },
- { url = "https://files.pythonhosted.org/packages/56/1a/6dec1aeb7be90753d4fcc273e69bc18bfd34b353223ed191da33f7519410/black-25.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:43945853a31099c7c0ff8dface53b4de56c41294fa6783c0441a8b1d9bf668bc", size = 1347452 },
- { url = "https://files.pythonhosted.org/packages/00/5d/aed32636ed30a6e7f9efd6ad14e2a0b0d687ae7c8c7ec4e4a557174b895c/black-25.11.0-py3-none-any.whl", hash = "sha256:e3f562da087791e96cefcd9dda058380a442ab322a02e222add53736451f604b", size = 204918 },
-]
-
-[[package]]
-name = "black"
-version = "26.1.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
+ { name = "click" },
+ { name = "mypy-extensions" },
+ { name = "packaging" },
+ { name = "pathspec" },
+ { name = "platformdirs" },
+ { name = "pytokens" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
-dependencies = [
- { name = "click", version = "8.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "mypy-extensions", marker = "python_full_version >= '3.10'" },
- { name = "packaging", marker = "python_full_version >= '3.10'" },
- { name = "pathspec", marker = "python_full_version >= '3.10'" },
- { name = "platformdirs", version = "4.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "pytokens", marker = "python_full_version >= '3.10'" },
- { name = "tomli", marker = "python_full_version == '3.10.*'" },
- { name = "typing-extensions", marker = "python_full_version == '3.10.*'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/13/88/560b11e521c522440af991d46848a2bde64b5f7202ec14e1f46f9509d328/black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58", size = 658785 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/51/1b/523329e713f965ad0ea2b7a047eeb003007792a0353622ac7a8cb2ee6fef/black-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ca699710dece84e3ebf6e92ee15f5b8f72870ef984bf944a57a777a48357c168", size = 1849661 },
- { url = "https://files.pythonhosted.org/packages/14/82/94c0640f7285fa71c2f32879f23e609dd2aa39ba2641f395487f24a578e7/black-26.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e8e75dabb6eb83d064b0db46392b25cabb6e784ea624219736e8985a6b3675d", size = 1689065 },
- { url = "https://files.pythonhosted.org/packages/f0/78/474373cbd798f9291ed8f7107056e343fd39fef42de4a51c7fd0d360840c/black-26.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb07665d9a907a1a645ee41a0df8a25ffac8ad9c26cdb557b7b88eeeeec934e0", size = 1751502 },
- { url = "https://files.pythonhosted.org/packages/29/89/59d0e350123f97bc32c27c4d79563432d7f3530dca2bff64d855c178af8b/black-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:7ed300200918147c963c87700ccf9966dceaefbbb7277450a8d646fc5646bf24", size = 1400102 },
- { url = "https://files.pythonhosted.org/packages/e1/bc/5d866c7ae1c9d67d308f83af5462ca7046760158bbf142502bad8f22b3a1/black-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:c5b7713daea9bf943f79f8c3b46f361cc5229e0e604dcef6a8bb6d1c37d9df89", size = 1207038 },
- { url = "https://files.pythonhosted.org/packages/30/83/f05f22ff13756e1a8ce7891db517dbc06200796a16326258268f4658a745/black-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3cee1487a9e4c640dc7467aaa543d6c0097c391dc8ac74eb313f2fbf9d7a7cb5", size = 1831956 },
- { url = "https://files.pythonhosted.org/packages/7d/f2/b2c570550e39bedc157715e43927360312d6dd677eed2cc149a802577491/black-26.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d62d14ca31c92adf561ebb2e5f2741bf8dea28aef6deb400d49cca011d186c68", size = 1672499 },
- { url = "https://files.pythonhosted.org/packages/7a/d7/990d6a94dc9e169f61374b1c3d4f4dd3037e93c2cc12b6f3b12bc663aa7b/black-26.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb1dafbbaa3b1ee8b4550a84425aac8874e5f390200f5502cf3aee4a2acb2f14", size = 1735431 },
- { url = "https://files.pythonhosted.org/packages/36/1c/cbd7bae7dd3cb315dfe6eeca802bb56662cc92b89af272e014d98c1f2286/black-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:101540cb2a77c680f4f80e628ae98bd2bd8812fb9d72ade4f8995c5ff019e82c", size = 1400468 },
- { url = "https://files.pythonhosted.org/packages/59/b1/9fe6132bb2d0d1f7094613320b56297a108ae19ecf3041d9678aec381b37/black-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:6f3977a16e347f1b115662be07daa93137259c711e526402aa444d7a88fdc9d4", size = 1207332 },
- { url = "https://files.pythonhosted.org/packages/f5/13/710298938a61f0f54cdb4d1c0baeb672c01ff0358712eddaf29f76d32a0b/black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f", size = 1878189 },
- { url = "https://files.pythonhosted.org/packages/79/a6/5179beaa57e5dbd2ec9f1c64016214057b4265647c62125aa6aeffb05392/black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6", size = 1700178 },
- { url = "https://files.pythonhosted.org/packages/8c/04/c96f79d7b93e8f09d9298b333ca0d31cd9b2ee6c46c274fd0f531de9dc61/black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a", size = 1777029 },
- { url = "https://files.pythonhosted.org/packages/49/f9/71c161c4c7aa18bdda3776b66ac2dc07aed62053c7c0ff8bbda8c2624fe2/black-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a19915ec61f3a8746e8b10adbac4a577c6ba9851fa4a9e9fbfbcf319887a5791", size = 1406466 },
- { url = "https://files.pythonhosted.org/packages/4a/8b/a7b0f974e473b159d0ac1b6bcefffeb6bec465898a516ee5cc989503cbc7/black-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:643d27fb5facc167c0b1b59d0315f2674a6e950341aed0fc05cf307d22bf4954", size = 1216393 },
- { url = "https://files.pythonhosted.org/packages/79/04/fa2f4784f7237279332aa735cdfd5ae2e7730db0072fb2041dadda9ae551/black-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ba1d768fbfb6930fc93b0ecc32a43d8861ded16f47a40f14afa9bb04ab93d304", size = 1877781 },
- { url = "https://files.pythonhosted.org/packages/cf/ad/5a131b01acc0e5336740a039628c0ab69d60cf09a2c87a4ec49f5826acda/black-26.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b807c240b64609cb0e80d2200a35b23c7df82259f80bef1b2c96eb422b4aac9", size = 1699670 },
- { url = "https://files.pythonhosted.org/packages/da/7c/b05f22964316a52ab6b4265bcd52c0ad2c30d7ca6bd3d0637e438fc32d6e/black-26.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1de0f7d01cc894066a1153b738145b194414cc6eeaad8ef4397ac9abacf40f6b", size = 1775212 },
- { url = "https://files.pythonhosted.org/packages/a6/a3/e8d1526bea0446e040193185353920a9506eab60a7d8beb062029129c7d2/black-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:91a68ae46bf07868963671e4d05611b179c2313301bd756a89ad4e3b3db2325b", size = 1409953 },
- { url = "https://files.pythonhosted.org/packages/c7/5a/d62ebf4d8f5e3a1daa54adaab94c107b57be1b1a2f115a0249b41931e188/black-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:be5e2fe860b9bd9edbf676d5b60a9282994c03fbbd40fe8f5e75d194f96064ca", size = 1217707 },
- { url = "https://files.pythonhosted.org/packages/6a/83/be35a175aacfce4b05584ac415fd317dd6c24e93a0af2dcedce0f686f5d8/black-26.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc8c71656a79ca49b8d3e2ce8103210c9481c57798b48deeb3a8bb02db5f115", size = 1871864 },
- { url = "https://files.pythonhosted.org/packages/a5/f5/d33696c099450b1274d925a42b7a030cd3ea1f56d72e5ca8bbed5f52759c/black-26.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b22b3810451abe359a964cc88121d57f7bce482b53a066de0f1584988ca36e79", size = 1701009 },
- { url = "https://files.pythonhosted.org/packages/1b/87/670dd888c537acb53a863bc15abbd85b22b429237d9de1b77c0ed6b79c42/black-26.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53c62883b3f999f14e5d30b5a79bd437236658ad45b2f853906c7cbe79de00af", size = 1767806 },
- { url = "https://files.pythonhosted.org/packages/fe/9c/cd3deb79bfec5bcf30f9d2100ffeec63eecce826eb63e3961708b9431ff1/black-26.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:f016baaadc423dc960cdddf9acae679e71ee02c4c341f78f3179d7e4819c095f", size = 1433217 },
- { url = "https://files.pythonhosted.org/packages/4e/29/f3be41a1cf502a283506f40f5d27203249d181f7a1a2abce1c6ce188035a/black-26.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:66912475200b67ef5a0ab665011964bf924745103f51977a78b4fb92a9fc1bf0", size = 1245773 },
- { url = "https://files.pythonhosted.org/packages/e4/3d/51bdb3ecbfadfaf825ec0c75e1de6077422b4afa2091c6c9ba34fbfc0c2d/black-26.1.0-py3-none-any.whl", hash = "sha256:1054e8e47ebd686e078c0bb0eaf31e6ce69c966058d122f2c0c950311f9f3ede", size = 204010 },
+sdist = { url = "https://files.pythonhosted.org/packages/e1/c5/61175d618685d42b005847464b8fb4743a67b1b8fdb75e50e5a96c31a27a/black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07", size = 666155, upload-time = "2026-03-12T03:36:03.593Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/32/a8/11170031095655d36ebc6664fe0897866f6023892396900eec0e8fdc4299/black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2", size = 1866562, upload-time = "2026-03-12T03:39:58.639Z" },
+ { url = "https://files.pythonhosted.org/packages/69/ce/9e7548d719c3248c6c2abfd555d11169457cbd584d98d179111338423790/black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b", size = 1703623, upload-time = "2026-03-12T03:40:00.347Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/0a/8d17d1a9c06f88d3d030d0b1d4373c1551146e252afe4547ed601c0e697f/black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac", size = 1768388, upload-time = "2026-03-12T03:40:01.765Z" },
+ { url = "https://files.pythonhosted.org/packages/52/79/c1ee726e221c863cde5164f925bacf183dfdf0397d4e3f94889439b947b4/black-26.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a", size = 1412969, upload-time = "2026-03-12T03:40:03.252Z" },
+ { url = "https://files.pythonhosted.org/packages/73/a5/15c01d613f5756f68ed8f6d4ec0a1e24b82b18889fa71affd3d1f7fad058/black-26.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a", size = 1220345, upload-time = "2026-03-12T03:40:04.892Z" },
+ { url = "https://files.pythonhosted.org/packages/17/57/5f11c92861f9c92eb9dddf515530bc2d06db843e44bdcf1c83c1427824bc/black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff", size = 1851987, upload-time = "2026-03-12T03:40:06.248Z" },
+ { url = "https://files.pythonhosted.org/packages/54/aa/340a1463660bf6831f9e39646bf774086dbd8ca7fc3cded9d59bbdf4ad0a/black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c", size = 1689499, upload-time = "2026-03-12T03:40:07.642Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/01/b726c93d717d72733da031d2de10b92c9fa4c8d0c67e8a8a372076579279/black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5", size = 1754369, upload-time = "2026-03-12T03:40:09.279Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/09/61e91881ca291f150cfc9eb7ba19473c2e59df28859a11a88248b5cbbc4d/black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e", size = 1413613, upload-time = "2026-03-12T03:40:10.943Z" },
+ { url = "https://files.pythonhosted.org/packages/16/73/544f23891b22e7efe4d8f812371ab85b57f6a01b2fc45e3ba2e52ba985b8/black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5", size = 1219719, upload-time = "2026-03-12T03:40:12.597Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/f8/da5eae4fc75e78e6dceb60624e1b9662ab00d6b452996046dfa9b8a6025b/black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1", size = 1895920, upload-time = "2026-03-12T03:40:13.921Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/9f/04e6f26534da2e1629b2b48255c264cabf5eedc5141d04516d9d68a24111/black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f", size = 1718499, upload-time = "2026-03-12T03:40:15.239Z" },
+ { url = "https://files.pythonhosted.org/packages/04/91/a5935b2a63e31b331060c4a9fdb5a6c725840858c599032a6f3aac94055f/black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7", size = 1794994, upload-time = "2026-03-12T03:40:17.124Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/0a/86e462cdd311a3c2a8ece708d22aba17d0b2a0d5348ca34b40cdcbea512e/black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983", size = 1420867, upload-time = "2026-03-12T03:40:18.83Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/e5/22515a19cb7eaee3440325a6b0d95d2c0e88dd180cb011b12ae488e031d1/black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb", size = 1230124, upload-time = "2026-03-12T03:40:20.425Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/77/5728052a3c0450c53d9bb3945c4c46b91baa62b2cafab6801411b6271e45/black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54", size = 1895034, upload-time = "2026-03-12T03:40:21.813Z" },
+ { url = "https://files.pythonhosted.org/packages/52/73/7cae55fdfdfbe9d19e9a8d25d145018965fe2079fa908101c3733b0c55a0/black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f", size = 1718503, upload-time = "2026-03-12T03:40:23.666Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/87/af89ad449e8254fdbc74654e6467e3c9381b61472cc532ee350d28cfdafb/black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56", size = 1793557, upload-time = "2026-03-12T03:40:25.497Z" },
+ { url = "https://files.pythonhosted.org/packages/43/10/d6c06a791d8124b843bf325ab4ac7d2f5b98731dff84d6064eafd687ded1/black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839", size = 1422766, upload-time = "2026-03-12T03:40:27.14Z" },
+ { url = "https://files.pythonhosted.org/packages/59/4f/40a582c015f2d841ac24fed6390bd68f0fc896069ff3a886317959c9daf8/black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2", size = 1232140, upload-time = "2026-03-12T03:40:28.882Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/da/e36e27c9cebc1311b7579210df6f1c86e50f2d7143ae4fcf8a5017dc8809/black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78", size = 1889234, upload-time = "2026-03-12T03:40:30.964Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/7b/9871acf393f64a5fa33668c19350ca87177b181f44bb3d0c33b2d534f22c/black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568", size = 1720522, upload-time = "2026-03-12T03:40:32.346Z" },
+ { url = "https://files.pythonhosted.org/packages/03/87/e766c7f2e90c07fb7586cc787c9ae6462b1eedab390191f2b7fc7f6170a9/black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f", size = 1787824, upload-time = "2026-03-12T03:40:33.636Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/94/2424338fb2d1875e9e83eed4c8e9c67f6905ec25afd826a911aea2b02535/black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c", size = 1445855, upload-time = "2026-03-12T03:40:35.442Z" },
+ { url = "https://files.pythonhosted.org/packages/86/43/0c3338bd928afb8ee7471f1a4eec3bdbe2245ccb4a646092a222e8669840/black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1", size = 1258109, upload-time = "2026-03-12T03:40:36.832Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/0d/52d98722666d6fc6c3dd4c76df339501d6efd40e0ff95e6186a7b7f0befd/black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b", size = 207542, upload-time = "2026-03-12T03:36:01.668Z" },
]
[[package]]
name = "cachetools"
-version = "6.2.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/fb/44/ca1675be2a83aeee1886ab745b28cda92093066590233cc501890eb8417a/cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6", size = 31571 }
+version = "7.0.5"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/af/dd/57fe3fdb6e65b25a5987fd2cdc7e22db0aef508b91634d2e57d22928d41b/cachetools-7.0.5.tar.gz", hash = "sha256:0cd042c24377200c1dcd225f8b7b12b0ca53cc2c961b43757e774ebe190fd990", size = 37367, upload-time = "2026-03-09T20:51:29.451Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e6/46/eb6eca305c77a4489affe1c5d8f4cae82f285d9addd8de4ec084a7184221/cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace", size = 11503 },
+ { url = "https://files.pythonhosted.org/packages/06/f3/39cf3367b8107baa44f861dc802cbf16263c945b62d8265d36034fc07bea/cachetools-7.0.5-py3-none-any.whl", hash = "sha256:46bc8ebefbe485407621d0a4264b23c080cedd913921bad7ac3ed2f26c183114", size = 13918, upload-time = "2026-03-09T20:51:27.33Z" },
]
[[package]]
name = "certifi"
-version = "2025.11.12"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538 }
+version = "2026.2.25"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438 },
+ { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" },
]
[[package]]
-name = "cfgv"
-version = "3.4.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 },
+name = "cffi"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple/" }
+dependencies = [
+ { name = "pycparser", marker = "implementation_name != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" },
+ { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" },
+ { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" },
+ { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" },
+ { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" },
+ { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" },
+ { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" },
+ { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" },
+ { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
+ { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
+ { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
+ { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
+ { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
+ { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
+ { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
+ { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
+ { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
+ { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
+ { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
+ { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
+ { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
+ { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
+ { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
+ { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
+ { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
]
[[package]]
name = "cfgv"
version = "3.5.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334 }
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445 },
-]
-
-[[package]]
-name = "chardet"
-version = "5.2.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385 },
+ { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" },
]
[[package]]
name = "charset-normalizer"
-version = "3.4.4"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709 },
- { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814 },
- { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467 },
- { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280 },
- { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454 },
- { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609 },
- { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849 },
- { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586 },
- { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290 },
- { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663 },
- { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964 },
- { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064 },
- { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015 },
- { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792 },
- { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198 },
- { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262 },
- { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988 },
- { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324 },
- { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742 },
- { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863 },
- { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837 },
- { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550 },
- { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162 },
- { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019 },
- { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310 },
- { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022 },
- { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383 },
- { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098 },
- { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991 },
- { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456 },
- { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978 },
- { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969 },
- { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425 },
- { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162 },
- { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558 },
- { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497 },
- { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240 },
- { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471 },
- { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864 },
- { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647 },
- { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110 },
- { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839 },
- { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667 },
- { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535 },
- { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816 },
- { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694 },
- { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131 },
- { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390 },
- { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091 },
- { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936 },
- { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180 },
- { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346 },
- { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874 },
- { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076 },
- { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601 },
- { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376 },
- { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825 },
- { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583 },
- { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366 },
- { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300 },
- { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465 },
- { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404 },
- { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092 },
- { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408 },
- { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746 },
- { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889 },
- { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641 },
- { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779 },
- { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035 },
- { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542 },
- { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524 },
- { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395 },
- { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680 },
- { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045 },
- { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687 },
- { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014 },
- { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044 },
- { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940 },
- { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104 },
- { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743 },
- { url = "https://files.pythonhosted.org/packages/46/7c/0c4760bccf082737ca7ab84a4c2034fcc06b1f21cf3032ea98bd6feb1725/charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9", size = 209609 },
- { url = "https://files.pythonhosted.org/packages/bb/a4/69719daef2f3d7f1819de60c9a6be981b8eeead7542d5ec4440f3c80e111/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d", size = 149029 },
- { url = "https://files.pythonhosted.org/packages/e6/21/8d4e1d6c1e6070d3672908b8e4533a71b5b53e71d16828cc24d0efec564c/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608", size = 144580 },
- { url = "https://files.pythonhosted.org/packages/a7/0a/a616d001b3f25647a9068e0b9199f697ce507ec898cacb06a0d5a1617c99/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc", size = 162340 },
- { url = "https://files.pythonhosted.org/packages/85/93/060b52deb249a5450460e0585c88a904a83aec474ab8e7aba787f45e79f2/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e", size = 159619 },
- { url = "https://files.pythonhosted.org/packages/dd/21/0274deb1cc0632cd587a9a0ec6b4674d9108e461cb4cd40d457adaeb0564/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1", size = 153980 },
- { url = "https://files.pythonhosted.org/packages/28/2b/e3d7d982858dccc11b31906976323d790dded2017a0572f093ff982d692f/charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3", size = 152174 },
- { url = "https://files.pythonhosted.org/packages/6e/ff/4a269f8e35f1e58b2df52c131a1fa019acb7ef3f8697b7d464b07e9b492d/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6", size = 151666 },
- { url = "https://files.pythonhosted.org/packages/da/c9/ec39870f0b330d58486001dd8e532c6b9a905f5765f58a6f8204926b4a93/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88", size = 145550 },
- { url = "https://files.pythonhosted.org/packages/75/8f/d186ab99e40e0ed9f82f033d6e49001701c81244d01905dd4a6924191a30/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1", size = 163721 },
- { url = "https://files.pythonhosted.org/packages/96/b1/6047663b9744df26a7e479ac1e77af7134b1fcf9026243bb48ee2d18810f/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf", size = 152127 },
- { url = "https://files.pythonhosted.org/packages/59/78/e5a6eac9179f24f704d1be67d08704c3c6ab9f00963963524be27c18ed87/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318", size = 161175 },
- { url = "https://files.pythonhosted.org/packages/e5/43/0e626e42d54dd2f8dd6fc5e1c5ff00f05fbca17cb699bedead2cae69c62f/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c", size = 155375 },
- { url = "https://files.pythonhosted.org/packages/e9/91/d9615bf2e06f35e4997616ff31248c3657ed649c5ab9d35ea12fce54e380/charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505", size = 99692 },
- { url = "https://files.pythonhosted.org/packages/d1/a9/6c040053909d9d1ef4fcab45fddec083aedc9052c10078339b47c8573ea8/charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966", size = 107192 },
- { url = "https://files.pythonhosted.org/packages/f0/c6/4fa536b2c0cd3edfb7ccf8469fa0f363ea67b7213a842b90909ca33dd851/charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50", size = 100220 },
- { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402 },
-]
-
-[[package]]
-name = "click"
-version = "8.1.8"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-dependencies = [
- { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
+version = "3.4.7"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" },
+ { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" },
+ { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" },
+ { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" },
+ { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" },
+ { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" },
+ { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" },
+ { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" },
+ { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" },
+ { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" },
+ { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" },
+ { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" },
+ { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" },
+ { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" },
+ { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" },
+ { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" },
+ { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" },
+ { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" },
+ { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" },
+ { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" },
+ { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" },
+ { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" },
+ { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" },
+ { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" },
+ { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" },
+ { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" },
+ { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" },
+ { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" },
+ { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" },
+ { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" },
+ { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" },
+ { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" },
+ { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" },
+ { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" },
+ { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" },
+ { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" },
+ { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" },
+ { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" },
+ { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" },
+ { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" },
+ { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" },
]
[[package]]
name = "click"
-version = "8.3.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
+version = "8.3.2"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" },
+ { name = "colorama", marker = "sys_platform == 'win32'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065 }
+sdist = { url = "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", size = 302856, upload-time = "2026-04-03T19:14:45.118Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274 },
+ { url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" },
]
[[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 }
+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 },
+ { 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 = "datamodel-code-generator"
-version = "0.45.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
+name = "cryptography"
+version = "46.0.7"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "argcomplete", marker = "python_full_version < '3.10'" },
- { name = "black", version = "25.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "genson", marker = "python_full_version < '3.10'" },
- { name = "inflect", marker = "python_full_version < '3.10'" },
- { name = "isort", version = "6.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "jinja2", marker = "python_full_version < '3.10'" },
- { name = "packaging", marker = "python_full_version < '3.10'" },
- { name = "pydantic", marker = "python_full_version < '3.10'" },
- { name = "pyyaml", marker = "python_full_version < '3.10'" },
- { name = "tomli", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f5/45/993fb3f7f4005bfa167fa94929e5737c8f574dc77fb1a4ebca359a106b56/datamodel_code_generator-0.45.0.tar.gz", hash = "sha256:a493add7a59e5a9c1620deb508694bc84e2e2ca80da4bb58a75262a28b9b2c84", size = 554380 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4d/15/b9b64a3576c3ac230c8f69806f32937364f837cda4facaa9b6d09dafd872/datamodel_code_generator-0.45.0-py3-none-any.whl", hash = "sha256:1c7065d3c9ebff7a0c967f2282e0472be971c721593c7e9cbe3f374f378749b8", size = 182003 },
-]
-
-[package.optional-dependencies]
-http = [
- { name = "httpx", marker = "python_full_version < '3.10'" },
+ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
-ruff = [
- { name = "ruff", marker = "python_full_version < '3.10'" },
+sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0b/5d/4a8f770695d73be252331e60e526291e3df0c9b27556a90a6b47bccca4c2/cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4", size = 7179869, upload-time = "2026-04-08T01:56:17.157Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" },
+ { url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" },
+ { url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" },
+ { url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/bb/a5c213c19ee94b15dfccc48f363738633a493812687f5567addbcbba9f6f/cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457", size = 3026504, upload-time = "2026-04-08T01:56:39.666Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/02/7788f9fefa1d060ca68717c3901ae7fffa21ee087a90b7f23c7a603c32ae/cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b", size = 3488363, upload-time = "2026-04-08T01:56:41.893Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/56/15619b210e689c5403bb0540e4cb7dbf11a6bf42e483b7644e471a2812b3/cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842", size = 7119671, upload-time = "2026-04-08T01:56:44Z" },
+ { url = "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", size = 4268551, upload-time = "2026-04-08T01:56:46.071Z" },
+ { url = "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", size = 4408887, upload-time = "2026-04-08T01:56:47.718Z" },
+ { url = "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", size = 4271354, upload-time = "2026-04-08T01:56:49.312Z" },
+ { url = "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", size = 4905845, upload-time = "2026-04-08T01:56:50.916Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", size = 4444641, upload-time = "2026-04-08T01:56:52.882Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", size = 3967749, upload-time = "2026-04-08T01:56:54.597Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", size = 4270942, upload-time = "2026-04-08T01:56:56.416Z" },
+ { url = "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", size = 4871079, upload-time = "2026-04-08T01:56:58.31Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", size = 4443999, upload-time = "2026-04-08T01:57:00.508Z" },
+ { url = "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", size = 4399191, upload-time = "2026-04-08T01:57:02.654Z" },
+ { url = "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", size = 4655782, upload-time = "2026-04-08T01:57:04.592Z" },
+ { url = "https://files.pythonhosted.org/packages/95/b6/3da51d48415bcb63b00dc17c2eff3a651b7c4fed484308d0f19b30e8cb2c/cryptography-46.0.7-cp314-cp314t-win32.whl", hash = "sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298", size = 3002227, upload-time = "2026-04-08T01:57:06.91Z" },
+ { url = "https://files.pythonhosted.org/packages/32/a8/9f0e4ed57ec9cebe506e58db11ae472972ecb0c659e4d52bbaee80ca340a/cryptography-46.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb", size = 3475332, upload-time = "2026-04-08T01:57:08.807Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/7f/cd42fc3614386bc0c12f0cb3c4ae1fc2bbca5c9662dfed031514911d513d/cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4", size = 7165618, upload-time = "2026-04-08T01:57:10.645Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" },
+ { url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" },
+ { url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" },
+ { url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" },
+ { url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/fa/f0ab06238e899cc3fb332623f337a7364f36f4bb3f2534c2bb95a35b132c/cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246", size = 3013001, upload-time = "2026-04-08T01:57:34.933Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", size = 3472985, upload-time = "2026-04-08T01:57:36.714Z" },
+ { url = "https://files.pythonhosted.org/packages/63/0c/dca8abb64e7ca4f6b2978769f6fea5ad06686a190cec381f0a796fdcaaba/cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f", size = 3476879, upload-time = "2026-04-08T01:57:38.664Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/ea/075aac6a84b7c271578d81a2f9968acb6e273002408729f2ddff517fed4a/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15", size = 4219700, upload-time = "2026-04-08T01:57:40.625Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/7b/1c55db7242b5e5612b29fc7a630e91ee7a6e3c8e7bf5406d22e206875fbd/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455", size = 4385982, upload-time = "2026-04-08T01:57:42.725Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/da/9870eec4b69c63ef5925bf7d8342b7e13bc2ee3d47791461c4e49ca212f4/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65", size = 4219115, upload-time = "2026-04-08T01:57:44.939Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/72/05aa5832b82dd341969e9a734d1812a6aadb088d9eb6f0430fc337cc5a8f/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968", size = 4385479, upload-time = "2026-04-08T01:57:46.86Z" },
+ { url = "https://files.pythonhosted.org/packages/20/2a/1b016902351a523aa2bd446b50a5bc1175d7a7d1cf90fe2ef904f9b84ebc/cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4", size = 3412829, upload-time = "2026-04-08T01:57:48.874Z" },
]
[[package]]
name = "datamodel-code-generator"
-version = "0.53.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
+version = "0.56.0"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "argcomplete", marker = "python_full_version >= '3.10'" },
- { name = "black", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "genson", marker = "python_full_version >= '3.10'" },
- { name = "inflect", marker = "python_full_version >= '3.10'" },
- { name = "isort", version = "7.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "jinja2", marker = "python_full_version >= '3.10'" },
- { name = "packaging", marker = "python_full_version >= '3.10'" },
- { name = "pydantic", marker = "python_full_version >= '3.10'" },
- { name = "pyyaml", marker = "python_full_version >= '3.10'" },
- { name = "tomli", marker = "python_full_version >= '3.10' and python_full_version < '3.12'" },
+ { name = "argcomplete" },
+ { name = "black" },
+ { name = "genson" },
+ { name = "inflect" },
+ { name = "isort" },
+ { name = "jinja2" },
+ { name = "pydantic" },
+ { name = "pyyaml" },
+ { name = "tomli", marker = "python_full_version < '3.12'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/40/65/3802abca0291263862a16e032e984e61e4d0d30a344d9be97815721d64ff/datamodel_code_generator-0.53.0.tar.gz", hash = "sha256:af46b57ad78e6435873132c52843ef0ec7b768a591d3b9917d3409dfc1ab1c90", size = 809949 }
+sdist = { url = "https://files.pythonhosted.org/packages/03/7d/7fc2bb3d8946ca45851da3f23497a2c6e252e92558ccbd89d609cf1e13d4/datamodel_code_generator-0.56.0.tar.gz", hash = "sha256:e7c003fb5421b890aabe12f66ae65b57198b04cfe1da7c40810798020835b3a8", size = 837708, upload-time = "2026-04-04T09:46:19.636Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/ff/43/5dbb6fe09842e10062f94016ccb48c9613f2443253866de3d7b815713b4d/datamodel_code_generator-0.53.0-py3-none-any.whl", hash = "sha256:d1cc2abe79f99b8208c363f5f4b603c29290327ff4e3219a08c0fff45f42aff4", size = 258912 },
+ { url = "https://files.pythonhosted.org/packages/ed/3a/7f169ffc7a2d69a4f9158b1ac083f685b7f4a1a8a1db5d1e4abbb4e741b7/datamodel_code_generator-0.56.0-py3-none-any.whl", hash = "sha256:a0559683fbe90cdf2ce9b6637e3adae3e3a8056a8d0516df581d486e2834ead2", size = 256545, upload-time = "2026-04-04T09:46:17.582Z" },
]
[package.optional-dependencies]
http = [
- { name = "httpx", marker = "python_full_version >= '3.10'" },
+ { name = "httpx" },
]
ruff = [
- { name = "ruff", marker = "python_full_version >= '3.10'" },
+ { name = "ruff" },
]
[[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 }
+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 },
+ { 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 = "exceptiongroup"
version = "1.3.1"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371 }
+sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740 },
+ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
]
[[package]]
name = "filelock"
-version = "3.19.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988 },
-]
-
-[[package]]
-name = "filelock"
-version = "3.20.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922 }
+version = "3.25.2"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054 },
+ { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" },
]
[[package]]
name = "genson"
version = "1.3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/c5/cf/2303c8ad276dcf5ee2ad6cf69c4338fd86ef0f471a5207b069adf7a393cf/genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37", size = 34919 }
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/c5/cf/2303c8ad276dcf5ee2ad6cf69c4338fd86ef0f471a5207b069adf7a393cf/genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37", size = 34919, upload-time = "2024-05-15T22:08:49.123Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/f8/5c/e226de133afd8bb267ec27eead9ae3d784b95b39a287ed404caab39a5f50/genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7", size = 21470 },
+ { url = "https://files.pythonhosted.org/packages/f8/5c/e226de133afd8bb267ec27eead9ae3d784b95b39a287ed404caab39a5f50/genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7", size = 21470, upload-time = "2024-05-15T22:08:47.056Z" },
]
[[package]]
name = "google-auth"
-version = "2.43.0"
-source = { registry = "https://pypi.org/simple" }
+version = "2.49.1"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "cachetools" },
+ { name = "cryptography" },
{ name = "pyasn1-modules" },
- { name = "rsa" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/ff/ef/66d14cf0e01b08d2d51ffc3c20410c4e134a1548fc246a6081eae585a4fe/google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483", size = 296359 }
+sdist = { url = "https://files.pythonhosted.org/packages/ea/80/6a696a07d3d3b0a92488933532f03dbefa4a24ab80fb231395b9a2a1be77/google_auth-2.49.1.tar.gz", hash = "sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64", size = 333825, upload-time = "2026-03-12T19:30:58.135Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/6f/d1/385110a9ae86d91cc14c5282c61fe9f4dc41c0b9f7d423c6ad77038c4448/google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16", size = 223114 },
+ { url = "https://files.pythonhosted.org/packages/e9/eb/c6c2478d8a8d633460be40e2a8a6f8f429171997a35a96f81d3b680dec83/google_auth-2.49.1-py3-none-any.whl", hash = "sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7", size = 240737, upload-time = "2026-03-12T19:30:53.159Z" },
]
[[package]]
name = "googleapis-common-protos"
-version = "1.72.0"
-source = { registry = "https://pypi.org/simple" }
+version = "1.74.0"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "protobuf" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433 }
+sdist = { url = "https://files.pythonhosted.org/packages/20/18/a746c8344152d368a5aac738d4c857012f2c5d1fd2eac7e17b647a7861bd/googleapis_common_protos-1.74.0.tar.gz", hash = "sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1", size = 151254, upload-time = "2026-04-02T21:23:26.679Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515 },
+ { url = "https://files.pythonhosted.org/packages/b6/b0/be5d3329badb9230b765de6eea66b73abd5944bdeb5afb3562ddcd80ae84/googleapis_common_protos-1.74.0-py3-none-any.whl", hash = "sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5", size = 300743, upload-time = "2026-04-02T21:22:49.108Z" },
]
[[package]]
name = "grpcio"
-version = "1.76.0"
-source = { registry = "https://pypi.org/simple" }
+version = "1.80.0"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/b6/e0/318c1ce3ae5a17894d5791e87aea147587c9e702f24122cc7a5c8bbaeeb1/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73", size = 12785182 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/88/17/ff4795dc9a34b6aee6ec379f1b66438a3789cd1315aac0cbab60d92f74b3/grpcio-1.76.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:65a20de41e85648e00305c1bb09a3598f840422e522277641145a32d42dcefcc", size = 5840037 },
- { url = "https://files.pythonhosted.org/packages/4e/ff/35f9b96e3fa2f12e1dcd58a4513a2e2294a001d64dec81677361b7040c9a/grpcio-1.76.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:40ad3afe81676fd9ec6d9d406eda00933f218038433980aa19d401490e46ecde", size = 11836482 },
- { url = "https://files.pythonhosted.org/packages/3e/1c/8374990f9545e99462caacea5413ed783014b3b66ace49e35c533f07507b/grpcio-1.76.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:035d90bc79eaa4bed83f524331d55e35820725c9fbb00ffa1904d5550ed7ede3", size = 6407178 },
- { url = "https://files.pythonhosted.org/packages/1e/77/36fd7d7c75a6c12542c90a6d647a27935a1ecaad03e0ffdb7c42db6b04d2/grpcio-1.76.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4215d3a102bd95e2e11b5395c78562967959824156af11fa93d18fdd18050990", size = 7075684 },
- { url = "https://files.pythonhosted.org/packages/38/f7/e3cdb252492278e004722306c5a8935eae91e64ea11f0af3437a7de2e2b7/grpcio-1.76.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:49ce47231818806067aea3324d4bf13825b658ad662d3b25fada0bdad9b8a6af", size = 6611133 },
- { url = "https://files.pythonhosted.org/packages/7e/20/340db7af162ccd20a0893b5f3c4a5d676af7b71105517e62279b5b61d95a/grpcio-1.76.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8cc3309d8e08fd79089e13ed4819d0af72aa935dd8f435a195fd152796752ff2", size = 7195507 },
- { url = "https://files.pythonhosted.org/packages/10/f0/b2160addc1487bd8fa4810857a27132fb4ce35c1b330c2f3ac45d697b106/grpcio-1.76.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:971fd5a1d6e62e00d945423a567e42eb1fa678ba89072832185ca836a94daaa6", size = 8160651 },
- { url = "https://files.pythonhosted.org/packages/2c/2c/ac6f98aa113c6ef111b3f347854e99ebb7fb9d8f7bb3af1491d438f62af4/grpcio-1.76.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d9adda641db7207e800a7f089068f6f645959f2df27e870ee81d44701dd9db3", size = 7620568 },
- { url = "https://files.pythonhosted.org/packages/90/84/7852f7e087285e3ac17a2703bc4129fafee52d77c6c82af97d905566857e/grpcio-1.76.0-cp310-cp310-win32.whl", hash = "sha256:063065249d9e7e0782d03d2bca50787f53bd0fb89a67de9a7b521c4a01f1989b", size = 3998879 },
- { url = "https://files.pythonhosted.org/packages/10/30/d3d2adcbb6dd3ff59d6ac3df6ef830e02b437fb5c90990429fd180e52f30/grpcio-1.76.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6ae758eb08088d36812dd5d9af7a9859c05b1e0f714470ea243694b49278e7b", size = 4706892 },
- { url = "https://files.pythonhosted.org/packages/a0/00/8163a1beeb6971f66b4bbe6ac9457b97948beba8dd2fc8e1281dce7f79ec/grpcio-1.76.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2e1743fbd7f5fa713a1b0a8ac8ebabf0ec980b5d8809ec358d488e273b9cf02a", size = 5843567 },
- { url = "https://files.pythonhosted.org/packages/10/c1/934202f5cf335e6d852530ce14ddb0fef21be612ba9ecbbcbd4d748ca32d/grpcio-1.76.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:a8c2cf1209497cf659a667d7dea88985e834c24b7c3b605e6254cbb5076d985c", size = 11848017 },
- { url = "https://files.pythonhosted.org/packages/11/0b/8dec16b1863d74af6eb3543928600ec2195af49ca58b16334972f6775663/grpcio-1.76.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:08caea849a9d3c71a542827d6df9d5a69067b0a1efbea8a855633ff5d9571465", size = 6412027 },
- { url = "https://files.pythonhosted.org/packages/d7/64/7b9e6e7ab910bea9d46f2c090380bab274a0b91fb0a2fe9b0cd399fffa12/grpcio-1.76.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f0e34c2079d47ae9f6188211db9e777c619a21d4faba6977774e8fa43b085e48", size = 7075913 },
- { url = "https://files.pythonhosted.org/packages/68/86/093c46e9546073cefa789bd76d44c5cb2abc824ca62af0c18be590ff13ba/grpcio-1.76.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8843114c0cfce61b40ad48df65abcfc00d4dba82eae8718fab5352390848c5da", size = 6615417 },
- { url = "https://files.pythonhosted.org/packages/f7/b6/5709a3a68500a9c03da6fb71740dcdd5ef245e39266461a03f31a57036d8/grpcio-1.76.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8eddfb4d203a237da6f3cc8a540dad0517d274b5a1e9e636fd8d2c79b5c1d397", size = 7199683 },
- { url = "https://files.pythonhosted.org/packages/91/d3/4b1f2bf16ed52ce0b508161df3a2d186e4935379a159a834cb4a7d687429/grpcio-1.76.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:32483fe2aab2c3794101c2a159070584e5db11d0aa091b2c0ea9c4fc43d0d749", size = 8163109 },
- { url = "https://files.pythonhosted.org/packages/5c/61/d9043f95f5f4cf085ac5dd6137b469d41befb04bd80280952ffa2a4c3f12/grpcio-1.76.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dcfe41187da8992c5f40aa8c5ec086fa3672834d2be57a32384c08d5a05b4c00", size = 7626676 },
- { url = "https://files.pythonhosted.org/packages/36/95/fd9a5152ca02d8881e4dd419cdd790e11805979f499a2e5b96488b85cf27/grpcio-1.76.0-cp311-cp311-win32.whl", hash = "sha256:2107b0c024d1b35f4083f11245c0e23846ae64d02f40b2b226684840260ed054", size = 3997688 },
- { url = "https://files.pythonhosted.org/packages/60/9c/5c359c8d4c9176cfa3c61ecd4efe5affe1f38d9bae81e81ac7186b4c9cc8/grpcio-1.76.0-cp311-cp311-win_amd64.whl", hash = "sha256:522175aba7af9113c48ec10cc471b9b9bd4f6ceb36aeb4544a8e2c80ed9d252d", size = 4709315 },
- { url = "https://files.pythonhosted.org/packages/bf/05/8e29121994b8d959ffa0afd28996d452f291b48cfc0875619de0bde2c50c/grpcio-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:81fd9652b37b36f16138611c7e884eb82e0cec137c40d3ef7c3f9b3ed00f6ed8", size = 5799718 },
- { url = "https://files.pythonhosted.org/packages/d9/75/11d0e66b3cdf998c996489581bdad8900db79ebd83513e45c19548f1cba4/grpcio-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:04bbe1bfe3a68bbfd4e52402ab7d4eb59d72d02647ae2042204326cf4bbad280", size = 11825627 },
- { url = "https://files.pythonhosted.org/packages/28/50/2f0aa0498bc188048f5d9504dcc5c2c24f2eb1a9337cd0fa09a61a2e75f0/grpcio-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d388087771c837cdb6515539f43b9d4bf0b0f23593a24054ac16f7a960be16f4", size = 6359167 },
- { url = "https://files.pythonhosted.org/packages/66/e5/bbf0bb97d29ede1d59d6588af40018cfc345b17ce979b7b45424628dc8bb/grpcio-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f8f757bebaaea112c00dba718fc0d3260052ce714e25804a03f93f5d1c6cc11", size = 7044267 },
- { url = "https://files.pythonhosted.org/packages/f5/86/f6ec2164f743d9609691115ae8ece098c76b894ebe4f7c94a655c6b03e98/grpcio-1.76.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:980a846182ce88c4f2f7e2c22c56aefd515daeb36149d1c897f83cf57999e0b6", size = 6573963 },
- { url = "https://files.pythonhosted.org/packages/60/bc/8d9d0d8505feccfdf38a766d262c71e73639c165b311c9457208b56d92ae/grpcio-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f92f88e6c033db65a5ae3d97905c8fea9c725b63e28d5a75cb73b49bda5024d8", size = 7164484 },
- { url = "https://files.pythonhosted.org/packages/67/e6/5d6c2fc10b95edf6df9b8f19cf10a34263b7fd48493936fffd5085521292/grpcio-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4baf3cbe2f0be3289eb68ac8ae771156971848bb8aaff60bad42005539431980", size = 8127777 },
- { url = "https://files.pythonhosted.org/packages/3f/c8/dce8ff21c86abe025efe304d9e31fdb0deaaa3b502b6a78141080f206da0/grpcio-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:615ba64c208aaceb5ec83bfdce7728b80bfeb8be97562944836a7a0a9647d882", size = 7594014 },
- { url = "https://files.pythonhosted.org/packages/e0/42/ad28191ebf983a5d0ecef90bab66baa5a6b18f2bfdef9d0a63b1973d9f75/grpcio-1.76.0-cp312-cp312-win32.whl", hash = "sha256:45d59a649a82df5718fd9527ce775fd66d1af35e6d31abdcdc906a49c6822958", size = 3984750 },
- { url = "https://files.pythonhosted.org/packages/9e/00/7bd478cbb851c04a48baccaa49b75abaa8e4122f7d86da797500cccdd771/grpcio-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:c088e7a90b6017307f423efbb9d1ba97a22aa2170876223f9709e9d1de0b5347", size = 4704003 },
- { url = "https://files.pythonhosted.org/packages/fc/ed/71467ab770effc9e8cef5f2e7388beb2be26ed642d567697bb103a790c72/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2", size = 5807716 },
- { url = "https://files.pythonhosted.org/packages/2c/85/c6ed56f9817fab03fa8a111ca91469941fb514e3e3ce6d793cb8f1e1347b/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468", size = 11821522 },
- { url = "https://files.pythonhosted.org/packages/ac/31/2b8a235ab40c39cbc141ef647f8a6eb7b0028f023015a4842933bc0d6831/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3", size = 6362558 },
- { url = "https://files.pythonhosted.org/packages/bd/64/9784eab483358e08847498ee56faf8ff6ea8e0a4592568d9f68edc97e9e9/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb", size = 7049990 },
- { url = "https://files.pythonhosted.org/packages/2b/94/8c12319a6369434e7a184b987e8e9f3b49a114c489b8315f029e24de4837/grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae", size = 6575387 },
- { url = "https://files.pythonhosted.org/packages/15/0f/f12c32b03f731f4a6242f771f63039df182c8b8e2cf8075b245b409259d4/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77", size = 7166668 },
- { url = "https://files.pythonhosted.org/packages/ff/2d/3ec9ce0c2b1d92dd59d1c3264aaec9f0f7c817d6e8ac683b97198a36ed5a/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03", size = 8124928 },
- { url = "https://files.pythonhosted.org/packages/1a/74/fd3317be5672f4856bcdd1a9e7b5e17554692d3db9a3b273879dc02d657d/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42", size = 7589983 },
- { url = "https://files.pythonhosted.org/packages/45/bb/ca038cf420f405971f19821c8c15bcbc875505f6ffadafe9ffd77871dc4c/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f", size = 3984727 },
- { url = "https://files.pythonhosted.org/packages/41/80/84087dc56437ced7cdd4b13d7875e7439a52a261e3ab4e06488ba6173b0a/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8", size = 4702799 },
- { url = "https://files.pythonhosted.org/packages/b4/46/39adac80de49d678e6e073b70204091e76631e03e94928b9ea4ecf0f6e0e/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62", size = 5808417 },
- { url = "https://files.pythonhosted.org/packages/9c/f5/a4531f7fb8b4e2a60b94e39d5d924469b7a6988176b3422487be61fe2998/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd", size = 11828219 },
- { url = "https://files.pythonhosted.org/packages/4b/1c/de55d868ed7a8bd6acc6b1d6ddc4aa36d07a9f31d33c912c804adb1b971b/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc", size = 6367826 },
- { url = "https://files.pythonhosted.org/packages/59/64/99e44c02b5adb0ad13ab3adc89cb33cb54bfa90c74770f2607eea629b86f/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a", size = 7049550 },
- { url = "https://files.pythonhosted.org/packages/43/28/40a5be3f9a86949b83e7d6a2ad6011d993cbe9b6bd27bea881f61c7788b6/grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba", size = 6575564 },
- { url = "https://files.pythonhosted.org/packages/4b/a9/1be18e6055b64467440208a8559afac243c66a8b904213af6f392dc2212f/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09", size = 7176236 },
- { url = "https://files.pythonhosted.org/packages/0f/55/dba05d3fcc151ce6e81327541d2cc8394f442f6b350fead67401661bf041/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc", size = 8125795 },
- { url = "https://files.pythonhosted.org/packages/4a/45/122df922d05655f63930cf42c9e3f72ba20aadb26c100ee105cad4ce4257/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc", size = 7592214 },
- { url = "https://files.pythonhosted.org/packages/4a/6e/0b899b7f6b66e5af39e377055fb4a6675c9ee28431df5708139df2e93233/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e", size = 4062961 },
- { url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462 },
- { url = "https://files.pythonhosted.org/packages/6e/d5/301e71c7d22a5c7aabf1953dd1106987bd47f883377d528355f898a850f2/grpcio-1.76.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:8ebe63ee5f8fa4296b1b8cfc743f870d10e902ca18afc65c68cf46fd39bb0783", size = 5840371 },
- { url = "https://files.pythonhosted.org/packages/00/55/e3181adccff8808301dd9214b5e03c6db5a404b5ae8a6ec5768a5a65ed63/grpcio-1.76.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:3bf0f392c0b806905ed174dcd8bdd5e418a40d5567a05615a030a5aeddea692d", size = 11840384 },
- { url = "https://files.pythonhosted.org/packages/65/36/db1dfe943bce7180f5b6d9be564366ca1024a005e914a1f10212c24a840b/grpcio-1.76.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b7604868b38c1bfd5cf72d768aedd7db41d78cb6a4a18585e33fb0f9f2363fd", size = 6408765 },
- { url = "https://files.pythonhosted.org/packages/1e/79/a8452764aa4b5ca30a970e514ec2fc5cf75451571793f6b276b6807f67dc/grpcio-1.76.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e6d1db20594d9daba22f90da738b1a0441a7427552cc6e2e3d1297aeddc00378", size = 7076220 },
- { url = "https://files.pythonhosted.org/packages/e0/61/4cca38c4e7bb3ac5a1e0be6cf700a4dd85c61cbd8a9c5e076c224967084e/grpcio-1.76.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d099566accf23d21037f18a2a63d323075bebace807742e4b0ac210971d4dd70", size = 6610195 },
- { url = "https://files.pythonhosted.org/packages/54/3d/3f8bfae264c22c95fa702c35aa2a8105b754b4ace049c66a8b2230c97671/grpcio-1.76.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ebea5cc3aa8ea72e04df9913492f9a96d9348db876f9dda3ad729cfedf7ac416", size = 7193343 },
- { url = "https://files.pythonhosted.org/packages/d1/cd/89f9254782b6cd94aa7c93fde370862877113b7189fb49900eaf9a706c82/grpcio-1.76.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0c37db8606c258e2ee0c56b78c62fc9dee0e901b5dbdcf816c2dd4ad652b8b0c", size = 8161922 },
- { url = "https://files.pythonhosted.org/packages/af/e0/99eb899d7cb9c676afea70ab6d02a72a9e6ce24d0300f625773fafe6d547/grpcio-1.76.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ebebf83299b0cb1721a8859ea98f3a77811e35dce7609c5c963b9ad90728f886", size = 7617951 },
- { url = "https://files.pythonhosted.org/packages/79/26/dca1b2bfaa9981cc28fa995730c80eedb0b86c912c30d1b676f08232e6ab/grpcio-1.76.0-cp39-cp39-win32.whl", hash = "sha256:0aaa82d0813fd4c8e589fac9b65d7dd88702555f702fb10417f96e2a2a6d4c0f", size = 3999306 },
- { url = "https://files.pythonhosted.org/packages/de/d1/fb90564a981eedd3cd87dc6bfd7c249e8a515cfad1ed8e9af73be223cd3b/grpcio-1.76.0-cp39-cp39-win_amd64.whl", hash = "sha256:acab0277c40eff7143c2323190ea57b9ee5fd353d8190ee9652369fae735668a", size = 4708771 },
+sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905, upload-time = "2026-03-30T08:49:10.502Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9d/cd/bb7b7e54084a344c03d68144450da7ddd5564e51a298ae1662de65f48e2d/grpcio-1.80.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:886457a7768e408cdce226ad1ca67d2958917d306523a0e21e1a2fdaa75c9c9c", size = 6050363, upload-time = "2026-03-30T08:46:20.894Z" },
+ { url = "https://files.pythonhosted.org/packages/16/02/1417f5c3460dea65f7a2e3c14e8b31e77f7ffb730e9bfadd89eda7a9f477/grpcio-1.80.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:7b641fc3f1dc647bfd80bd713addc68f6d145956f64677e56d9ebafc0bd72388", size = 12026037, upload-time = "2026-03-30T08:46:25.144Z" },
+ { url = "https://files.pythonhosted.org/packages/43/98/c910254eedf2cae368d78336a2de0678e66a7317d27c02522392f949b5c6/grpcio-1.80.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:33eb763f18f006dc7fee1e69831d38d23f5eccd15b2e0f92a13ee1d9242e5e02", size = 6602306, upload-time = "2026-03-30T08:46:27.593Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/f8/88ca4e78c077b2b2113d95da1e1ab43efd43d723c9a0397d26529c2c1a56/grpcio-1.80.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:52d143637e3872633fc7dd7c3c6a1c84e396b359f3a72e215f8bf69fd82084fc", size = 7301535, upload-time = "2026-03-30T08:46:29.556Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/96/f28660fe2fe0f153288bf4a04e4910b7309d442395135c88ed4f5b3b8b40/grpcio-1.80.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c51bf8ac4575af2e0678bccfb07e47321fc7acb5049b4482832c5c195e04e13a", size = 6808669, upload-time = "2026-03-30T08:46:31.984Z" },
+ { url = "https://files.pythonhosted.org/packages/47/eb/3f68a5e955779c00aeef23850e019c1c1d0e032d90633ba49c01ad5a96e0/grpcio-1.80.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:50a9871536d71c4fba24ee856abc03a87764570f0c457dd8db0b4018f379fed9", size = 7409489, upload-time = "2026-03-30T08:46:34.684Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/a7/d2f681a4bfb881be40659a309771f3bdfbfdb1190619442816c3f0ffc079/grpcio-1.80.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a72d84ad0514db063e21887fbacd1fd7acb4d494a564cae22227cd45c7fbf199", size = 8423167, upload-time = "2026-03-30T08:46:36.833Z" },
+ { url = "https://files.pythonhosted.org/packages/97/8a/29b4589c204959aa35ce5708400a05bba72181807c45c47b3ec000c39333/grpcio-1.80.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f7691a6788ad9196872f95716df5bc643ebba13c97140b7a5ee5c8e75d1dea81", size = 7846761, upload-time = "2026-03-30T08:46:40.091Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/d2/ed143e097230ee121ac5848f6ff14372dba91289b10b536d54fb1b7cbae7/grpcio-1.80.0-cp310-cp310-win32.whl", hash = "sha256:46c2390b59d67f84e882694d489f5b45707c657832d7934859ceb8c33f467069", size = 4156534, upload-time = "2026-03-30T08:46:42.026Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/c9/df8279bb49b29409995e95efa85b72973d62f8aeff89abee58c91f393710/grpcio-1.80.0-cp310-cp310-win_amd64.whl", hash = "sha256:dc053420fc75749c961e2a4c906398d7c15725d36ccc04ae6d16093167223b58", size = 4889869, upload-time = "2026-03-30T08:46:44.219Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/db/1d56e5f5823257b291962d6c0ce106146c6447f405b60b234c4f222a7cde/grpcio-1.80.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a", size = 6055009, upload-time = "2026-03-30T08:46:46.265Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/18/c83f3cad64c5ca63bca7e91e5e46b0d026afc5af9d0a9972472ceba294b3/grpcio-1.80.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060", size = 12035295, upload-time = "2026-03-30T08:46:49.099Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/8e/e14966b435be2dda99fbe89db9525ea436edc79780431a1c2875a3582644/grpcio-1.80.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2", size = 6610297, upload-time = "2026-03-30T08:46:52.123Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/26/d5eb38f42ce0e3fdc8174ea4d52036ef8d58cc4426cb800f2610f625dd75/grpcio-1.80.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21", size = 7300208, upload-time = "2026-03-30T08:46:54.859Z" },
+ { url = "https://files.pythonhosted.org/packages/25/51/bd267c989f85a17a5b3eea65a6feb4ff672af41ca614e5a0279cc0ea381c/grpcio-1.80.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab", size = 6813442, upload-time = "2026-03-30T08:46:57.056Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/d9/d80eef735b19e9169e30164bbf889b46f9df9127598a83d174eb13a48b26/grpcio-1.80.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1", size = 7414743, upload-time = "2026-03-30T08:46:59.682Z" },
+ { url = "https://files.pythonhosted.org/packages/de/f2/567f5bd5054398ed6b0509b9a30900376dcf2786bd936812098808b49d8d/grpcio-1.80.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106", size = 8426046, upload-time = "2026-03-30T08:47:02.474Z" },
+ { url = "https://files.pythonhosted.org/packages/62/29/73ef0141b4732ff5eacd68430ff2512a65c004696997f70476a83e548e7e/grpcio-1.80.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6", size = 7851641, upload-time = "2026-03-30T08:47:05.462Z" },
+ { url = "https://files.pythonhosted.org/packages/46/69/abbfa360eb229a8623bab5f5a4f8105e445bd38ce81a89514ba55d281ad0/grpcio-1.80.0-cp311-cp311-win32.whl", hash = "sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440", size = 4154368, upload-time = "2026-03-30T08:47:08.027Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/d4/ae92206d01183b08613e846076115f5ac5991bae358d2a749fa864da5699/grpcio-1.80.0-cp311-cp311-win_amd64.whl", hash = "sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9", size = 4894235, upload-time = "2026-03-30T08:47:10.839Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/e8/a2b749265eb3415abc94f2e619bbd9e9707bebdda787e61c593004ec927a/grpcio-1.80.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0", size = 6015616, upload-time = "2026-03-30T08:47:13.428Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/97/b1282161a15d699d1e90c360df18d19165a045ce1c343c7f313f5e8a0b77/grpcio-1.80.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2", size = 12014204, upload-time = "2026-03-30T08:47:15.873Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/5e/d319c6e997b50c155ac5a8cb12f5173d5b42677510e886d250d50264949d/grpcio-1.80.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de", size = 6563866, upload-time = "2026-03-30T08:47:18.588Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/f6/fdd975a2cb4d78eb67769a7b3b3830970bfa2e919f1decf724ae4445f42c/grpcio-1.80.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921", size = 7273060, upload-time = "2026-03-30T08:47:21.113Z" },
+ { url = "https://files.pythonhosted.org/packages/db/f0/a3deb5feba60d9538a962913e37bd2e69a195f1c3376a3dd44fe0427e996/grpcio-1.80.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411", size = 6782121, upload-time = "2026-03-30T08:47:23.827Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/84/36c6dcfddc093e108141f757c407902a05085e0c328007cb090d56646cdf/grpcio-1.80.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd", size = 7383811, upload-time = "2026-03-30T08:47:26.517Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/ef/f3a77e3dc5b471a0ec86c564c98d6adfa3510d38f8ee99010410858d591e/grpcio-1.80.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f", size = 8393860, upload-time = "2026-03-30T08:47:29.439Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/8d/9d4d27ed7f33d109c50d6b5ce578a9914aa68edab75d65869a17e630a8d1/grpcio-1.80.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f", size = 7830132, upload-time = "2026-03-30T08:47:33.254Z" },
+ { url = "https://files.pythonhosted.org/packages/14/e4/9990b41c6d7a44e1e9dee8ac11d7a9802ba1378b40d77468a7761d1ad288/grpcio-1.80.0-cp312-cp312-win32.whl", hash = "sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193", size = 4140904, upload-time = "2026-03-30T08:47:35.319Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/2c/296f6138caca1f4b92a31ace4ae1b87dab692fc16a7a3417af3bb3c805bf/grpcio-1.80.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff", size = 4880944, upload-time = "2026-03-30T08:47:37.831Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/3a/7c3c25789e3f069e581dc342e03613c5b1cb012c4e8c7d9d5cf960a75856/grpcio-1.80.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad", size = 6017243, upload-time = "2026-03-30T08:47:40.075Z" },
+ { url = "https://files.pythonhosted.org/packages/04/19/21a9806eb8240e174fd1ab0cd5b9aa948bb0e05c2f2f55f9d5d7405e6d08/grpcio-1.80.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0", size = 12010840, upload-time = "2026-03-30T08:47:43.11Z" },
+ { url = "https://files.pythonhosted.org/packages/18/3a/23347d35f76f639e807fb7a36fad3068aed100996849a33809591f26eca6/grpcio-1.80.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f", size = 6567644, upload-time = "2026-03-30T08:47:46.806Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/40/96e07ecb604a6a67ae6ab151e3e35b132875d98bc68ec65f3e5ab3e781d7/grpcio-1.80.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6", size = 7277830, upload-time = "2026-03-30T08:47:49.643Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/e2/da1506ecea1f34a5e365964644b35edef53803052b763ca214ba3870c856/grpcio-1.80.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140", size = 6783216, upload-time = "2026-03-30T08:47:52.817Z" },
+ { url = "https://files.pythonhosted.org/packages/44/83/3b20ff58d0c3b7f6caaa3af9a4174d4023701df40a3f39f7f1c8e7c48f9d/grpcio-1.80.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d", size = 7385866, upload-time = "2026-03-30T08:47:55.687Z" },
+ { url = "https://files.pythonhosted.org/packages/47/45/55c507599c5520416de5eefecc927d6a0d7af55e91cfffb2e410607e5744/grpcio-1.80.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7", size = 8391602, upload-time = "2026-03-30T08:47:58.303Z" },
+ { url = "https://files.pythonhosted.org/packages/10/bb/dd06f4c24c01db9cf11341b547d0a016b2c90ed7dbbb086a5710df7dd1d7/grpcio-1.80.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7", size = 7826752, upload-time = "2026-03-30T08:48:01.311Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/1e/9d67992ba23371fd63d4527096eb8c6b76d74d52b500df992a3343fd7251/grpcio-1.80.0-cp313-cp313-win32.whl", hash = "sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294", size = 4142310, upload-time = "2026-03-30T08:48:04.594Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/e6/283326a27da9e2c3038bc93eeea36fb118ce0b2d03922a9cda6688f53c5b/grpcio-1.80.0-cp313-cp313-win_amd64.whl", hash = "sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50", size = 4882833, upload-time = "2026-03-30T08:48:07.363Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/6d/e65307ce20f5a09244ba9e9d8476e99fb039de7154f37fb85f26978b59c3/grpcio-1.80.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e", size = 6017376, upload-time = "2026-03-30T08:48:10.005Z" },
+ { url = "https://files.pythonhosted.org/packages/69/10/9cef5d9650c72625a699c549940f0abb3c4bfdb5ed45a5ce431f92f31806/grpcio-1.80.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f", size = 12018133, upload-time = "2026-03-30T08:48:12.927Z" },
+ { url = "https://files.pythonhosted.org/packages/04/82/983aabaad82ba26113caceeb9091706a0696b25da004fe3defb5b346e15b/grpcio-1.80.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9", size = 6574748, upload-time = "2026-03-30T08:48:16.386Z" },
+ { url = "https://files.pythonhosted.org/packages/07/d7/031666ef155aa0bf399ed7e19439656c38bbd143779ae0861b038ce82abd/grpcio-1.80.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14", size = 7277711, upload-time = "2026-03-30T08:48:19.627Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/43/f437a78f7f4f1d311804189e8f11fb311a01049b2e08557c1068d470cb2e/grpcio-1.80.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05", size = 6785372, upload-time = "2026-03-30T08:48:22.373Z" },
+ { url = "https://files.pythonhosted.org/packages/93/3d/f6558e9c6296cb4227faa5c43c54a34c68d32654b829f53288313d16a86e/grpcio-1.80.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1", size = 7395268, upload-time = "2026-03-30T08:48:25.638Z" },
+ { url = "https://files.pythonhosted.org/packages/06/21/0fdd77e84720b08843c371a2efa6f2e19dbebf56adc72df73d891f5506f0/grpcio-1.80.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f", size = 8392000, upload-time = "2026-03-30T08:48:28.974Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/68/67f4947ed55d2e69f2cc199ab9fd85e0a0034d813bbeef84df6d2ba4d4b7/grpcio-1.80.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e", size = 7828477, upload-time = "2026-03-30T08:48:32.054Z" },
+ { url = "https://files.pythonhosted.org/packages/44/b6/8d4096691b2e385e8271911a0de4f35f0a6c7d05aff7098e296c3de86939/grpcio-1.80.0-cp314-cp314-win32.whl", hash = "sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae", size = 4218563, upload-time = "2026-03-30T08:48:34.538Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/8c/bbe6baf2557262834f2070cf668515fa308b2d38a4bbf771f8f7872a7036/grpcio-1.80.0-cp314-cp314-win_amd64.whl", hash = "sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f", size = 5019457, upload-time = "2026-03-30T08:48:37.308Z" },
]
[[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 }
+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 },
+ { 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 = "httpcore"
version = "1.0.9"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 }
+sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 },
+ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
]
[[package]]
name = "httpx"
version = "0.28.1"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "anyio" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 }
+sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
+ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
]
[[package]]
name = "identify"
-version = "2.6.15"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311 }
+version = "2.6.18"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/46/c4/7fb4db12296cdb11893d61c92048fe617ee853f8523b9b296ac03b43757e/identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd", size = 99580, upload-time = "2026-03-15T18:39:50.319Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183 },
+ { url = "https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737", size = 99394, upload-time = "2026-03-15T18:39:48.915Z" },
]
[[package]]
name = "idna"
version = "3.11"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 },
-]
-
-[[package]]
-name = "importlib-metadata"
-version = "8.7.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "zipp" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641 }
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656 },
+ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
]
[[package]]
name = "inflect"
version = "7.5.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "more-itertools" },
{ name = "typeguard" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/78/c6/943357d44a21fd995723d07ccaddd78023eace03c1846049a2645d4324a3/inflect-7.5.0.tar.gz", hash = "sha256:faf19801c3742ed5a05a8ce388e0d8fe1a07f8d095c82201eb904f5d27ad571f", size = 73751 }
+sdist = { url = "https://files.pythonhosted.org/packages/78/c6/943357d44a21fd995723d07ccaddd78023eace03c1846049a2645d4324a3/inflect-7.5.0.tar.gz", hash = "sha256:faf19801c3742ed5a05a8ce388e0d8fe1a07f8d095c82201eb904f5d27ad571f", size = 73751, upload-time = "2024-12-28T17:11:18.897Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/8a/eb/427ed2b20a38a4ee29f24dbe4ae2dafab198674fe9a85e3d6adf9e5f5f41/inflect-7.5.0-py3-none-any.whl", hash = "sha256:2aea70e5e70c35d8350b8097396ec155ffd68def678c7ff97f51aa69c1d92344", size = 35197 },
+ { url = "https://files.pythonhosted.org/packages/8a/eb/427ed2b20a38a4ee29f24dbe4ae2dafab198674fe9a85e3d6adf9e5f5f41/inflect-7.5.0-py3-none-any.whl", hash = "sha256:2aea70e5e70c35d8350b8097396ec155ffd68def678c7ff97f51aa69c1d92344", size = 35197, upload-time = "2024-12-28T17:11:15.931Z" },
]
[[package]]
name = "isort"
-version = "6.1.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-dependencies = [
- { name = "importlib-metadata", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/1e/82/fa43935523efdfcce6abbae9da7f372b627b27142c3419fcf13bf5b0c397/isort-6.1.0.tar.gz", hash = "sha256:9b8f96a14cfee0677e78e941ff62f03769a06d412aabb9e2a90487b3b7e8d481", size = 824325 }
+version = "8.0.1"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592, upload-time = "2026-02-28T10:08:20.685Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/7f/cc/9b681a170efab4868a032631dea1e8446d8ec718a7f657b94d49d1a12643/isort-6.1.0-py3-none-any.whl", hash = "sha256:58d8927ecce74e5087aef019f778d4081a3b6c98f15a80ba35782ca8a2097784", size = 94329 },
-]
-
-[[package]]
-name = "isort"
-version = "7.0.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/63/53/4f3c058e3bace40282876f9b553343376ee687f3c35a525dc79dbd450f88/isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187", size = 805049 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7f/ed/e3705d6d02b4f7aea715a353c8ce193efd0b5db13e204df895d38734c244/isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1", size = 94672 },
+ { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" },
]
[[package]]
name = "jinja2"
version = "3.1.6"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "markupsafe" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 },
-]
-
-[[package]]
-name = "jsonschema"
-version = "4.25.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-dependencies = [
- { name = "attrs", marker = "python_full_version < '3.10'" },
- { name = "jsonschema-specifications", marker = "python_full_version < '3.10'" },
- { name = "referencing", version = "0.36.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "rpds-py", version = "0.27.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342 }
+sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040 },
+ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
]
[[package]]
name = "jsonschema"
version = "4.26.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "attrs", marker = "python_full_version >= '3.10'" },
- { name = "jsonschema-specifications", marker = "python_full_version >= '3.10'" },
- { name = "referencing", version = "0.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "attrs" },
+ { name = "jsonschema-specifications" },
+ { name = "referencing" },
+ { name = "rpds-py" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583 }
+sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630 },
+ { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" },
]
[[package]]
name = "jsonschema-specifications"
version = "2025.9.1"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "referencing", version = "0.36.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "referencing", version = "0.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "referencing" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 }
+sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 },
+ { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
]
[[package]]
name = "markupsafe"
version = "3.0.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631 },
- { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057 },
- { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050 },
- { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681 },
- { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705 },
- { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524 },
- { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282 },
- { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745 },
- { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571 },
- { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056 },
- { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932 },
- { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631 },
- { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058 },
- { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287 },
- { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940 },
- { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887 },
- { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692 },
- { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471 },
- { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923 },
- { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572 },
- { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077 },
- { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876 },
- { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615 },
- { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020 },
- { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332 },
- { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947 },
- { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962 },
- { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760 },
- { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529 },
- { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015 },
- { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540 },
- { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105 },
- { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906 },
- { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622 },
- { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029 },
- { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374 },
- { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980 },
- { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990 },
- { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784 },
- { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588 },
- { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041 },
- { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 },
- { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 },
- { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 },
- { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658 },
- { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066 },
- { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639 },
- { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569 },
- { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284 },
- { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801 },
- { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769 },
- { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642 },
- { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 },
- { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 },
- { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 },
- { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619 },
- { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029 },
- { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408 },
- { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005 },
- { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048 },
- { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821 },
- { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606 },
- { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043 },
- { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 },
- { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 },
- { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 },
- { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661 },
- { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069 },
- { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670 },
- { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598 },
- { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261 },
- { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835 },
- { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733 },
- { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672 },
- { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 },
- { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 },
- { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 },
- { url = "https://files.pythonhosted.org/packages/56/23/0d8c13a44bde9154821586520840643467aee574d8ce79a17da539ee7fed/markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26", size = 11623 },
- { url = "https://files.pythonhosted.org/packages/fd/23/07a2cb9a8045d5f3f0890a8c3bc0859d7a47bfd9a560b563899bec7b72ed/markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc", size = 12049 },
- { url = "https://files.pythonhosted.org/packages/bc/e4/6be85eb81503f8e11b61c0b6369b6e077dcf0a74adbd9ebf6b349937b4e9/markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c", size = 21923 },
- { url = "https://files.pythonhosted.org/packages/6f/bc/4dc914ead3fe6ddaef035341fee0fc956949bbd27335b611829292b89ee2/markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42", size = 20543 },
- { url = "https://files.pythonhosted.org/packages/89/6e/5fe81fbcfba4aef4093d5f856e5c774ec2057946052d18d168219b7bd9f9/markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b", size = 20585 },
- { url = "https://files.pythonhosted.org/packages/f6/f6/e0e5a3d3ae9c4020f696cd055f940ef86b64fe88de26f3a0308b9d3d048c/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758", size = 21387 },
- { url = "https://files.pythonhosted.org/packages/c8/25/651753ef4dea08ea790f4fbb65146a9a44a014986996ca40102e237aa49a/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2", size = 20133 },
- { url = "https://files.pythonhosted.org/packages/dc/0a/c3cf2b4fef5f0426e8a6d7fce3cb966a17817c568ce59d76b92a233fdbec/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d", size = 20588 },
- { url = "https://files.pythonhosted.org/packages/cd/1b/a7782984844bd519ad4ffdbebbba2671ec5d0ebbeac34736c15fb86399e8/markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7", size = 14566 },
- { url = "https://files.pythonhosted.org/packages/18/1f/8d9c20e1c9440e215a44be5ab64359e207fcb4f675543f1cf9a2a7f648d0/markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e", size = 15053 },
- { url = "https://files.pythonhosted.org/packages/4e/d3/fe08482b5cd995033556d45041a4f4e76e7f0521112a9c9991d40d39825f/markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8", size = 13928 },
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" },
+ { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" },
+ { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" },
+ { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" },
+ { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" },
+ { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" },
+ { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" },
+ { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" },
+ { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" },
+ { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" },
+ { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" },
+ { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" },
+ { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
+ { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
+ { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
+ { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
+ { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
+ { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
+ { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
+ { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
+ { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
+ { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
+ { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
+ { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
+ { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
+ { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
+ { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
+ { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
+ { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
+ { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
+ { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
+ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
]
[[package]]
name = "more-itertools"
-version = "10.8.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431 }
+version = "11.0.2"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/f7/139d22fef48ac78127d18e01d80cf1be40236ae489769d17f35c3d425293/more_itertools-11.0.2.tar.gz", hash = "sha256:392a9e1e362cbc106a2457d37cabf9b36e5e12efd4ebff1654630e76597df804", size = 144659, upload-time = "2026-04-09T15:01:33.297Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667 },
+ { url = "https://files.pythonhosted.org/packages/cb/98/6af411189d9413534c3eb691182bff1f5c6d44ed2f93f2edfe52a1bbceb8/more_itertools-11.0.2-py3-none-any.whl", hash = "sha256:6e35b35f818b01f691643c6c611bc0902f2e92b46c18fffa77ae1e7c46e912e4", size = 71939, upload-time = "2026-04-09T15:01:32.21Z" },
]
[[package]]
name = "mypy-extensions"
version = "1.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343 }
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 },
+ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
]
[[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 }
+version = "1.10.0"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 },
+ { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
]
[[package]]
name = "opentelemetry-api"
source = { editable = "opentelemetry-api" }
dependencies = [
- { name = "importlib-metadata" },
{ name = "typing-extensions" },
]
[package.metadata]
-requires-dist = [
- { name = "importlib-metadata", specifier = ">=6.0,<8.8.0" },
- { name = "typing-extensions", specifier = ">=4.5.0" },
-]
+requires-dist = [{ name = "typing-extensions", specifier = ">=4.5.0" }]
[[package]]
name = "opentelemetry-codegen-json"
+version = "0.0.0"
source = { editable = "codegen/opentelemetry-codegen-json" }
dependencies = [
{ name = "protobuf" },
- { name = "types-protobuf", version = "6.32.1.20251210", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "types-protobuf", version = "6.32.1.20260221", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "types-protobuf" },
]
[package.metadata]
@@ -920,16 +828,16 @@ requires-dist = [
[[package]]
name = "opentelemetry-exporter-credential-provider-gcp"
-version = "0.60b0"
-source = { registry = "https://pypi.org/simple" }
+version = "0.62b0"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "google-auth" },
{ name = "grpcio" },
{ name = "requests" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/58/80/7dd763dbff05328804d83aa31507e18722b31fec18e1f6ec92bd8fc2894d/opentelemetry_exporter_credential_provider_gcp-0.60b0.tar.gz", hash = "sha256:90b010ef9e14100301a6e95db41dc3278955de029a46501588a4a2a446e9eb31", size = 7162 }
+sdist = { url = "https://files.pythonhosted.org/packages/4f/23/d048d312b0f126adf271c2e65613cd8a2a5c8be1f87d56e6203aa216a279/opentelemetry_exporter_credential_provider_gcp-0.62b0.tar.gz", hash = "sha256:14c3951ff1ac41178d41fd19c1e011ee8e3727d50dc7f1077d675434430362c3", size = 7188, upload-time = "2026-04-09T14:40:21.035Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/af/7a/3ff5e7db388eebb31c4c94f7511488834adede24cd0913c0ef078d9f8fac/opentelemetry_exporter_credential_provider_gcp-0.60b0-py3-none-any.whl", hash = "sha256:80bab18f7a2cfe1b7c3fcc21999fc51a5675f3c67b07304adcde7bdfe0a50c5d", size = 8339 },
+ { url = "https://files.pythonhosted.org/packages/45/a4/d59629684cef86a9da39241edd25dc2dff849d08def6200d50c951e1dd2a/opentelemetry_exporter_credential_provider_gcp-0.62b0-py3-none-any.whl", hash = "sha256:56d15d6486c40d9f958f34b1e9b4b6c9122789f5d136fc1381948b7b4b3af3ca", size = 8343, upload-time = "2026-04-09T14:39:18.399Z" },
]
[[package]]
@@ -947,6 +855,20 @@ requires-dist = [
]
[[package]]
+name = "opentelemetry-exporter-otlp-json-common"
+source = { editable = "exporter/opentelemetry-exporter-otlp-json-common" }
+dependencies = [
+ { name = "opentelemetry-proto-json" },
+ { name = "opentelemetry-sdk" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "opentelemetry-proto-json", editable = "opentelemetry-proto-json" },
+ { name = "opentelemetry-sdk", editable = "opentelemetry-sdk" },
+]
+
+[[package]]
name = "opentelemetry-exporter-otlp-proto-common"
source = { editable = "exporter/opentelemetry-exporter-otlp-proto-common" }
dependencies = [
@@ -1097,6 +1019,7 @@ source = { virtual = "." }
dependencies = [
{ name = "opentelemetry-api" },
{ name = "opentelemetry-codegen-json" },
+ { name = "opentelemetry-exporter-otlp-json-common" },
{ name = "opentelemetry-exporter-otlp-proto-common" },
{ name = "opentelemetry-exporter-otlp-proto-grpc" },
{ name = "opentelemetry-exporter-otlp-proto-http" },
@@ -1113,20 +1036,18 @@ dependencies = [
[package.dev-dependencies]
dev = [
- { name = "datamodel-code-generator", version = "0.45.0", source = { registry = "https://pypi.org/simple" }, extra = ["http", "ruff"], marker = "python_full_version < '3.10'" },
- { name = "datamodel-code-generator", version = "0.53.0", source = { registry = "https://pypi.org/simple" }, extra = ["http", "ruff"], marker = "python_full_version >= '3.10'" },
- { name = "pre-commit", version = "4.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "pre-commit", version = "4.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "tox", version = "4.30.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "tox", version = "4.32.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "tox-uv", version = "1.28.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "tox-uv", version = "1.29.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "datamodel-code-generator", extra = ["http", "ruff"] },
+ { name = "pre-commit" },
+ { name = "towncrier" },
+ { name = "tox" },
+ { name = "tox-uv" },
]
[package.metadata]
requires-dist = [
{ name = "opentelemetry-api", editable = "opentelemetry-api" },
{ name = "opentelemetry-codegen-json", editable = "codegen/opentelemetry-codegen-json" },
+ { name = "opentelemetry-exporter-otlp-json-common", editable = "exporter/opentelemetry-exporter-otlp-json-common" },
{ name = "opentelemetry-exporter-otlp-proto-common", editable = "exporter/opentelemetry-exporter-otlp-proto-common" },
{ name = "opentelemetry-exporter-otlp-proto-grpc", editable = "exporter/opentelemetry-exporter-otlp-proto-grpc" },
{ name = "opentelemetry-exporter-otlp-proto-http", editable = "exporter/opentelemetry-exporter-otlp-proto-http" },
@@ -1146,6 +1067,7 @@ dev = [
{ name = "datamodel-code-generator", extras = ["http"] },
{ name = "datamodel-code-generator", extras = ["ruff"] },
{ name = "pre-commit" },
+ { name = "towncrier" },
{ name = "tox" },
{ name = "tox-uv", specifier = ">=1" },
]
@@ -1161,8 +1083,7 @@ dependencies = [
[package.optional-dependencies]
file-configuration = [
- { name = "jsonschema", version = "4.25.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "jsonschema", version = "4.26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "jsonschema" },
{ name = "pyyaml" },
]
@@ -1197,6 +1118,7 @@ dependencies = [
{ name = "asgiref" },
{ name = "opentelemetry-api" },
{ name = "opentelemetry-sdk" },
+ { name = "requests" },
]
[package.metadata]
@@ -1204,1074 +1126,768 @@ requires-dist = [
{ name = "asgiref", specifier = "~=3.0" },
{ name = "opentelemetry-api", editable = "opentelemetry-api" },
{ name = "opentelemetry-sdk", editable = "opentelemetry-sdk" },
+ { name = "requests", specifier = "~=2.28" },
]
[[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 }
+version = "26.0"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 },
+ { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" },
]
[[package]]
name = "pathspec"
version = "1.0.4"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206 },
-]
-
-[[package]]
-name = "platformdirs"
-version = "4.4.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634 }
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654 },
+ { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" },
]
[[package]]
name = "platformdirs"
-version = "4.5.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715 }
+version = "4.9.6"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731 },
+ { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" },
]
[[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 }
+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 },
+ { 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" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-dependencies = [
- { name = "cfgv", version = "3.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "identify", marker = "python_full_version < '3.10'" },
- { name = "nodeenv", marker = "python_full_version < '3.10'" },
- { name = "pyyaml", marker = "python_full_version < '3.10'" },
- { name = "virtualenv", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792 }
-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 },
-]
-
-[[package]]
-name = "pre-commit"
-version = "4.5.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
+version = "4.5.1"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "cfgv", version = "3.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "identify", marker = "python_full_version >= '3.10'" },
- { name = "nodeenv", marker = "python_full_version >= '3.10'" },
- { name = "pyyaml", marker = "python_full_version >= '3.10'" },
- { name = "virtualenv", marker = "python_full_version >= '3.10'" },
+ { name = "cfgv" },
+ { name = "identify" },
+ { name = "nodeenv" },
+ { name = "pyyaml" },
+ { name = "virtualenv" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/f4/9b/6a4ffb4ed980519da959e1cf3122fc6cb41211daa58dbae1c73c0e519a37/pre_commit-4.5.0.tar.gz", hash = "sha256:dc5a065e932b19fc1d4c653c6939068fe54325af8e741e74e88db4d28a4dd66b", size = 198428 }
+sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/5d/c4/b2d28e9d2edf4f1713eb3c29307f1a63f3d67cf09bdda29715a36a68921a/pre_commit-4.5.0-py2.py3-none-any.whl", hash = "sha256:25e2ce09595174d9c97860a95609f9f852c0614ba602de3561e267547f2335e1", size = 226429 },
+ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" },
]
[[package]]
name = "prometheus-client"
-version = "0.23.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/23/53/3edb5d68ecf6b38fcbcc1ad28391117d2a322d9a1a3eff04bfdb184d8c3b/prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce", size = 80481 }
+version = "0.25.0"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/1b/fb/d9aa83ffe43ce1f19e557c0971d04b90561b0cfd50762aafb01968285553/prometheus_client-0.25.0.tar.gz", hash = "sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28", size = 86035, upload-time = "2026-04-09T19:53:42.359Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145 },
+ { url = "https://files.pythonhosted.org/packages/8d/9b/d4b1e644385499c8346fa9b622a3f030dce14cd6ef8a1871c221a17a67e7/prometheus_client-0.25.0-py3-none-any.whl", hash = "sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1", size = 64154, upload-time = "2026-04-09T19:53:41.324Z" },
]
[[package]]
name = "protobuf"
-version = "6.33.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/34/44/e49ecff446afeec9d1a66d6bbf9adc21e3c7cea7803a920ca3773379d4f6/protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4", size = 444296 }
+version = "6.33.6"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/bc/91/1e3a34881a88697a7354ffd177e8746e97a722e5e8db101544b47e84afb1/protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d", size = 425603 },
- { url = "https://files.pythonhosted.org/packages/64/20/4d50191997e917ae13ad0a235c8b42d8c1ab9c3e6fd455ca16d416944355/protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4", size = 436930 },
- { url = "https://files.pythonhosted.org/packages/b2/ca/7e485da88ba45c920fb3f50ae78de29ab925d9e54ef0de678306abfbb497/protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43", size = 427621 },
- { url = "https://files.pythonhosted.org/packages/7d/4f/f743761e41d3b2b2566748eb76bbff2b43e14d5fcab694f494a16458b05f/protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e", size = 324460 },
- { url = "https://files.pythonhosted.org/packages/b1/fa/26468d00a92824020f6f2090d827078c09c9c587e34cbfd2d0c7911221f8/protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872", size = 339168 },
- { url = "https://files.pythonhosted.org/packages/56/13/333b8f421738f149d4fe5e49553bc2a2ab75235486259f689b4b91f96cec/protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f", size = 323270 },
- { url = "https://files.pythonhosted.org/packages/87/85/5c1115e68fd34e8ada6fa75974b4c778a298a3c7170575b49efc1eb99dd2/protobuf-6.33.2-cp39-cp39-win32.whl", hash = "sha256:7109dcc38a680d033ffb8bf896727423528db9163be1b6a02d6a49606dcadbfe", size = 425692 },
- { url = "https://files.pythonhosted.org/packages/c5/74/18d9de7fd3c41a8b4808d6268515b320abae003423da1b1319f71bdf0779/protobuf-6.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:2981c58f582f44b6b13173e12bb8656711189c2a70250845f264b877f00b1913", size = 436932 },
- { url = "https://files.pythonhosted.org/packages/0e/15/4f02896cc3df04fc465010a4c6a0cd89810f54617a32a70ef531ed75d61c/protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c", size = 170501 },
+ { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" },
+ { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" },
+ { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" },
]
[[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 }
+version = "0.6.3"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 },
+ { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" },
]
[[package]]
name = "pyasn1-modules"
version = "0.4.2"
-source = { registry = "https://pypi.org/simple" }
+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 }
+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 },
+ { 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 = "pycparser"
+version = "3.0"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
]
[[package]]
name = "pydantic"
version = "2.12.5"
-source = { registry = "https://pypi.org/simple" }
+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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591 }
+sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580 },
+ { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" },
]
[[package]]
name = "pydantic-core"
version = "2.41.5"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298 },
- { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475 },
- { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815 },
- { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567 },
- { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442 },
- { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956 },
- { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253 },
- { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050 },
- { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178 },
- { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833 },
- { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156 },
- { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378 },
- { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622 },
- { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873 },
- { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826 },
- { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869 },
- { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890 },
- { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740 },
- { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021 },
- { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378 },
- { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761 },
- { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303 },
- { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355 },
- { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875 },
- { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549 },
- { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305 },
- { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902 },
- { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990 },
- { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003 },
- { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200 },
- { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578 },
- { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504 },
- { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816 },
- { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366 },
- { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698 },
- { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603 },
- { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591 },
- { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068 },
- { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908 },
- { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145 },
- { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179 },
- { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403 },
- { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206 },
- { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307 },
- { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258 },
- { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917 },
- { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186 },
- { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164 },
- { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146 },
- { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788 },
- { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133 },
- { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852 },
- { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679 },
- { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766 },
- { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005 },
- { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622 },
- { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725 },
- { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040 },
- { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691 },
- { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897 },
- { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302 },
- { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877 },
- { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680 },
- { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960 },
- { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102 },
- { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039 },
- { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126 },
- { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489 },
- { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288 },
- { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255 },
- { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760 },
- { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092 },
- { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385 },
- { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832 },
- { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585 },
- { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078 },
- { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914 },
- { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560 },
- { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244 },
- { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955 },
- { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906 },
- { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607 },
- { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769 },
- { url = "https://files.pythonhosted.org/packages/54/db/160dffb57ed9a3705c4cbcbff0ac03bdae45f1ca7d58ab74645550df3fbd/pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf", size = 2107999 },
- { url = "https://files.pythonhosted.org/packages/a3/7d/88e7de946f60d9263cc84819f32513520b85c0f8322f9b8f6e4afc938383/pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5", size = 1929745 },
- { url = "https://files.pythonhosted.org/packages/d5/c2/aef51e5b283780e85e99ff19db0f05842d2d4a8a8cd15e63b0280029b08f/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d", size = 1920220 },
- { url = "https://files.pythonhosted.org/packages/c7/97/492ab10f9ac8695cd76b2fdb24e9e61f394051df71594e9bcc891c9f586e/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60", size = 2067296 },
- { url = "https://files.pythonhosted.org/packages/ec/23/984149650e5269c59a2a4c41d234a9570adc68ab29981825cfaf4cfad8f4/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82", size = 2231548 },
- { url = "https://files.pythonhosted.org/packages/71/0c/85bcbb885b9732c28bec67a222dbed5ed2d77baee1f8bba2002e8cd00c5c/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5", size = 2362571 },
- { url = "https://files.pythonhosted.org/packages/c0/4a/412d2048be12c334003e9b823a3fa3d038e46cc2d64dd8aab50b31b65499/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3", size = 2068175 },
- { url = "https://files.pythonhosted.org/packages/73/f4/c58b6a776b502d0a5540ad02e232514285513572060f0d78f7832ca3c98b/pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425", size = 2177203 },
- { url = "https://files.pythonhosted.org/packages/ed/ae/f06ea4c7e7a9eead3d165e7623cd2ea0cb788e277e4f935af63fc98fa4e6/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504", size = 2148191 },
- { url = "https://files.pythonhosted.org/packages/c1/57/25a11dcdc656bf5f8b05902c3c2934ac3ea296257cc4a3f79a6319e61856/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5", size = 2343907 },
- { url = "https://files.pythonhosted.org/packages/96/82/e33d5f4933d7a03327c0c43c65d575e5919d4974ffc026bc917a5f7b9f61/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3", size = 2322174 },
- { url = "https://files.pythonhosted.org/packages/81/45/4091be67ce9f469e81656f880f3506f6a5624121ec5eb3eab37d7581897d/pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460", size = 1990353 },
- { url = "https://files.pythonhosted.org/packages/44/8a/a98aede18db6e9cd5d66bcacd8a409fcf8134204cdede2e7de35c5a2c5ef/pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b", size = 2015698 },
- { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351 },
- { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363 },
- { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615 },
- { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369 },
- { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218 },
- { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951 },
- { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428 },
- { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009 },
- { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980 },
- { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865 },
- { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256 },
- { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762 },
- { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141 },
- { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317 },
- { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992 },
- { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302 },
+sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" },
+ { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" },
+ { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" },
+ { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" },
+ { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" },
+ { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" },
+ { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" },
+ { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" },
+ { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" },
+ { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" },
+ { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
+ { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
+ { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" },
+ { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" },
+ { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" },
+ { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" },
+ { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" },
+ { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" },
+ { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
+ { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
+ { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" },
+ { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" },
+ { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" },
+ { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" },
+ { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" },
+ { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" },
+ { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" },
+ { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" },
+ { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" },
+ { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" },
+ { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" },
+ { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" },
+ { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
+ { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" },
+ { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" },
+ { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" },
+ { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" },
+ { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" },
+ { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" },
+ { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" },
+ { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" },
+ { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
]
[[package]]
name = "pyproject-api"
-version = "1.9.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
+version = "1.10.0"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "packaging", marker = "python_full_version < '3.10'" },
- { name = "tomli", marker = "python_full_version < '3.10'" },
+ { name = "packaging" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/19/fd/437901c891f58a7b9096511750247535e891d2d5a5a6eefbc9386a2b41d5/pyproject_api-1.9.1.tar.gz", hash = "sha256:43c9918f49daab37e302038fc1aed54a8c7a91a9fa935d00b9a485f37e0f5335", size = 22710 }
+sdist = { url = "https://files.pythonhosted.org/packages/45/7b/c0e1333b61d41c69e59e5366e727b18c4992688caf0de1be10b3e5265f6b/pyproject_api-1.10.0.tar.gz", hash = "sha256:40c6f2d82eebdc4afee61c773ed208c04c19db4c4a60d97f8d7be3ebc0bbb330", size = 22785, upload-time = "2025-10-09T19:12:27.21Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/ef/e6/c293c06695d4a3ab0260ef124a74ebadba5f4c511ce3a4259e976902c00b/pyproject_api-1.9.1-py3-none-any.whl", hash = "sha256:7d6238d92f8962773dd75b5f0c4a6a27cce092a14b623b811dba656f3b628948", size = 13158 },
+ { url = "https://files.pythonhosted.org/packages/54/cc/cecf97be298bee2b2a37dd360618c819a2a7fd95251d8e480c1f0eb88f3b/pyproject_api-1.10.0-py3-none-any.whl", hash = "sha256:8757c41a79c0f4ab71b99abed52b97ecf66bd20b04fa59da43b5840bac105a09", size = 13218, upload-time = "2025-10-09T19:12:24.428Z" },
]
[[package]]
-name = "pyproject-api"
-version = "1.10.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
+name = "python-discovery"
+version = "1.2.2"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "packaging", marker = "python_full_version >= '3.10'" },
- { name = "tomli", marker = "python_full_version == '3.10.*'" },
+ { name = "filelock" },
+ { name = "platformdirs" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/45/7b/c0e1333b61d41c69e59e5366e727b18c4992688caf0de1be10b3e5265f6b/pyproject_api-1.10.0.tar.gz", hash = "sha256:40c6f2d82eebdc4afee61c773ed208c04c19db4c4a60d97f8d7be3ebc0bbb330", size = 22785 }
+sdist = { url = "https://files.pythonhosted.org/packages/de/ef/3bae0e537cfe91e8431efcba4434463d2c5a65f5a89edd47c6cf2f03c55f/python_discovery-1.2.2.tar.gz", hash = "sha256:876e9c57139eb757cb5878cbdd9ae5379e5d96266c99ef731119e04fffe533bb", size = 58872, upload-time = "2026-04-07T17:28:49.249Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/54/cc/cecf97be298bee2b2a37dd360618c819a2a7fd95251d8e480c1f0eb88f3b/pyproject_api-1.10.0-py3-none-any.whl", hash = "sha256:8757c41a79c0f4ab71b99abed52b97ecf66bd20b04fa59da43b5840bac105a09", size = 13218 },
+ { url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894, upload-time = "2026-04-07T17:28:48.09Z" },
]
[[package]]
name = "pytokens"
version = "0.4.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5", size = 161522 },
- { url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe", size = 246945 },
- { url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c", size = 259525 },
- { url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7", size = 262693 },
- { url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2", size = 103567 },
- { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864 },
- { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565 },
- { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824 },
- { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075 },
- { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323 },
- { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663 },
- { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626 },
- { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779 },
- { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076 },
- { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552 },
- { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720 },
- { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204 },
- { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423 },
- { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859 },
- { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520 },
- { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821 },
- { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263 },
- { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071 },
- { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716 },
- { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539 },
- { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474 },
- { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473 },
- { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485 },
- { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698 },
- { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287 },
- { url = "https://files.pythonhosted.org/packages/51/2a/f125667ce48105bf1f4e50e03cfa7b24b8c4f47684d7f1cf4dcb6f6b1c15/pytokens-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:34bcc734bd2f2d5fe3b34e7b3c0116bfb2397f2d9666139988e7a3eb5f7400e3", size = 161464 },
- { url = "https://files.pythonhosted.org/packages/40/df/065a30790a7ca6bb48ad9018dd44668ed9135610ebf56a2a4cb8e513fd5c/pytokens-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941d4343bf27b605e9213b26bfa1c4bf197c9c599a9627eb7305b0defcfe40c1", size = 246159 },
- { url = "https://files.pythonhosted.org/packages/a5/1c/fd09976a7e04960dabc07ab0e0072c7813d566ec67d5490a4c600683c158/pytokens-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ad72b851e781478366288743198101e5eb34a414f1d5627cdd585ca3b25f1db", size = 259120 },
- { url = "https://files.pythonhosted.org/packages/52/49/59fdc6fc5a390ae9f308eadeb97dfc70fc2d804ffc49dd39fc97604622ec/pytokens-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:682fa37ff4d8e95f7df6fe6fe6a431e8ed8e788023c6bcc0f0880a12eab80ad1", size = 262196 },
- { url = "https://files.pythonhosted.org/packages/3d/e7/d6734dccf0080e3dc00a55b0827ab5af30c886f8bc127bbc04bc3445daec/pytokens-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:30f51edd9bb7f85c748979384165601d028b84f7bd13fe14d3e065304093916a", size = 103510 },
- { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729 },
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5", size = 161522, upload-time = "2026-01-30T01:02:50.393Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe", size = 246945, upload-time = "2026-01-30T01:02:52.399Z" },
+ { url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c", size = 259525, upload-time = "2026-01-30T01:02:53.737Z" },
+ { url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7", size = 262693, upload-time = "2026-01-30T01:02:54.871Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2", size = 103567, upload-time = "2026-01-30T01:02:56.414Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" },
+ { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" },
+ { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" },
+ { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" },
+ { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" },
+ { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" },
+ { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" },
+ { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" },
+ { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" },
+ { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" },
+ { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" },
+ { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" },
+ { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227 },
- { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019 },
- { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646 },
- { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793 },
- { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293 },
- { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872 },
- { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828 },
- { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415 },
- { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561 },
- { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826 },
- { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577 },
- { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556 },
- { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114 },
- { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638 },
- { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463 },
- { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986 },
- { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543 },
- { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763 },
- { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063 },
- { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973 },
- { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116 },
- { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011 },
- { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870 },
- { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089 },
- { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181 },
- { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658 },
- { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003 },
- { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344 },
- { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 },
- { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 },
- { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 },
- { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 },
- { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 },
- { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 },
- { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 },
- { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 },
- { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 },
- { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 },
- { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814 },
- { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809 },
- { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454 },
- { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355 },
- { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175 },
- { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228 },
- { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194 },
- { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 },
- { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 },
- { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108 },
- { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641 },
- { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901 },
- { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132 },
- { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261 },
- { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272 },
- { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923 },
- { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 },
- { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 },
- { url = "https://files.pythonhosted.org/packages/9f/62/67fc8e68a75f738c9200422bf65693fb79a4cd0dc5b23310e5202e978090/pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da", size = 184450 },
- { url = "https://files.pythonhosted.org/packages/ae/92/861f152ce87c452b11b9d0977952259aa7df792d71c1053365cc7b09cc08/pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917", size = 174319 },
- { url = "https://files.pythonhosted.org/packages/d0/cd/f0cfc8c74f8a030017a2b9c771b7f47e5dd702c3e28e5b2071374bda2948/pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9", size = 737631 },
- { url = "https://files.pythonhosted.org/packages/ef/b2/18f2bd28cd2055a79a46c9b0895c0b3d987ce40ee471cecf58a1a0199805/pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5", size = 836795 },
- { url = "https://files.pythonhosted.org/packages/73/b9/793686b2d54b531203c160ef12bec60228a0109c79bae6c1277961026770/pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a", size = 750767 },
- { url = "https://files.pythonhosted.org/packages/a9/86/a137b39a611def2ed78b0e66ce2fe13ee701a07c07aebe55c340ed2a050e/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926", size = 727982 },
- { url = "https://files.pythonhosted.org/packages/dd/62/71c27c94f457cf4418ef8ccc71735324c549f7e3ea9d34aba50874563561/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7", size = 755677 },
- { url = "https://files.pythonhosted.org/packages/29/3d/6f5e0d58bd924fb0d06c3a6bad00effbdae2de5adb5cda5648006ffbd8d3/pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0", size = 142592 },
- { url = "https://files.pythonhosted.org/packages/f0/0c/25113e0b5e103d7f1490c0e947e303fe4a696c10b501dea7a9f49d4e876c/pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007", size = 158777 },
-]
-
-[[package]]
-name = "referencing"
-version = "0.36.2"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-dependencies = [
- { name = "attrs", marker = "python_full_version < '3.10'" },
- { name = "rpds-py", version = "0.27.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "typing-extensions", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 },
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" },
+ { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" },
+ { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" },
+ { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" },
+ { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" },
+ { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" },
+ { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" },
+ { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
+ { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
+ { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
+ { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
+ { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
+ { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
+ { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
+ { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
+ { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
+ { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
+ { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
+ { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
+ { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
+ { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
+ { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
+ { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
+ { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
]
[[package]]
name = "referencing"
version = "0.37.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "attrs", marker = "python_full_version >= '3.10'" },
- { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" },
+ { name = "attrs" },
+ { name = "rpds-py" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 }
+sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 },
+ { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" },
]
[[package]]
name = "requests"
-version = "2.32.5"
-source = { registry = "https://pypi.org/simple" }
+version = "2.33.1"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 }
+sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 },
-]
-
-[[package]]
-name = "rpds-py"
-version = "0.27.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a5/ed/3aef893e2dd30e77e35d20d4ddb45ca459db59cead748cad9796ad479411/rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef", size = 371606 },
- { url = "https://files.pythonhosted.org/packages/6d/82/9818b443e5d3eb4c83c3994561387f116aae9833b35c484474769c4a8faf/rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be", size = 353452 },
- { url = "https://files.pythonhosted.org/packages/99/c7/d2a110ffaaa397fc6793a83c7bd3545d9ab22658b7cdff05a24a4535cc45/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61", size = 381519 },
- { url = "https://files.pythonhosted.org/packages/5a/bc/e89581d1f9d1be7d0247eaef602566869fdc0d084008ba139e27e775366c/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb", size = 394424 },
- { url = "https://files.pythonhosted.org/packages/ac/2e/36a6861f797530e74bb6ed53495f8741f1ef95939eed01d761e73d559067/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657", size = 523467 },
- { url = "https://files.pythonhosted.org/packages/c4/59/c1bc2be32564fa499f988f0a5c6505c2f4746ef96e58e4d7de5cf923d77e/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013", size = 402660 },
- { url = "https://files.pythonhosted.org/packages/0a/ec/ef8bf895f0628dd0a59e54d81caed6891663cb9c54a0f4bb7da918cb88cf/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a", size = 384062 },
- { url = "https://files.pythonhosted.org/packages/69/f7/f47ff154be8d9a5e691c083a920bba89cef88d5247c241c10b9898f595a1/rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1", size = 401289 },
- { url = "https://files.pythonhosted.org/packages/3b/d9/ca410363efd0615814ae579f6829cafb39225cd63e5ea5ed1404cb345293/rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10", size = 417718 },
- { url = "https://files.pythonhosted.org/packages/e3/a0/8cb5c2ff38340f221cc067cc093d1270e10658ba4e8d263df923daa18e86/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808", size = 558333 },
- { url = "https://files.pythonhosted.org/packages/6f/8c/1b0de79177c5d5103843774ce12b84caa7164dfc6cd66378768d37db11bf/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8", size = 589127 },
- { url = "https://files.pythonhosted.org/packages/c8/5e/26abb098d5e01266b0f3a2488d299d19ccc26849735d9d2b95c39397e945/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9", size = 554899 },
- { url = "https://files.pythonhosted.org/packages/de/41/905cc90ced13550db017f8f20c6d8e8470066c5738ba480d7ba63e3d136b/rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4", size = 217450 },
- { url = "https://files.pythonhosted.org/packages/75/3d/6bef47b0e253616ccdf67c283e25f2d16e18ccddd38f92af81d5a3420206/rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1", size = 228447 },
- { url = "https://files.pythonhosted.org/packages/b5/c1/7907329fbef97cbd49db6f7303893bd1dd5a4a3eae415839ffdfb0762cae/rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881", size = 371063 },
- { url = "https://files.pythonhosted.org/packages/11/94/2aab4bc86228bcf7c48760990273653a4900de89c7537ffe1b0d6097ed39/rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5", size = 353210 },
- { url = "https://files.pythonhosted.org/packages/3a/57/f5eb3ecf434342f4f1a46009530e93fd201a0b5b83379034ebdb1d7c1a58/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e", size = 381636 },
- { url = "https://files.pythonhosted.org/packages/ae/f4/ef95c5945e2ceb5119571b184dd5a1cc4b8541bbdf67461998cfeac9cb1e/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c", size = 394341 },
- { url = "https://files.pythonhosted.org/packages/5a/7e/4bd610754bf492d398b61725eb9598ddd5eb86b07d7d9483dbcd810e20bc/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195", size = 523428 },
- { url = "https://files.pythonhosted.org/packages/9f/e5/059b9f65a8c9149361a8b75094864ab83b94718344db511fd6117936ed2a/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52", size = 402923 },
- { url = "https://files.pythonhosted.org/packages/f5/48/64cabb7daced2968dd08e8a1b7988bf358d7bd5bcd5dc89a652f4668543c/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed", size = 384094 },
- { url = "https://files.pythonhosted.org/packages/ae/e1/dc9094d6ff566bff87add8a510c89b9e158ad2ecd97ee26e677da29a9e1b/rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a", size = 401093 },
- { url = "https://files.pythonhosted.org/packages/37/8e/ac8577e3ecdd5593e283d46907d7011618994e1d7ab992711ae0f78b9937/rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde", size = 417969 },
- { url = "https://files.pythonhosted.org/packages/66/6d/87507430a8f74a93556fe55c6485ba9c259949a853ce407b1e23fea5ba31/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21", size = 558302 },
- { url = "https://files.pythonhosted.org/packages/3a/bb/1db4781ce1dda3eecc735e3152659a27b90a02ca62bfeea17aee45cc0fbc/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9", size = 589259 },
- { url = "https://files.pythonhosted.org/packages/7b/0e/ae1c8943d11a814d01b482e1f8da903f88047a962dff9bbdadf3bd6e6fd1/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948", size = 554983 },
- { url = "https://files.pythonhosted.org/packages/b2/d5/0b2a55415931db4f112bdab072443ff76131b5ac4f4dc98d10d2d357eb03/rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39", size = 217154 },
- { url = "https://files.pythonhosted.org/packages/24/75/3b7ffe0d50dc86a6a964af0d1cc3a4a2cdf437cb7b099a4747bbb96d1819/rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15", size = 228627 },
- { url = "https://files.pythonhosted.org/packages/8d/3f/4fd04c32abc02c710f09a72a30c9a55ea3cc154ef8099078fd50a0596f8e/rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746", size = 220998 },
- { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887 },
- { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795 },
- { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121 },
- { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976 },
- { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953 },
- { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915 },
- { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883 },
- { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699 },
- { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713 },
- { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324 },
- { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646 },
- { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137 },
- { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343 },
- { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497 },
- { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790 },
- { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741 },
- { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574 },
- { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051 },
- { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395 },
- { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334 },
- { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691 },
- { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868 },
- { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469 },
- { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125 },
- { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341 },
- { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511 },
- { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736 },
- { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462 },
- { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034 },
- { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392 },
- { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355 },
- { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138 },
- { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247 },
- { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699 },
- { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852 },
- { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582 },
- { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126 },
- { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486 },
- { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832 },
- { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249 },
- { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356 },
- { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300 },
- { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714 },
- { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943 },
- { url = "https://files.pythonhosted.org/packages/70/36/b6e6066520a07cf029d385de869729a895917b411e777ab1cde878100a1d/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334", size = 362472 },
- { url = "https://files.pythonhosted.org/packages/af/07/b4646032e0dcec0df9c73a3bd52f63bc6c5f9cda992f06bd0e73fe3fbebd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33", size = 345676 },
- { url = "https://files.pythonhosted.org/packages/b0/16/2f1003ee5d0af4bcb13c0cf894957984c32a6751ed7206db2aee7379a55e/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a", size = 385313 },
- { url = "https://files.pythonhosted.org/packages/05/cd/7eb6dd7b232e7f2654d03fa07f1414d7dfc980e82ba71e40a7c46fd95484/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b", size = 399080 },
- { url = "https://files.pythonhosted.org/packages/20/51/5829afd5000ec1cb60f304711f02572d619040aa3ec033d8226817d1e571/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7", size = 523868 },
- { url = "https://files.pythonhosted.org/packages/05/2c/30eebca20d5db95720ab4d2faec1b5e4c1025c473f703738c371241476a2/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136", size = 408750 },
- { url = "https://files.pythonhosted.org/packages/90/1a/cdb5083f043597c4d4276eae4e4c70c55ab5accec078da8611f24575a367/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff", size = 387688 },
- { url = "https://files.pythonhosted.org/packages/7c/92/cf786a15320e173f945d205ab31585cc43969743bb1a48b6888f7a2b0a2d/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9", size = 407225 },
- { url = "https://files.pythonhosted.org/packages/33/5c/85ee16df5b65063ef26017bef33096557a4c83fbe56218ac7cd8c235f16d/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60", size = 423361 },
- { url = "https://files.pythonhosted.org/packages/4b/8e/1c2741307fcabd1a334ecf008e92c4f47bb6f848712cf15c923becfe82bb/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e", size = 562493 },
- { url = "https://files.pythonhosted.org/packages/04/03/5159321baae9b2222442a70c1f988cbbd66b9be0675dd3936461269be360/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212", size = 592623 },
- { url = "https://files.pythonhosted.org/packages/ff/39/c09fd1ad28b85bc1d4554a8710233c9f4cefd03d7717a1b8fbfd171d1167/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675", size = 558800 },
- { url = "https://files.pythonhosted.org/packages/c5/d6/99228e6bbcf4baa764b18258f519a9035131d91b538d4e0e294313462a98/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3", size = 221943 },
- { url = "https://files.pythonhosted.org/packages/be/07/c802bc6b8e95be83b79bdf23d1aa61d68324cb1006e245d6c58e959e314d/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456", size = 233739 },
- { url = "https://files.pythonhosted.org/packages/c8/89/3e1b1c16d4c2d547c5717377a8df99aee8099ff050f87c45cb4d5fa70891/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3", size = 223120 },
- { url = "https://files.pythonhosted.org/packages/62/7e/dc7931dc2fa4a6e46b2a4fa744a9fe5c548efd70e0ba74f40b39fa4a8c10/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2", size = 358944 },
- { url = "https://files.pythonhosted.org/packages/e6/22/4af76ac4e9f336bfb1a5f240d18a33c6b2fcaadb7472ac7680576512b49a/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4", size = 342283 },
- { url = "https://files.pythonhosted.org/packages/1c/15/2a7c619b3c2272ea9feb9ade67a45c40b3eeb500d503ad4c28c395dc51b4/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e", size = 380320 },
- { url = "https://files.pythonhosted.org/packages/a2/7d/4c6d243ba4a3057e994bb5bedd01b5c963c12fe38dde707a52acdb3849e7/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817", size = 391760 },
- { url = "https://files.pythonhosted.org/packages/b4/71/b19401a909b83bcd67f90221330bc1ef11bc486fe4e04c24388d28a618ae/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec", size = 522476 },
- { url = "https://files.pythonhosted.org/packages/e4/44/1a3b9715c0455d2e2f0f6df5ee6d6f5afdc423d0773a8a682ed2b43c566c/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a", size = 403418 },
- { url = "https://files.pythonhosted.org/packages/1c/4b/fb6c4f14984eb56673bc868a66536f53417ddb13ed44b391998100a06a96/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8", size = 384771 },
- { url = "https://files.pythonhosted.org/packages/c0/56/d5265d2d28b7420d7b4d4d85cad8ef891760f5135102e60d5c970b976e41/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48", size = 400022 },
- { url = "https://files.pythonhosted.org/packages/8f/e9/9f5fc70164a569bdd6ed9046486c3568d6926e3a49bdefeeccfb18655875/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb", size = 416787 },
- { url = "https://files.pythonhosted.org/packages/d4/64/56dd03430ba491db943a81dcdef115a985aac5f44f565cd39a00c766d45c/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734", size = 557538 },
- { url = "https://files.pythonhosted.org/packages/3f/36/92cc885a3129993b1d963a2a42ecf64e6a8e129d2c7cc980dbeba84e55fb/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb", size = 588512 },
- { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813 },
- { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385 },
- { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097 },
- { url = "https://files.pythonhosted.org/packages/7f/6c/252e83e1ce7583c81f26d1d884b2074d40a13977e1b6c9c50bbf9a7f1f5a/rpds_py-0.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c918c65ec2e42c2a78d19f18c553d77319119bf43aa9e2edf7fb78d624355527", size = 372140 },
- { url = "https://files.pythonhosted.org/packages/9d/71/949c195d927c5aeb0d0629d329a20de43a64c423a6aa53836290609ef7ec/rpds_py-0.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1fea2b1a922c47c51fd07d656324531adc787e415c8b116530a1d29c0516c62d", size = 354086 },
- { url = "https://files.pythonhosted.org/packages/9f/02/e43e332ad8ce4f6c4342d151a471a7f2900ed1d76901da62eb3762663a71/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbf94c58e8e0cd6b6f38d8de67acae41b3a515c26169366ab58bdca4a6883bb8", size = 382117 },
- { url = "https://files.pythonhosted.org/packages/d0/05/b0fdeb5b577197ad72812bbdfb72f9a08fa1e64539cc3940b1b781cd3596/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2a8fed130ce946d5c585eddc7c8eeef0051f58ac80a8ee43bd17835c144c2cc", size = 394520 },
- { url = "https://files.pythonhosted.org/packages/67/1f/4cfef98b2349a7585181e99294fa2a13f0af06902048a5d70f431a66d0b9/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:037a2361db72ee98d829bc2c5b7cc55598ae0a5e0ec1823a56ea99374cfd73c1", size = 522657 },
- { url = "https://files.pythonhosted.org/packages/44/55/ccf37ddc4c6dce7437b335088b5ca18da864b334890e2fe9aa6ddc3f79a9/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5281ed1cc1d49882f9997981c88df1a22e140ab41df19071222f7e5fc4e72125", size = 402967 },
- { url = "https://files.pythonhosted.org/packages/74/e5/5903f92e41e293b07707d5bf00ef39a0eb2af7190aff4beaf581a6591510/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd50659a069c15eef8aa3d64bbef0d69fd27bb4a50c9ab4f17f83a16cbf8905", size = 384372 },
- { url = "https://files.pythonhosted.org/packages/8f/e3/fbb409e18aeefc01e49f5922ac63d2d914328430e295c12183ce56ebf76b/rpds_py-0.27.1-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:c4b676c4ae3921649a15d28ed10025548e9b561ded473aa413af749503c6737e", size = 401264 },
- { url = "https://files.pythonhosted.org/packages/55/79/529ad07794e05cb0f38e2f965fc5bb20853d523976719400acecc447ec9d/rpds_py-0.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:079bc583a26db831a985c5257797b2b5d3affb0386e7ff886256762f82113b5e", size = 418691 },
- { url = "https://files.pythonhosted.org/packages/33/39/6554a7fd6d9906fda2521c6d52f5d723dca123529fb719a5b5e074c15e01/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e44099bd522cba71a2c6b97f68e19f40e7d85399de899d66cdb67b32d7cb786", size = 558989 },
- { url = "https://files.pythonhosted.org/packages/19/b2/76fa15173b6f9f445e5ef15120871b945fb8dd9044b6b8c7abe87e938416/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e202e6d4188e53c6661af813b46c37ca2c45e497fc558bacc1a7630ec2695aec", size = 589835 },
- { url = "https://files.pythonhosted.org/packages/ee/9e/5560a4b39bab780405bed8a88ee85b30178061d189558a86003548dea045/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f41f814b8eaa48768d1bb551591f6ba45f87ac76899453e8ccd41dba1289b04b", size = 555227 },
- { url = "https://files.pythonhosted.org/packages/52/d7/cd9c36215111aa65724c132bf709c6f35175973e90b32115dedc4ced09cb/rpds_py-0.27.1-cp39-cp39-win32.whl", hash = "sha256:9e71f5a087ead99563c11fdaceee83ee982fd39cf67601f4fd66cb386336ee52", size = 217899 },
- { url = "https://files.pythonhosted.org/packages/5b/e0/d75ab7b4dd8ba777f6b365adbdfc7614bbfe7c5f05703031dfa4b61c3d6c/rpds_py-0.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:71108900c9c3c8590697244b9519017a400d9ba26a36c48381b3f64743a44aab", size = 228725 },
- { url = "https://files.pythonhosted.org/packages/d5/63/b7cc415c345625d5e62f694ea356c58fb964861409008118f1245f8c3347/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf", size = 371360 },
- { url = "https://files.pythonhosted.org/packages/e5/8c/12e1b24b560cf378b8ffbdb9dc73abd529e1adcfcf82727dfd29c4a7b88d/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3", size = 353933 },
- { url = "https://files.pythonhosted.org/packages/9b/85/1bb2210c1f7a1b99e91fea486b9f0f894aa5da3a5ec7097cbad7dec6d40f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636", size = 382962 },
- { url = "https://files.pythonhosted.org/packages/cc/c9/a839b9f219cf80ed65f27a7f5ddbb2809c1b85c966020ae2dff490e0b18e/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8", size = 394412 },
- { url = "https://files.pythonhosted.org/packages/02/2d/b1d7f928b0b1f4fc2e0133e8051d199b01d7384875adc63b6ddadf3de7e5/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc", size = 523972 },
- { url = "https://files.pythonhosted.org/packages/a9/af/2cbf56edd2d07716df1aec8a726b3159deb47cb5c27e1e42b71d705a7c2f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8", size = 403273 },
- { url = "https://files.pythonhosted.org/packages/c0/93/425e32200158d44ff01da5d9612c3b6711fe69f606f06e3895511f17473b/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc", size = 385278 },
- { url = "https://files.pythonhosted.org/packages/eb/1a/1a04a915ecd0551bfa9e77b7672d1937b4b72a0fc204a17deef76001cfb2/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71", size = 402084 },
- { url = "https://files.pythonhosted.org/packages/51/f7/66585c0fe5714368b62951d2513b684e5215beaceab2c6629549ddb15036/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad", size = 419041 },
- { url = "https://files.pythonhosted.org/packages/8e/7e/83a508f6b8e219bba2d4af077c35ba0e0cdd35a751a3be6a7cba5a55ad71/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab", size = 560084 },
- { url = "https://files.pythonhosted.org/packages/66/66/bb945683b958a1b19eb0fe715594630d0f36396ebdef4d9b89c2fa09aa56/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059", size = 590115 },
- { url = "https://files.pythonhosted.org/packages/12/00/ccfaafaf7db7e7adace915e5c2f2c2410e16402561801e9c7f96683002d3/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b", size = 556561 },
- { url = "https://files.pythonhosted.org/packages/e1/b7/92b6ed9aad103bfe1c45df98453dfae40969eef2cb6c6239c58d7e96f1b3/rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819", size = 229125 },
- { url = "https://files.pythonhosted.org/packages/0c/ed/e1fba02de17f4f76318b834425257c8ea297e415e12c68b4361f63e8ae92/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df", size = 371402 },
- { url = "https://files.pythonhosted.org/packages/af/7c/e16b959b316048b55585a697e94add55a4ae0d984434d279ea83442e460d/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3", size = 354084 },
- { url = "https://files.pythonhosted.org/packages/de/c1/ade645f55de76799fdd08682d51ae6724cb46f318573f18be49b1e040428/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9", size = 383090 },
- { url = "https://files.pythonhosted.org/packages/1f/27/89070ca9b856e52960da1472efcb6c20ba27cfe902f4f23ed095b9cfc61d/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc", size = 394519 },
- { url = "https://files.pythonhosted.org/packages/b3/28/be120586874ef906aa5aeeae95ae8df4184bc757e5b6bd1c729ccff45ed5/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4", size = 523817 },
- { url = "https://files.pythonhosted.org/packages/a8/ef/70cc197bc11cfcde02a86f36ac1eed15c56667c2ebddbdb76a47e90306da/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66", size = 403240 },
- { url = "https://files.pythonhosted.org/packages/cf/35/46936cca449f7f518f2f4996e0e8344db4b57e2081e752441154089d2a5f/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e", size = 385194 },
- { url = "https://files.pythonhosted.org/packages/e1/62/29c0d3e5125c3270b51415af7cbff1ec587379c84f55a5761cc9efa8cd06/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c", size = 402086 },
- { url = "https://files.pythonhosted.org/packages/8f/66/03e1087679227785474466fdd04157fb793b3b76e3fcf01cbf4c693c1949/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf", size = 419272 },
- { url = "https://files.pythonhosted.org/packages/6a/24/e3e72d265121e00b063aef3e3501e5b2473cf1b23511d56e529531acf01e/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf", size = 560003 },
- { url = "https://files.pythonhosted.org/packages/26/ca/f5a344c534214cc2d41118c0699fffbdc2c1bc7046f2a2b9609765ab9c92/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6", size = 590482 },
- { url = "https://files.pythonhosted.org/packages/ce/08/4349bdd5c64d9d193c360aa9db89adeee6f6682ab8825dca0a3f535f434f/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a", size = 556523 },
- { url = "https://files.pythonhosted.org/packages/4e/ea/5463cd5048a7a2fcdae308b6e96432802132c141bfb9420260142632a0f1/rpds_py-0.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa8933159edc50be265ed22b401125c9eebff3171f570258854dbce3ecd55475", size = 371778 },
- { url = "https://files.pythonhosted.org/packages/0d/c8/f38c099db07f5114029c1467649d308543906933eebbc226d4527a5f4693/rpds_py-0.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a50431bf02583e21bf273c71b89d710e7a710ad5e39c725b14e685610555926f", size = 354394 },
- { url = "https://files.pythonhosted.org/packages/7d/79/b76f97704d9dd8ddbd76fed4c4048153a847c5d6003afe20a6b5c3339065/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78af06ddc7fe5cc0e967085a9115accee665fb912c22a3f54bad70cc65b05fe6", size = 382348 },
- { url = "https://files.pythonhosted.org/packages/8a/3f/ef23d3c1be1b837b648a3016d5bbe7cfe711422ad110b4081c0a90ef5a53/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70d0738ef8fee13c003b100c2fbd667ec4f133468109b3472d249231108283a3", size = 394159 },
- { url = "https://files.pythonhosted.org/packages/74/8a/9e62693af1a34fd28b1a190d463d12407bd7cf561748cb4745845d9548d3/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2f6fd8a1cea5bbe599b6e78a6e5ee08db434fc8ffea51ff201c8765679698b3", size = 522775 },
- { url = "https://files.pythonhosted.org/packages/36/0d/8d5bb122bf7a60976b54c5c99a739a3819f49f02d69df3ea2ca2aff47d5c/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8177002868d1426305bb5de1e138161c2ec9eb2d939be38291d7c431c4712df8", size = 402633 },
- { url = "https://files.pythonhosted.org/packages/0f/0e/237948c1f425e23e0cf5a566d702652a6e55c6f8fbd332a1792eb7043daf/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:008b839781d6c9bf3b6a8984d1d8e56f0ec46dc56df61fd669c49b58ae800400", size = 384867 },
- { url = "https://files.pythonhosted.org/packages/d6/0a/da0813efcd998d260cbe876d97f55b0f469ada8ba9cbc47490a132554540/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:a55b9132bb1ade6c734ddd2759c8dc132aa63687d259e725221f106b83a0e485", size = 401791 },
- { url = "https://files.pythonhosted.org/packages/51/78/c6c9e8a8aaca416a6f0d1b6b4a6ee35b88fe2c5401d02235d0a056eceed2/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a46fdec0083a26415f11d5f236b79fa1291c32aaa4a17684d82f7017a1f818b1", size = 419525 },
- { url = "https://files.pythonhosted.org/packages/a3/69/5af37e1d71487cf6d56dd1420dc7e0c2732c1b6ff612aa7a88374061c0a8/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8a63b640a7845f2bdd232eb0d0a4a2dd939bcdd6c57e6bb134526487f3160ec5", size = 559255 },
- { url = "https://files.pythonhosted.org/packages/40/7f/8b7b136069ef7ac3960eda25d832639bdb163018a34c960ed042dd1707c8/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7e32721e5d4922deaaf963469d795d5bde6093207c52fec719bd22e5d1bedbc4", size = 590384 },
- { url = "https://files.pythonhosted.org/packages/d8/06/c316d3f6ff03f43ccb0eba7de61376f8ec4ea850067dddfafe98274ae13c/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2c426b99a068601b5f4623573df7a7c3d72e87533a2dd2253353a03e7502566c", size = 555959 },
- { url = "https://files.pythonhosted.org/packages/60/94/384cf54c430b9dac742bbd2ec26c23feb78ded0d43d6d78563a281aec017/rpds_py-0.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4fc9b7fe29478824361ead6e14e4f5aed570d477e06088826537e202d25fe859", size = 228784 },
+ { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" },
]
[[package]]
name = "rpds-py"
version = "0.30.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490 },
- { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751 },
- { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696 },
- { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136 },
- { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699 },
- { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022 },
- { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522 },
- { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579 },
- { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305 },
- { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503 },
- { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322 },
- { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792 },
- { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901 },
- { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823 },
- { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157 },
- { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676 },
- { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938 },
- { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932 },
- { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830 },
- { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033 },
- { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828 },
- { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683 },
- { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583 },
- { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496 },
- { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669 },
- { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011 },
- { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406 },
- { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024 },
- { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069 },
- { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086 },
- { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053 },
- { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763 },
- { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951 },
- { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622 },
- { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492 },
- { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080 },
- { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680 },
- { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589 },
- { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289 },
- { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737 },
- { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120 },
- { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782 },
- { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463 },
- { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868 },
- { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887 },
- { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904 },
- { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945 },
- { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783 },
- { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021 },
- { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589 },
- { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025 },
- { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895 },
- { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799 },
- { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731 },
- { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027 },
- { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020 },
- { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139 },
- { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224 },
- { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645 },
- { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443 },
- { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375 },
- { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850 },
- { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812 },
- { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841 },
- { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149 },
- { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843 },
- { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507 },
- { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949 },
- { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790 },
- { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217 },
- { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806 },
- { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341 },
- { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768 },
- { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099 },
- { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192 },
- { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080 },
- { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841 },
- { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670 },
- { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005 },
- { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112 },
- { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049 },
- { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661 },
- { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606 },
- { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126 },
- { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371 },
- { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298 },
- { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604 },
- { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391 },
- { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868 },
- { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747 },
- { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795 },
- { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330 },
- { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194 },
- { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340 },
- { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765 },
- { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834 },
- { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470 },
- { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630 },
- { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148 },
- { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030 },
- { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570 },
- { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532 },
- { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292 },
- { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128 },
- { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542 },
- { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004 },
- { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063 },
- { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099 },
- { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177 },
- { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015 },
- { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736 },
- { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981 },
- { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782 },
- { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191 },
-]
-
-[[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 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696 },
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" },
+ { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" },
+ { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" },
+ { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" },
+ { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" },
+ { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" },
+ { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" },
+ { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" },
+ { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" },
+ { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" },
+ { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" },
+ { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" },
+ { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" },
+ { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" },
+ { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" },
+ { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" },
+ { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" },
+ { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" },
+ { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" },
+ { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" },
+ { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" },
+ { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" },
+ { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" },
+ { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" },
+ { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" },
+ { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" },
+ { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" },
+ { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" },
+ { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" },
+ { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" },
+ { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" },
+ { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" },
+ { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" },
+ { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" },
+ { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" },
+ { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" },
+ { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" },
+ { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" },
+ { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" },
+ { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" },
+ { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" },
+ { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" },
+ { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" },
+ { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" },
+ { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" },
+ { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" },
+ { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" },
+ { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" },
+ { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" },
+ { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" },
+ { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" },
+ { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" },
]
[[package]]
name = "ruff"
-version = "0.15.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/04/dc/4e6ac71b511b141cf626357a3946679abeba4cf67bc7cc5a17920f31e10d/ruff-0.15.1.tar.gz", hash = "sha256:c590fe13fb57c97141ae975c03a1aedb3d3156030cabd740d6ff0b0d601e203f", size = 4540855 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/23/bf/e6e4324238c17f9d9120a9d60aa99a7daaa21204c07fcd84e2ef03bb5fd1/ruff-0.15.1-py3-none-linux_armv6l.whl", hash = "sha256:b101ed7cf4615bda6ffe65bdb59f964e9f4a0d3f85cbf0e54f0ab76d7b90228a", size = 10367819 },
- { url = "https://files.pythonhosted.org/packages/b3/ea/c8f89d32e7912269d38c58f3649e453ac32c528f93bb7f4219258be2e7ed/ruff-0.15.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:939c995e9277e63ea632cc8d3fae17aa758526f49a9a850d2e7e758bfef46602", size = 10798618 },
- { url = "https://files.pythonhosted.org/packages/5e/0f/1d0d88bc862624247d82c20c10d4c0f6bb2f346559d8af281674cf327f15/ruff-0.15.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1d83466455fdefe60b8d9c8df81d3c1bbb2115cede53549d3b522ce2bc703899", size = 10148518 },
- { url = "https://files.pythonhosted.org/packages/f5/c8/291c49cefaa4a9248e986256df2ade7add79388fe179e0691be06fae6f37/ruff-0.15.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9457e3c3291024866222b96108ab2d8265b477e5b1534c7ddb1810904858d16", size = 10518811 },
- { url = "https://files.pythonhosted.org/packages/c3/1a/f5707440e5ae43ffa5365cac8bbb91e9665f4a883f560893829cf16a606b/ruff-0.15.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:92c92b003e9d4f7fbd33b1867bb15a1b785b1735069108dfc23821ba045b29bc", size = 10196169 },
- { url = "https://files.pythonhosted.org/packages/2a/ff/26ddc8c4da04c8fd3ee65a89c9fb99eaa5c30394269d424461467be2271f/ruff-0.15.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fe5c41ab43e3a06778844c586251eb5a510f67125427625f9eb2b9526535779", size = 10990491 },
- { url = "https://files.pythonhosted.org/packages/fc/00/50920cb385b89413f7cdb4bb9bc8fc59c1b0f30028d8bccc294189a54955/ruff-0.15.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66a6dd6df4d80dc382c6484f8ce1bcceb55c32e9f27a8b94c32f6c7331bf14fb", size = 11843280 },
- { url = "https://files.pythonhosted.org/packages/5d/6d/2f5cad8380caf5632a15460c323ae326f1e1a2b5b90a6ee7519017a017ca/ruff-0.15.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a4a42cbb8af0bda9bcd7606b064d7c0bc311a88d141d02f78920be6acb5aa83", size = 11274336 },
- { url = "https://files.pythonhosted.org/packages/a3/1d/5f56cae1d6c40b8a318513599b35ea4b075d7dc1cd1d04449578c29d1d75/ruff-0.15.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ab064052c31dddada35079901592dfba2e05f5b1e43af3954aafcbc1096a5b2", size = 11137288 },
- { url = "https://files.pythonhosted.org/packages/cd/20/6f8d7d8f768c93b0382b33b9306b3b999918816da46537d5a61635514635/ruff-0.15.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5631c940fe9fe91f817a4c2ea4e81f47bee3ca4aa646134a24374f3c19ad9454", size = 11070681 },
- { url = "https://files.pythonhosted.org/packages/9a/67/d640ac76069f64cdea59dba02af2e00b1fa30e2103c7f8d049c0cff4cafd/ruff-0.15.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:68138a4ba184b4691ccdc39f7795c66b3c68160c586519e7e8444cf5a53e1b4c", size = 10486401 },
- { url = "https://files.pythonhosted.org/packages/65/3d/e1429f64a3ff89297497916b88c32a5cc88eeca7e9c787072d0e7f1d3e1e/ruff-0.15.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:518f9af03bfc33c03bdb4cb63fabc935341bb7f54af500f92ac309ecfbba6330", size = 10197452 },
- { url = "https://files.pythonhosted.org/packages/78/83/e2c3bade17dad63bf1e1c2ffaf11490603b760be149e1419b07049b36ef2/ruff-0.15.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:da79f4d6a826caaea95de0237a67e33b81e6ec2e25fc7e1993a4015dffca7c61", size = 10693900 },
- { url = "https://files.pythonhosted.org/packages/a1/27/fdc0e11a813e6338e0706e8b39bb7a1d61ea5b36873b351acee7e524a72a/ruff-0.15.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3dd86dccb83cd7d4dcfac303ffc277e6048600dfc22e38158afa208e8bf94a1f", size = 11227302 },
- { url = "https://files.pythonhosted.org/packages/f6/58/ac864a75067dcbd3b95be5ab4eb2b601d7fbc3d3d736a27e391a4f92a5c1/ruff-0.15.1-py3-none-win32.whl", hash = "sha256:660975d9cb49b5d5278b12b03bb9951d554543a90b74ed5d366b20e2c57c2098", size = 10462555 },
- { url = "https://files.pythonhosted.org/packages/e0/5e/d4ccc8a27ecdb78116feac4935dfc39d1304536f4296168f91ed3ec00cd2/ruff-0.15.1-py3-none-win_amd64.whl", hash = "sha256:c820fef9dd5d4172a6570e5721704a96c6679b80cf7be41659ed439653f62336", size = 11599956 },
- { url = "https://files.pythonhosted.org/packages/2a/07/5bda6a85b220c64c65686bc85bd0bbb23b29c62b3a9f9433fa55f17cda93/ruff-0.15.1-py3-none-win_arm64.whl", hash = "sha256:5ff7d5f0f88567850f45081fac8f4ec212be8d0b963e385c3f7d0d2eb4899416", size = 10874604 },
+version = "0.15.10"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/d9/aa3f7d59a10ef6b14fe3431706f854dbf03c5976be614a9796d36326810c/ruff-0.15.10.tar.gz", hash = "sha256:d1f86e67ebfdef88e00faefa1552b5e510e1d35f3be7d423dc7e84e63788c94e", size = 4631728, upload-time = "2026-04-09T14:06:09.884Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/eb/00/a1c2fdc9939b2c03691edbda290afcd297f1f389196172826b03d6b6a595/ruff-0.15.10-py3-none-linux_armv6l.whl", hash = "sha256:0744e31482f8f7d0d10a11fcbf897af272fefdfcb10f5af907b18c2813ff4d5f", size = 10563362, upload-time = "2026-04-09T14:06:21.189Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/15/006990029aea0bebe9d33c73c3e28c80c391ebdba408d1b08496f00d422d/ruff-0.15.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b1e7c16ea0ff5a53b7c2df52d947e685973049be1cdfe2b59a9c43601897b22e", size = 10951122, upload-time = "2026-04-09T14:06:02.236Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/c0/4ac978fe874d0618c7da647862afe697b281c2806f13ce904ad652fa87e4/ruff-0.15.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:93cc06a19e5155b4441dd72808fdf84290d84ad8a39ca3b0f994363ade4cebb1", size = 10314005, upload-time = "2026-04-09T14:06:00.026Z" },
+ { url = "https://files.pythonhosted.org/packages/da/73/c209138a5c98c0d321266372fc4e33ad43d506d7e5dd817dd89b60a8548f/ruff-0.15.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83e1dd04312997c99ea6965df66a14fb4f03ba978564574ffc68b0d61fd3989e", size = 10643450, upload-time = "2026-04-09T14:05:42.137Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/76/0deec355d8ec10709653635b1f90856735302cb8e149acfdf6f82a5feb70/ruff-0.15.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8154d43684e4333360fedd11aaa40b1b08a4e37d8ffa9d95fee6fa5b37b6fab1", size = 10379597, upload-time = "2026-04-09T14:05:49.984Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/be/86bba8fc8798c081e28a4b3bb6d143ccad3fd5f6f024f02002b8f08a9fa3/ruff-0.15.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ab88715f3a6deb6bde6c227f3a123410bec7b855c3ae331b4c006189e895cef", size = 11146645, upload-time = "2026-04-09T14:06:12.246Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/89/140025e65911b281c57be1d385ba1d932c2366ca88ae6663685aed8d4881/ruff-0.15.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a768ff5969b4f44c349d48edf4ab4f91eddb27fd9d77799598e130fb628aa158", size = 12030289, upload-time = "2026-04-09T14:06:04.776Z" },
+ { url = "https://files.pythonhosted.org/packages/88/de/ddacca9545a5e01332567db01d44bd8cf725f2db3b3d61a80550b48308ea/ruff-0.15.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ee3ef42dab7078bda5ff6a1bcba8539e9857deb447132ad5566a038674540d0", size = 11496266, upload-time = "2026-04-09T14:05:55.485Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/bb/7ddb00a83760ff4a83c4e2fc231fd63937cc7317c10c82f583302e0f6586/ruff-0.15.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51cb8cc943e891ba99989dd92d61e29b1d231e14811db9be6440ecf25d5c1609", size = 11256418, upload-time = "2026-04-09T14:05:57.69Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/8d/55de0d35aacf6cd50b6ee91ee0f291672080021896543776f4170fc5c454/ruff-0.15.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:e59c9bdc056a320fb9ea1700a8d591718b8faf78af065484e801258d3a76bc3f", size = 11288416, upload-time = "2026-04-09T14:05:44.695Z" },
+ { url = "https://files.pythonhosted.org/packages/68/cf/9438b1a27426ec46a80e0a718093c7f958ef72f43eb3111862949ead3cc1/ruff-0.15.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:136c00ca2f47b0018b073f28cb5c1506642a830ea941a60354b0e8bc8076b151", size = 10621053, upload-time = "2026-04-09T14:05:52.782Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/50/e29be6e2c135e9cd4cb15fbade49d6a2717e009dff3766dd080fcb82e251/ruff-0.15.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b80a2f3c9c8a950d6237f2ca12b206bccff626139be9fa005f14feb881a1ae8", size = 10378302, upload-time = "2026-04-09T14:06:14.361Z" },
+ { url = "https://files.pythonhosted.org/packages/18/2f/e0b36a6f99c51bb89f3a30239bc7bf97e87a37ae80aa2d6542d6e5150364/ruff-0.15.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e3e53c588164dc025b671c9df2462429d60357ea91af7e92e9d56c565a9f1b07", size = 10850074, upload-time = "2026-04-09T14:06:16.581Z" },
+ { url = "https://files.pythonhosted.org/packages/11/08/874da392558ce087a0f9b709dc6ec0d60cbc694c1c772dab8d5f31efe8cb/ruff-0.15.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b0c52744cf9f143a393e284125d2576140b68264a93c6716464e129a3e9adb48", size = 11358051, upload-time = "2026-04-09T14:06:18.948Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/46/602938f030adfa043e67112b73821024dc79f3ab4df5474c25fa4c1d2d14/ruff-0.15.10-py3-none-win32.whl", hash = "sha256:d4272e87e801e9a27a2e8df7b21011c909d9ddd82f4f3281d269b6ba19789ca5", size = 10588964, upload-time = "2026-04-09T14:06:07.14Z" },
+ { url = "https://files.pythonhosted.org/packages/25/b6/261225b875d7a13b33a6d02508c39c28450b2041bb01d0f7f1a83d569512/ruff-0.15.10-py3-none-win_amd64.whl", hash = "sha256:28cb32d53203242d403d819fd6983152489b12e4a3ae44993543d6fe62ab42ed", size = 11745044, upload-time = "2026-04-09T14:05:39.473Z" },
+ { url = "https://files.pythonhosted.org/packages/58/ed/dea90a65b7d9e69888890fb14c90d7f51bf0c1e82ad800aeb0160e4bacfd/ruff-0.15.10-py3-none-win_arm64.whl", hash = "sha256:601d1610a9e1f1c2165a4f561eeaa2e2ea1e97f3287c5aa258d3dab8b57c6188", size = 11035607, upload-time = "2026-04-09T14:05:47.593Z" },
]
[[package]]
name = "tomli"
-version = "2.3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236 },
- { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084 },
- { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832 },
- { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052 },
- { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555 },
- { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128 },
- { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445 },
- { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165 },
- { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891 },
- { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796 },
- { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121 },
- { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070 },
- { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859 },
- { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296 },
- { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124 },
- { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698 },
- { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819 },
- { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766 },
- { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771 },
- { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586 },
- { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792 },
- { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909 },
- { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946 },
- { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705 },
- { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244 },
- { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637 },
- { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925 },
- { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045 },
- { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835 },
- { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109 },
- { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930 },
- { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964 },
- { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065 },
- { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088 },
- { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193 },
- { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488 },
- { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669 },
- { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709 },
- { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563 },
- { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756 },
- { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408 },
-]
-
-[[package]]
-name = "tox"
-version = "4.30.3"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
+version = "2.4.1"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" },
+ { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" },
+ { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" },
+ { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" },
+ { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" },
+ { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" },
+ { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" },
+ { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" },
+ { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" },
+ { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" },
+ { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" },
+ { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" },
+ { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" },
+ { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" },
+ { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" },
+ { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" },
+ { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" },
+ { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" },
+ { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" },
+ { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" },
+ { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" },
+ { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" },
+ { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" },
+ { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" },
+ { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" },
+ { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
+]
+
+[[package]]
+name = "tomli-w"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
+]
+
+[[package]]
+name = "towncrier"
+version = "25.8.0"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "cachetools", marker = "python_full_version < '3.10'" },
- { name = "chardet", marker = "python_full_version < '3.10'" },
- { name = "colorama", marker = "python_full_version < '3.10'" },
- { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "packaging", marker = "python_full_version < '3.10'" },
- { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "pluggy", marker = "python_full_version < '3.10'" },
- { name = "pyproject-api", version = "1.9.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "tomli", marker = "python_full_version < '3.10'" },
- { name = "typing-extensions", marker = "python_full_version < '3.10'" },
- { name = "virtualenv", marker = "python_full_version < '3.10'" },
+ { name = "click" },
+ { name = "jinja2" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/51/b2/cee55172e5e10ce030b087cd3ac06641e47d08a3dc8d76c17b157dba7558/tox-4.30.3.tar.gz", hash = "sha256:f3dd0735f1cd4e8fbea5a3661b77f517456b5f0031a6256432533900e34b90bf", size = 202799 }
+sdist = { url = "https://files.pythonhosted.org/packages/c2/eb/5bf25a34123698d3bbab39c5bc5375f8f8bcbcc5a136964ade66935b8b9d/towncrier-25.8.0.tar.gz", hash = "sha256:eef16d29f831ad57abb3ae32a0565739866219f1ebfbdd297d32894eb9940eb1", size = 76322, upload-time = "2025-08-30T11:41:55.393Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e2/e4/8bb9ce952820df4165eb34610af347665d6cb436898a234db9d84d093ce6/tox-4.30.3-py3-none-any.whl", hash = "sha256:a9f17b4b2d0f74fe0d76207236925a119095011e5c2e661a133115a8061178c9", size = 175512 },
+ { url = "https://files.pythonhosted.org/packages/42/06/8ba22ec32c74ac1be3baa26116e3c28bc0e76a5387476921d20b6fdade11/towncrier-25.8.0-py3-none-any.whl", hash = "sha256:b953d133d98f9aeae9084b56a3563fd2519dfc6ec33f61c9cd2c61ff243fb513", size = 65101, upload-time = "2025-08-30T11:41:53.644Z" },
]
[[package]]
name = "tox"
-version = "4.32.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
+version = "4.52.1"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "cachetools", marker = "python_full_version >= '3.10'" },
- { name = "chardet", marker = "python_full_version >= '3.10'" },
- { name = "colorama", marker = "python_full_version >= '3.10'" },
- { name = "filelock", version = "3.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "packaging", marker = "python_full_version >= '3.10'" },
- { name = "platformdirs", version = "4.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "pluggy", marker = "python_full_version >= '3.10'" },
- { name = "pyproject-api", version = "1.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "tomli", marker = "python_full_version == '3.10.*'" },
- { name = "typing-extensions", marker = "python_full_version == '3.10.*'" },
- { name = "virtualenv", marker = "python_full_version >= '3.10'" },
+ { name = "cachetools" },
+ { name = "colorama" },
+ { name = "filelock" },
+ { name = "packaging" },
+ { name = "platformdirs" },
+ { name = "pluggy" },
+ { name = "pyproject-api" },
+ { name = "python-discovery" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+ { name = "tomli-w" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+ { name = "virtualenv" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/59/bf/0e4dbd42724cbae25959f0e34c95d0c730df03ab03f54d52accd9abfc614/tox-4.32.0.tar.gz", hash = "sha256:1ad476b5f4d3679455b89a992849ffc3367560bbc7e9495ee8a3963542e7c8ff", size = 203330 }
+sdist = { url = "https://files.pythonhosted.org/packages/bc/fb/d7d634eb513f741ffd40f4c262b7feea19d5c616882eb554045c620670a6/tox-4.52.1.tar.gz", hash = "sha256:297e71ea0ae4ef3acc45cb5fdf080b74537e6ecb5eea7d4646fa7322ca10473e", size = 273730, upload-time = "2026-04-09T16:46:45.838Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/fc/cc/e09c0d663a004945f82beecd4f147053567910479314e8d01ba71e5d5dea/tox-4.32.0-py3-none-any.whl", hash = "sha256:451e81dc02ba8d1ed20efd52ee409641ae4b5d5830e008af10fe8823ef1bd551", size = 175905 },
+ { url = "https://files.pythonhosted.org/packages/3a/70/0d4fb1eefa05a24ca2f58272b4c4718090dd5ed7e38b54b9a7e757bfafc8/tox-4.52.1-py3-none-any.whl", hash = "sha256:3c4eef0a64f319df0b67dacdb7edcfeda87c8cc722581af5d98dd54f3ffdd8ef", size = 212179, upload-time = "2026-04-09T16:46:44.5Z" },
]
[[package]]
name = "tox-uv"
-version = "1.28.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
+version = "1.35.0"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "packaging", marker = "python_full_version < '3.10'" },
- { name = "tomli", marker = "python_full_version < '3.10'" },
- { name = "tox", version = "4.30.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "typing-extensions", marker = "python_full_version < '3.10'" },
- { name = "uv", marker = "python_full_version < '3.10'" },
+ { name = "tox-uv-bare" },
+ { name = "uv" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/f2/23/5c7f9bb50f25b4e9096a3b38e4b67604d3030388fdb6e645e54226b30cb0/tox_uv-1.28.1.tar.gz", hash = "sha256:fb01a34f49496e51e198196ee73a2be19ecd9cdbdc2508d86b981314c3d1b058", size = 23518 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/f1/54/46abc86d4cf2844d34dbd8e7bd0e4ed226ed6fb6a9a9481a85d4daff28ca/tox_uv-1.28.1-py3-none-any.whl", hash = "sha256:29f64076c57bda643b0c25dcb925011a35bfa57b0a94d3aaf550607d31e9f30a", size = 17363 },
+ { url = "https://files.pythonhosted.org/packages/06/f1/c1ccb36e2d37a81bdd08a57a798cbc93153644207f1d97803bb37a4396c0/tox_uv-1.35.0-py3-none-any.whl", hash = "sha256:633a56ef0c9ccd6c83a491faf5591cb0eba07de0876f6d504aa35b645736d232", size = 6375, upload-time = "2026-04-09T17:59:38.96Z" },
]
[[package]]
-name = "tox-uv"
-version = "1.29.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
+name = "tox-uv-bare"
+version = "1.35.0"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "packaging", marker = "python_full_version >= '3.10'" },
- { name = "tomli", marker = "python_full_version == '3.10.*'" },
- { name = "tox", version = "4.32.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "uv", marker = "python_full_version >= '3.10'" },
+ { name = "packaging" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+ { name = "tox" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/4f/90/06752775b8cfadba8856190f5beae9f552547e0f287e0246677972107375/tox_uv-1.29.0.tar.gz", hash = "sha256:30fa9e6ad507df49d3c6a2f88894256bcf90f18e240a00764da6ecab1db24895", size = 23427 }
+sdist = { url = "https://files.pythonhosted.org/packages/b1/4b/2c929a667d33201bb3852c190ec02634ff8a82ad1ab2c69eeda33cf09cc6/tox_uv_bare-1.35.0.tar.gz", hash = "sha256:1bd935ac7be9b7b60f84c15e138a7f5b2bd26f6cc4c9e0bfa77dd50c7a8ab746", size = 31389, upload-time = "2026-04-09T17:59:41.471Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/5c/17/221d62937c4130b044bb437caac4181e7e13d5536bbede65264db1f0ac9f/tox_uv-1.29.0-py3-none-any.whl", hash = "sha256:b1d251286edeeb4bc4af1e24c8acfdd9404700143c2199ccdbb4ea195f7de6cc", size = 17254 },
+ { url = "https://files.pythonhosted.org/packages/13/e8/fe21bd86ce17871ea840f1069b9f9cc0cf39cda0eaab455ae03f4f94e06b/tox_uv_bare-1.35.0-py3-none-any.whl", hash = "sha256:280302d08ef4f26fc9e9a8d07a94a3e64642fd5a91d70cf50628620f7c305868", size = 21879, upload-time = "2026-04-09T17:59:40.342Z" },
]
[[package]]
name = "typeguard"
-version = "4.4.4"
-source = { registry = "https://pypi.org/simple" }
+version = "4.5.1"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
- { name = "importlib-metadata", marker = "python_full_version < '3.10'" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/c7/68/71c1a15b5f65f40e91b65da23b8224dad41349894535a97f63a52e462196/typeguard-4.4.4.tar.gz", hash = "sha256:3a7fd2dffb705d4d0efaed4306a704c89b9dee850b688f060a8b1615a79e5f74", size = 75203 }
+sdist = { url = "https://files.pythonhosted.org/packages/2b/e8/66e25efcc18542d58706ce4e50415710593721aae26e794ab1dec34fb66f/typeguard-4.5.1.tar.gz", hash = "sha256:f6f8ecbbc819c9bc749983cc67c02391e16a9b43b8b27f15dc70ed7c4a007274", size = 80121, upload-time = "2026-02-19T16:09:03.392Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl", hash = "sha256:b5f562281b6bfa1f5492470464730ef001646128b180769880468bd84b68b09e", size = 34874 },
+ { url = "https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl", hash = "sha256:44d2bf329d49a244110a090b55f5f91aa82d9a9834ebfd30bcc73651e4a8cc40", size = 36745, upload-time = "2026-02-19T16:09:01.6Z" },
]
[[package]]
name = "types-protobuf"
-version = "6.32.1.20251210"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.10'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c2/59/c743a842911887cd96d56aa8936522b0cd5f7a7f228c96e81b59fced45be/types_protobuf-6.32.1.20251210.tar.gz", hash = "sha256:c698bb3f020274b1a2798ae09dc773728ce3f75209a35187bd11916ebfde6763", size = 63900, upload-time = "2025-12-10T03:14:25.451Z" }
+version = "7.34.1.20260408"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/5b/b1/4521e68c2cc17703d80eb42796751345376dd4c706f84007ef5e7c707774/types_protobuf-7.34.1.20260408.tar.gz", hash = "sha256:e2c0a0430e08c75b52671a6f0035abfdcc791aad12af16274282de1b721758ab", size = 68835, upload-time = "2026-04-08T04:26:43.613Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/aa/43/58e75bac4219cbafee83179505ff44cae3153ec279be0e30583a73b8f108/types_protobuf-6.32.1.20251210-py3-none-any.whl", hash = "sha256:2641f78f3696822a048cfb8d0ff42ccd85c25f12f871fbebe86da63793692140", size = 77921, upload-time = "2025-12-10T03:14:24.477Z" },
-]
-
-[[package]]
-name = "types-protobuf"
-version = "6.32.1.20260221"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14'",
- "python_full_version == '3.13.*'",
- "python_full_version >= '3.10' and python_full_version < '3.13'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/5f/e2/9aa4a3b2469508bd7b4e2ae11cbedaf419222a09a1b94daffcd5efca4023/types_protobuf-6.32.1.20260221.tar.gz", hash = "sha256:6d5fb060a616bfb076cbb61b4b3c3969f5fc8bec5810f9a2f7e648ee5cbcbf6e", size = 64408, upload-time = "2026-02-21T03:55:13.916Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2e/e8/1fd38926f9cf031188fbc5a96694203ea6f24b0e34bd64a225ec6f6291ba/types_protobuf-6.32.1.20260221-py3-none-any.whl", hash = "sha256:da7cdd947975964a93c30bfbcc2c6841ee646b318d3816b033adc2c4eb6448e4", size = 77956, upload-time = "2026-02-21T03:55:12.894Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/b5/0bc9874d89c58fb0ce851e150055ce732d254dbb10b06becbc7635d0d635/types_protobuf-7.34.1.20260408-py3-none-any.whl", hash = "sha256:ebbcd4e27b145aef6a59bc0cb6c013b3528151c1ba5e7f7337aeee355d276a5e", size = 86012, upload-time = "2026-04-08T04:26:42.566Z" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 }
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 },
+ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]
[[package]]
name = "typing-inspection"
version = "0.4.2"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 }
+sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 },
+ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
]
[[package]]
name = "urllib3"
-version = "2.6.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/5e/1d/0f3a93cca1ac5e8287842ed4eebbd0f7a991315089b1a0b01c7788aa7b63/urllib3-2.6.1.tar.gz", hash = "sha256:5379eb6e1aba4088bae84f8242960017ec8d8e3decf30480b3a1abdaa9671a3f", size = 432678 }
+version = "2.6.3"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/bc/56/190ceb8cb10511b730b564fb1e0293fa468363dbad26145c34928a60cb0c/urllib3-2.6.1-py3-none-any.whl", hash = "sha256:e67d06fe947c36a7ca39f4994b08d73922d40e6cca949907be05efa6fd75110b", size = 131138 },
+ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
]
[[package]]
name = "uv"
-version = "0.9.17"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/52/1a/cb0c37ae8513b253bcbc13d42392feb7d95ea696eb398b37535a28df9040/uv-0.9.17.tar.gz", hash = "sha256:6d93ab9012673e82039cfa7f9f66f69b388bc3f910f9e8a2ebee211353f620aa", size = 3815957 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2b/e2/b6e2d473bdc37f4d86307151b53c0776e9925de7376ce297e92eab2e8894/uv-0.9.17-py3-none-linux_armv6l.whl", hash = "sha256:c708e6560ae5bc3cda1ba93f0094148ce773b6764240ced433acf88879e57a67", size = 21254511 },
- { url = "https://files.pythonhosted.org/packages/d5/40/75f1529a8bf33cc5c885048e64a014c3096db5ac7826c71e20f2b731b588/uv-0.9.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:233b3d90f104c59d602abf434898057876b87f64df67a37129877d6dab6e5e10", size = 20384366 },
- { url = "https://files.pythonhosted.org/packages/de/30/b3a343893681a569cbb74f8747a1c24e5f18ca9e07de0430aceaf9389ef4/uv-0.9.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4b8e5513d48a267bfa180ca7fefaf6f27b1267e191573b3dba059981143e88ef", size = 18924624 },
- { url = "https://files.pythonhosted.org/packages/21/56/9daf8bbe4a9a36eb0b9257cf5e1e20f9433d0ce996778ccf1929cbe071a4/uv-0.9.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:8f283488bbcf19754910cc1ae7349c567918d6367c596e5a75d4751e0080eee0", size = 20671687 },
- { url = "https://files.pythonhosted.org/packages/9f/c8/4050ff7dc692770092042fcef57223b8852662544f5981a7f6cac8fc488d/uv-0.9.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9cf8052ba669dc17bdba75dae655094d820f4044990ea95c01ec9688c182f1da", size = 20861866 },
- { url = "https://files.pythonhosted.org/packages/84/d4/208e62b7db7a65cb3390a11604c59937e387d07ed9f8b63b54edb55e2292/uv-0.9.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06749461b11175a884be193120044e7f632a55e2624d9203398808907d346aad", size = 21858420 },
- { url = "https://files.pythonhosted.org/packages/86/2c/91288cd5a04db37dfc1e0dad26ead84787db5832d9836b4cc8e0fa7f3c53/uv-0.9.17-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:35eb1a519688209160e48e1bb8032d36d285948a13b4dd21afe7ec36dc2a9787", size = 23471658 },
- { url = "https://files.pythonhosted.org/packages/44/ba/493eba650ffad1df9e04fd8eabfc2d0aebc23e8f378acaaee9d95ca43518/uv-0.9.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2bfb60a533e82690ab17dfe619ff7f294d053415645800d38d13062170230714", size = 23062950 },
- { url = "https://files.pythonhosted.org/packages/9a/9e/f7f679503c06843ba59451e3193f35fb7c782ff0afc697020d4718a7de46/uv-0.9.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd0f3e380ff148aff3d769e95a9743cb29c7f040d7ef2896cafe8063279a6bc1", size = 22080299 },
- { url = "https://files.pythonhosted.org/packages/32/2e/76ba33c7d9efe9f17480db1b94d3393025062005e346bb8b3660554526da/uv-0.9.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2c3d25fbd8f91b30d0fac69a13b8e2c2cd8e606d7e6e924c1423e4ff84e616", size = 22087554 },
- { url = "https://files.pythonhosted.org/packages/14/db/ef4aae4a6c49076db2acd2a7b0278ddf3dbf785d5172b3165018b96ba2fb/uv-0.9.17-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:330e7085857e4205c5196a417aca81cfbfa936a97dd2a0871f6560a88424ebf2", size = 20823225 },
- { url = "https://files.pythonhosted.org/packages/11/73/e0f816cacd802a1cb25e71de9d60e57fa1f6c659eb5599cef708668618cc/uv-0.9.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:45880faa9f6cf91e3cda4e5f947da6a1004238fdc0ed4ebc18783a12ce197312", size = 22004893 },
- { url = "https://files.pythonhosted.org/packages/15/6b/700f6256ee191136eb06e40d16970a4fc687efdccf5e67c553a258063019/uv-0.9.17-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:8e775a1b94c6f248e22f0ce2f86ed37c24e10ae31fb98b7e1b9f9a3189d25991", size = 20853850 },
- { url = "https://files.pythonhosted.org/packages/bc/6a/13f02e2ed6510223c40f74804586b09e5151d9319f93aab1e49d91db13bb/uv-0.9.17-py3-none-musllinux_1_1_i686.whl", hash = "sha256:8650c894401ec96488a6fd84a5b4675e09be102f5525c902a12ba1c8ef8ff230", size = 21322623 },
- { url = "https://files.pythonhosted.org/packages/d0/18/2d19780cebfbec877ea645463410c17859f8070f79c1a34568b153d78e1d/uv-0.9.17-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:673066b72d8b6c86be0dae6d5f73926bcee8e4810f1690d7b8ce5429d919cde3", size = 22290123 },
- { url = "https://files.pythonhosted.org/packages/77/69/ab79bde3f7b6d2ac89f839ea40411a9cf3e67abede2278806305b6ba797e/uv-0.9.17-py3-none-win32.whl", hash = "sha256:7407d45afeae12399de048f7c8c2256546899c94bd7892dbddfae6766616f5a3", size = 20070709 },
- { url = "https://files.pythonhosted.org/packages/08/a0/ab5b1850197bf407d095361b214352e40805441791fed35b891621cb1562/uv-0.9.17-py3-none-win_amd64.whl", hash = "sha256:22fcc26755abebdf366becc529b2872a831ce8bb14b36b6a80d443a1d7f84d3b", size = 22122852 },
- { url = "https://files.pythonhosted.org/packages/37/ef/813cfedda3c8e49d8b59a41c14fcc652174facfd7a1caf9fee162b40ccbd/uv-0.9.17-py3-none-win_arm64.whl", hash = "sha256:6761076b27a763d0ede2f5e72455d2a46968ff334badf8312bb35988c5254831", size = 20435751 },
+version = "0.11.6"
+source = { registry = "https://pypi.org/simple/" }
+sdist = { url = "https://files.pythonhosted.org/packages/dd/f3/8aceeab67ea69805293ab290e7ca8cc1b61a064d28b8a35c76d8eba063dd/uv-0.11.6.tar.gz", hash = "sha256:e3b21b7e80024c95ff339fcd147ac6fc3dd98d3613c9d45d3a1f4fd1057f127b", size = 4073298, upload-time = "2026-04-09T12:09:01.738Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1f/fe/4b61a3d5ad9d02e8a4405026ccd43593d7044598e0fa47d892d4dafe44c9/uv-0.11.6-py3-none-linux_armv6l.whl", hash = "sha256:ada04dcf89ddea5b69d27ac9cdc5ef575a82f90a209a1392e930de504b2321d6", size = 23780079, upload-time = "2026-04-09T12:08:56.609Z" },
+ { url = "https://files.pythonhosted.org/packages/52/db/d27519a9e1a5ffee9d71af1a811ad0e19ce7ab9ae815453bef39dd479389/uv-0.11.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5be013888420f96879c6e0d3081e7bcf51b539b034a01777041934457dfbedf3", size = 23214721, upload-time = "2026-04-09T12:09:32.228Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/8f/4399fa8b882bd7e0efffc829f73ab24d117d490a93e6bc7104a50282b854/uv-0.11.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ffa5dc1cbb52bdce3b8447e83d1601a57ad4da6b523d77d4b47366db8b1ceb18", size = 21750109, upload-time = "2026-04-09T12:09:24.357Z" },
+ { url = "https://files.pythonhosted.org/packages/32/07/5a12944c31c3dda253632da7a363edddb869ed47839d4d92a2dc5f546c93/uv-0.11.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:bfb107b4dade1d2c9e572992b06992d51dd5f2136eb8ceee9e62dd124289e825", size = 23551146, upload-time = "2026-04-09T12:09:10.439Z" },
+ { url = "https://files.pythonhosted.org/packages/79/5b/2ec8b0af80acd1016ed596baf205ddc77b19ece288473b01926c4a9cf6db/uv-0.11.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:9e2fe7ce12161d8016b7deb1eaad7905a76ff7afec13383333ca75e0c4b5425d", size = 23331192, upload-time = "2026-04-09T12:09:34.792Z" },
+ { url = "https://files.pythonhosted.org/packages/62/7d/eea35935f2112b21c296a3e42645f3e4b1aa8bcd34dcf13345fbd55134b7/uv-0.11.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ed9c6f70c25e8dfeedddf4eddaf14d353f5e6b0eb43da9a14d3a1033d51d915", size = 23337686, upload-time = "2026-04-09T12:09:18.522Z" },
+ { url = "https://files.pythonhosted.org/packages/21/47/2584f5ab618f6ebe9bdefb2f765f2ca8540e9d739667606a916b35449eec/uv-0.11.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d68a013e609cebf82077cbeeb0809ed5e205257814273bfd31e02fc0353bbfc2", size = 25008139, upload-time = "2026-04-09T12:09:03.983Z" },
+ { url = "https://files.pythonhosted.org/packages/95/81/497ae5c1d36355b56b97dc59f550c7e89d0291c163a3f203c6f341dff195/uv-0.11.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93f736dddca03dae732c6fdea177328d3bc4bf137c75248f3d433c57416a4311", size = 25712458, upload-time = "2026-04-09T12:09:07.598Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/1c/74083238e4fab2672b63575b9008f1ea418b02a714bcfcf017f4f6a309b6/uv-0.11.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e96a66abe53fced0e3389008b8d2eff8278cfa8bb545d75631ae8ceb9c929aba", size = 24915507, upload-time = "2026-04-09T12:08:50.892Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/ee/e14fe10ba455a823ed18233f12de6699a601890905420b5c504abf115116/uv-0.11.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b096311b2743b228df911a19532b3f18fa420bf9530547aecd6a8e04bbfaccd", size = 24971011, upload-time = "2026-04-09T12:08:54.016Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/a1/7b9c83eaadf98e343317ff6384a7227a4855afd02cdaf9696bcc71ee6155/uv-0.11.6-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:904d537b4a6e798015b4a64ff5622023bd4601b43b6cd1e5f423d63471f5e948", size = 23640234, upload-time = "2026-04-09T12:09:15.735Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/51/75ccdd23e76ff1703b70eb82881cd5b4d2a954c9679f8ef7e0136ef2cfab/uv-0.11.6-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:4ed8150c26b5e319381d75ae2ce6aba1e9c65888f4850f4e3b3fa839953c90a5", size = 24452664, upload-time = "2026-04-09T12:09:26.875Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/86/ace80fe47d8d48b5e3b5aee0b6eb1a49deaacc2313782870250b3faa36f5/uv-0.11.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1c9218c8d4ac35ca6e617fb0951cc0ab2d907c91a6aea2617de0a5494cf162c0", size = 24494599, upload-time = "2026-04-09T12:09:37.368Z" },
+ { url = "https://files.pythonhosted.org/packages/05/2d/4b642669b56648194f026de79bc992cbfc3ac2318b0a8d435f3c284934e8/uv-0.11.6-py3-none-musllinux_1_1_i686.whl", hash = "sha256:9e211c83cc890c569b86a4183fcf5f8b6f0c7adc33a839b699a98d30f1310d3a", size = 24159150, upload-time = "2026-04-09T12:09:13.17Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/24/7eecd76fe983a74fed1fc700a14882e70c4e857f1d562a9f2303d4286c12/uv-0.11.6-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d2a1d2089afdf117ad19a4c1dd36b8189c00ae1ad4135d3bfbfced82342595cf", size = 25164324, upload-time = "2026-04-09T12:08:59.56Z" },
+ { url = "https://files.pythonhosted.org/packages/27/e0/bbd4ba7c2e5067bbba617d87d306ec146889edaeeaa2081d3e122178ca08/uv-0.11.6-py3-none-win32.whl", hash = "sha256:6e8344f38fa29f85dcfd3e62dc35a700d2448f8e90381077ef393438dcd5012e", size = 22865693, upload-time = "2026-04-09T12:09:21.415Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/33/1983ce113c538a856f2d620d16e39691962ecceef091a84086c5785e32e5/uv-0.11.6-py3-none-win_amd64.whl", hash = "sha256:a28bea69c1186303d1200f155c7a28c449f8a4431e458fcf89360cc7ef546e40", size = 25371258, upload-time = "2026-04-09T12:09:40.52Z" },
+ { url = "https://files.pythonhosted.org/packages/35/01/be0873f44b9c9bc250fcbf263367fcfc1f59feab996355bcb6b52fff080d/uv-0.11.6-py3-none-win_arm64.whl", hash = "sha256:a78f6d64b9950e24061bc7ec7f15ff8089ad7f5a976e7b65fcadce58fe02f613", size = 23869585, upload-time = "2026-04-09T12:09:29.425Z" },
]
[[package]]
name = "virtualenv"
-version = "20.35.4"
-source = { registry = "https://pypi.org/simple" }
+version = "21.2.1"
+source = { registry = "https://pypi.org/simple/" }
dependencies = [
{ name = "distlib" },
- { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "filelock", version = "3.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "platformdirs", version = "4.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+ { name = "filelock" },
+ { name = "platformdirs" },
+ { name = "python-discovery" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095 },
-]
-
-[[package]]
-name = "zipp"
-version = "3.23.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547 }
+sdist = { url = "https://files.pythonhosted.org/packages/97/c5/aff062c66b42e2183201a7ace10c6b2e959a9a16525c8e8ca8e59410d27a/virtualenv-21.2.1.tar.gz", hash = "sha256:b66ffe81301766c0d5e2208fc3576652c59d44e7b731fc5f5ed701c9b537fa78", size = 5844770, upload-time = "2026-04-09T18:47:11.482Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276 },
+ { url = "https://files.pythonhosted.org/packages/20/0e/f083a76cb590e60dff3868779558eefefb8dfb7c9ed020babc7aa014ccbf/virtualenv-21.2.1-py3-none-any.whl", hash = "sha256:bd16b49c53562b28cf1a3ad2f36edb805ad71301dee70ddc449e5c88a9f919a2", size = 5828326, upload-time = "2026-04-09T18:47:09.331Z" },
]