Remove packaging as a runtime dependency of opentelemetry-instrumentation
Summary
opentelemetry-instrumentation declares packaging
(packaging >= 18.0) as a runtime dependency. It is used only for two small,
well-defined tasks — comparing PEP 440 version strings and parsing PEP 508
requirement specifiers — both of which can be satisfied by a tiny internal
implementation, letting us drop the external dependency.
This issue is scoped to the core opentelemetry-instrumentation package. The
same removal for the four instrumentations that also depend on packaging is
tracked separately and depends on this one (they will reuse the internal
implementation added here):
Motivation: the auto-instrumentation injector
The main driver is the OpenTelemetry auto-instrumentation injector (the Python
auto-instrumentation shipped by the OpenTelemetry Operator). The injector bundles
the instrumentation packages and all of their runtime dependencies into an
init-container image, copies that directory into a volume shared with the target
application's container, and prepends it to the application's PYTHONPATH.
As a result, every runtime dependency is injected into the user's application
process, alongside the application's own dependencies:
- Version conflicts / shadowing.
packaging is an extremely common library
that many applications already import. Injecting our copy onto PYTHONPATH
risks shadowing the version the application expects, or being shadowed — the
class of hard-to-debug failures the injector is designed to avoid.
- Payload size. Everything shipped must be baked into the init-container
image and copied at pod startup.
- Blast radius. Fewer third-party packages forced into a customer's runtime
means a smaller surface for incompatibilities.
Because packaging is used only for a narrow, stable subset of functionality,
the cost of carrying it as an injected runtime dependency outweighs the cost of a
small internal implementation.
Where it is used in opentelemetry-instrumentation
pyproject.toml: packaging >= 18.0 in dependencies.
PEP 440 version comparison (packaging.version)
src/opentelemetry/instrumentation/_semconv.py:745 — compares semantic
convention schema version strings (Version(a) > Version(b)) to select the
highest schema URL across signal types.
PEP 508 requirement parsing / PEP 440 specifier matching (packaging.requirements, and transitively packaging.specifiers / packaging.markers)
src/opentelemetry/instrumentation/dependencies.py:9 — the dependency-conflict
machinery (get_dist_dependency_conflicts, get_dependency_conflicts) parses
each instrumentation's declared instruments / instruments-any dependency
strings into Requirement objects, evaluates their environment markers
(extra == "instruments"), and checks the installed distribution version
against the requirement's specifier (req.specifier.contains(dist_version)).
src/opentelemetry/instrumentation/bootstrap.py:90 — Requirement(req) when
computing which instrumentation packages to install/print.
Proposed resolution
Add a small internal implementation under
opentelemetry.instrumentation._packaging covering just the required subset:
- PEP 440
Version parsing, normalization and ordering.
- PEP 508
Requirement parsing plus PEP 440 specifier containment and marker
evaluation.
Point _semconv.py, dependencies.py and bootstrap.py at it, and remove
packaging from opentelemetry-instrumentation's dependencies. The internal
implementation must match packaging's behavior on the inputs these sites see,
verified against packaging across a broad version/specifier corpus.
Remove
packagingas a runtime dependency ofopentelemetry-instrumentationSummary
opentelemetry-instrumentationdeclarespackaging(
packaging >= 18.0) as a runtime dependency. It is used only for two small,well-defined tasks — comparing PEP 440 version strings and parsing PEP 508
requirement specifiers — both of which can be satisfied by a tiny internal
implementation, letting us drop the external dependency.
This issue is scoped to the core
opentelemetry-instrumentationpackage. Thesame removal for the four instrumentations that also depend on
packagingistracked separately and depends on this one (they will reuse the internal
implementation added here):
packagingas a runtime dependency ofopentelemetry-instrumentation-falcon#4884 —opentelemetry-instrumentation-falconpackagingas a runtime dependency ofopentelemetry-instrumentation-flask#4885 —opentelemetry-instrumentation-flaskpackagingas a runtime dependency ofopentelemetry-instrumentation-pika#4886 —opentelemetry-instrumentation-pikapackagingas a runtime dependency ofopentelemetry-instrumentation-sqlalchemy#4887 —opentelemetry-instrumentation-sqlalchemyMotivation: the auto-instrumentation injector
The main driver is the OpenTelemetry auto-instrumentation injector (the Python
auto-instrumentation shipped by the OpenTelemetry Operator). The injector bundles
the instrumentation packages and all of their runtime dependencies into an
init-container image, copies that directory into a volume shared with the target
application's container, and prepends it to the application's
PYTHONPATH.As a result, every runtime dependency is injected into the user's application
process, alongside the application's own dependencies:
packagingis an extremely common librarythat many applications already import. Injecting our copy onto
PYTHONPATHrisks shadowing the version the application expects, or being shadowed — the
class of hard-to-debug failures the injector is designed to avoid.
image and copied at pod startup.
means a smaller surface for incompatibilities.
Because
packagingis used only for a narrow, stable subset of functionality,the cost of carrying it as an injected runtime dependency outweighs the cost of a
small internal implementation.
Where it is used in
opentelemetry-instrumentationpyproject.toml:packaging >= 18.0independencies.PEP 440 version comparison (
packaging.version)src/opentelemetry/instrumentation/_semconv.py:745— compares semanticconvention schema version strings (
Version(a) > Version(b)) to select thehighest schema URL across signal types.
PEP 508 requirement parsing / PEP 440 specifier matching (
packaging.requirements, and transitivelypackaging.specifiers/packaging.markers)src/opentelemetry/instrumentation/dependencies.py:9— the dependency-conflictmachinery (
get_dist_dependency_conflicts,get_dependency_conflicts) parseseach instrumentation's declared
instruments/instruments-anydependencystrings into
Requirementobjects, evaluates their environment markers(
extra == "instruments"), and checks the installed distribution versionagainst the requirement's specifier (
req.specifier.contains(dist_version)).src/opentelemetry/instrumentation/bootstrap.py:90—Requirement(req)whencomputing which instrumentation packages to install/print.
Proposed resolution
Add a small internal implementation under
opentelemetry.instrumentation._packagingcovering just the required subset:Versionparsing, normalization and ordering.Requirementparsing plus PEP 440 specifier containment and markerevaluation.
Point
_semconv.py,dependencies.pyandbootstrap.pyat it, and removepackagingfromopentelemetry-instrumentation'sdependencies. The internalimplementation must match
packaging's behavior on the inputs these sites see,verified against
packagingacross a broad version/specifier corpus.