Skip to content

refactor: consolidate much shared code in ddtrace.internal.utils#18587

Draft
emmettbutler wants to merge 33 commits into
mainfrom
emmett.butler/consolidate-utils
Draft

refactor: consolidate much shared code in ddtrace.internal.utils#18587
emmettbutler wants to merge 33 commits into
mainfrom
emmett.butler/consolidate-utils

Conversation

@emmettbutler

@emmettbutler emmettbutler commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Description

This change moves a bunch of sources from ddtrace.internal to ddtrace.internal.utils in the interest of colocating as much as possible code that's used by multiple product components of the library. This colocation will enable the eventual separation of shared functionality into a separate package.

ddtrace.internal.utils is chosen as the target location due to its high concentration of code that's both shared by multiple products and not specific to any of those products.

Existing imports are unchanged because of the presence of reexports at the locations of the moved modules.

Testing

No functional changes, so existing tests are appropriate.

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Jun 11, 2026

Copy link
Copy Markdown

Circular import analysis

🚨 New circular imports detected 🚨

195 new circular import(s) have been introduced by this PR:

Show new cycles (showing 5 of 195 shortest)
ddtrace.internal.telemetry -> ddtrace.internal.telemetry.writer -> ddtrace.internal.packages -> ddtrace.internal.module -> ddtrace.internal.utils.module -> ddtrace.internal.utils -> ddtrace.internal.core -> ddtrace._trace.span -> ddtrace.internal.settings._config -> ddtrace.internal.telemetry
ddtrace.internal.telemetry -> ddtrace.internal.telemetry.writer -> ddtrace.internal.telemetry.dependency_tracker -> ddtrace.internal.packages -> ddtrace.internal.module -> ddtrace.internal.utils.module -> ddtrace.internal.utils -> ddtrace.internal.core -> ddtrace._trace.span -> ddtrace.internal.settings._config -> ddtrace.internal.telemetry
ddtrace.internal.telemetry -> ddtrace.internal.telemetry.writer -> ddtrace.internal.packages -> ddtrace.internal.module -> ddtrace.internal.utils.module -> ddtrace.internal.utils -> ddtrace.internal.core -> ddtrace._trace.span -> ddtrace._trace._span_pointer -> ddtrace._trace.telemetry -> ddtrace.internal.telemetry
ddtrace.internal.telemetry -> ddtrace.internal.telemetry.writer -> ddtrace.internal.telemetry.dependency_tracker -> ddtrace.internal.packages -> ddtrace.internal.module -> ddtrace.internal.utils.module -> ddtrace.internal.utils -> ddtrace.internal.core -> ddtrace._trace.span -> ddtrace._trace._span_pointer -> ddtrace._trace.telemetry -> ddtrace.internal.telemetry
ddtrace -> ddtrace.internal.telemetry -> ddtrace.internal.telemetry.writer -> ddtrace.internal.packages -> ddtrace.internal.module -> ddtrace.internal.utils.module -> ddtrace.internal.utils -> ddtrace.internal.core -> ddtrace._trace.span -> ddtrace.internal.settings._config -> ddtrace.internal.schema -> ddtrace.internal.schema.span_attribute_schema -> ddtrace

To see all cycles, download the cycles-base.json and cycles-pr.json artifacts from this CI job and run:

uv run --script scripts/import-analysis/cycles.py compare cycles-base.json cycles-pr.json

Please consider refactoring your changes in accordance to the Separation of Concerns principle.

⚠️ Existing circular imports

There are 4779 circular imports that already exist on the base branch and have not been changed by this PR.

Show existing cycles (showing 5 of 4779 shortest)
ddtrace.internal.datastreams -> ddtrace.internal.datastreams.kombu -> ddtrace.internal.datastreams
ddtrace.internal.datastreams -> ddtrace.internal.datastreams.botocore -> ddtrace.internal.datastreams
ddtrace.internal.core -> ddtrace._trace.span -> ddtrace.internal.core
ddtrace.internal.datastreams -> ddtrace.internal.datastreams.aiokafka -> ddtrace.internal.datastreams
ddtrace.internal.datastreams -> ddtrace.internal.datastreams.kafka -> ddtrace.internal.datastreams

To see all cycles, download the cycles-base.json and cycles-pr.json artifacts from this CI job and run:

uv run --script scripts/import-analysis/cycles.py compare cycles-base.json cycles-pr.json

✅ Circular imports removed

195 circular import(s) have been removed by this PR.

Show removed cycles (showing 5 of 195 shortest)
ddtrace.internal.telemetry -> ddtrace.internal.telemetry.writer -> ddtrace.internal.packages -> ddtrace.internal.module -> ddtrace.internal.utils -> ddtrace.internal.core -> ddtrace._trace.span -> ddtrace.internal.settings._config -> ddtrace.internal.telemetry
ddtrace.internal.telemetry -> ddtrace.internal.telemetry.writer -> ddtrace.internal.packages -> ddtrace.internal.module -> ddtrace.internal.utils -> ddtrace.internal.core -> ddtrace._trace.span -> ddtrace._trace._span_pointer -> ddtrace._trace.telemetry -> ddtrace.internal.telemetry
ddtrace.internal.telemetry -> ddtrace.internal.telemetry.writer -> ddtrace.internal.telemetry.dependency_tracker -> ddtrace.internal.packages -> ddtrace.internal.module -> ddtrace.internal.utils -> ddtrace.internal.core -> ddtrace._trace.span -> ddtrace.internal.settings._config -> ddtrace.internal.telemetry
ddtrace.internal.telemetry -> ddtrace.internal.telemetry.writer -> ddtrace.internal.telemetry.dependency_tracker -> ddtrace.internal.packages -> ddtrace.internal.module -> ddtrace.internal.utils -> ddtrace.internal.core -> ddtrace._trace.span -> ddtrace._trace._span_pointer -> ddtrace._trace.telemetry -> ddtrace.internal.telemetry
ddtrace -> ddtrace.internal.telemetry -> ddtrace.internal.telemetry.writer -> ddtrace.internal.packages -> ddtrace.internal.module -> ddtrace.internal.utils -> ddtrace.internal.core -> ddtrace._trace.span -> ddtrace.internal.settings._config -> ddtrace.internal.schema -> ddtrace.internal.schema.span_attribute_schema -> ddtrace

To see all cycles, download the cycles-base.json and cycles-pr.json artifacts from this CI job and run:

uv run --script scripts/import-analysis/cycles.py compare cycles-base.json cycles-pr.json

@datadog-prod-us1-3

datadog-prod-us1-3 Bot commented Jun 11, 2026

Copy link
Copy Markdown

Pipelines  Tests

Fix all issues with BitsAI

⚠️ Warnings

🚦 10 Pipeline jobs failed

DataDog/apm-reliability/dd-trace-py | build linux serverless: [amd64, cp315-cp315, v113741491-d2b8243-musllinux_1_2_x86_64, 1]   View in Datadog   GitLab

DataDog/apm-reliability/dd-trace-py | build linux: [amd64, cp315-cp315, v113741491-d2b8243-musllinux_1_2_x86_64]   View in Datadog   GitLab

DataDog/apm-reliability/dd-trace-py | build linux: [arm64, cp315-cp315, v113741357-d2b8243-manylinux2014_aarch64]   View in Datadog   GitLab

View all 10 failed jobs.

ℹ️ Info

No other issues found (see more)

🧪 All tests passed
❄️ No new flaky tests detected

🔄 Datadog auto-retried 2 jobs - 2 passed on retry View in Datadog

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 7dc57f7 | Docs | Datadog PR Page | Give us feedback!

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 02e5a7b2d3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread ddtrace/internal/encoding.py Outdated
@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codeowners resolved as

ddtrace/internal/_exceptions.py                                         @DataDog/asm-python
ddtrace/internal/constants.py                                           @DataDog/apm-core-python
ddtrace/internal/schema/__init__.py                                     @DataDog/apm-core-python @DataDog/apm-idm-python
ddtrace/internal/schema/processor.py                                    @DataDog/apm-core-python @DataDog/apm-idm-python
ddtrace/internal/schema/span_attribute_schema.py                        @DataDog/apm-core-python @DataDog/apm-idm-python

@pr-commenter

pr-commenter Bot commented Jun 16, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-07-09 16:51:45

Comparing candidate commit 7dc57f7 in PR branch emmett.butler/consolidate-utils with baseline commit 857c18a in branch main.

Found 0 performance improvements and 3 performance regressions! Performance is the same for 611 metrics, 10 unstable metrics.

scenario:iastaspects-swapcase_aspect

  • 🟥 execution_time [+45.787µs; +56.309µs] or [+13.028%; +16.022%]

scenario:iastaspectsospath-ospathbasename_aspect

  • 🟥 execution_time [+92.884µs; +105.257µs] or [+21.775%; +24.676%]

scenario:span-start

  • 🟥 execution_time [+1.247ms; +1.408ms] or [+8.238%; +9.299%]

@typotter typotter left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm for ffe

@gh-worker-ownership-write-b05516 gh-worker-ownership-write-b05516 Bot removed the request for review from a team July 7, 2026 15:11

@P403n1x87 P403n1x87 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the idea, but I feel that this PR doesn't make a step forward towards modularisation yet. IMO it would give us more value if we addressed the existing problems (like partitioning the monolithic constants.py module, moving context-specific utilities under the more relevant component etc...) first so that we don't end up bloating the new generic package with product-specific stuff. My feeling is that, once we have done this, ddtrace.internal would already contain just the content of the new package, without the need to move things under utils.

Comment thread ddtrace/internal/schema/__init__.py
Comment thread ddtrace/internal/utils/constants.py Outdated
Comment thread ddtrace/internal/utils/_exceptions.py Outdated
@gh-worker-ownership-write-b05516 gh-worker-ownership-write-b05516 Bot removed the request for review from a team July 9, 2026 15:20
@emmettbutler emmettbutler requested a review from P403n1x87 July 9, 2026 15:21
@emmettbutler emmettbutler changed the title chore: consolidate much shared code in ddtrace.internal.utils refactor: consolidate much shared code in ddtrace.internal.utils Jul 9, 2026

@P403n1x87 P403n1x87 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving as this is a non-functional change. But I still believe that this work is premature and that the other tasks mentioned in the other review comments should be prioritised over this. I expect that we will have to undo most of the changes here (e.g. remove the new shim modules), so I feel there are a few unnecessary moves and throwaway work that we can spare by preparing the ground a bit better.

Comment thread mypy.ini
ignore_errors = true
no_implicit_reexport = false

# ddtrace.internal.* shims re-export from ddtrace.internal.utils.* via `import *`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to move to __all__ and/or explicit re-exports vs just import *?

it seems with these changes we'll stop doing type checking on anything in the new utils/*.py modules now?

is that why warn_return_any = false is now needed? seems like we might not want that ?

@mabdinur mabdinur left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving with a few non-blocking follow ups.

@mabdinur mabdinur left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow ups:

Comment thread mypy.ini
files = ddtrace,
docs
exclude = ddtrace/appsec/_iast/_taint_tracking/cmake-build-debug/|ddtrace/appsec/_iast/_taint_tracking/_vendor/|ddtrace/appsec/_iast/_taint_tracking/_deps/|ddtrace/internal/datadog/profiling/build|ddtrace/profiling/collector/test/build/|tests/profiling/collector/pprof_\d.*_pb2\.py
exclude = ddtrace/internal/constants\.py|ddtrace/internal/compat\.py|ddtrace/internal/module\.py|ddtrace/internal/threads\.py|ddtrace/internal/logger\.py|ddtrace/internal/_exceptions\.py|ddtrace/internal/schema/__init__\.py|ddtrace/internal/schema/processor\.py|ddtrace/internal/schema/span_attribute_schema\.py|ddtrace/internal/wrapping/__init__\.py|ddtrace/internal/wrapping/context\.py|ddtrace/internal/wrapping/generators\.py|ddtrace/internal/wrapping/asyncs\.py|ddtrace/appsec/_iast/_taint_tracking/cmake-build-debug/|ddtrace/appsec/_iast/_taint_tracking/_vendor/|ddtrace/appsec/_iast/_taint_tracking/_deps/|ddtrace/internal/datadog/profiling/build|ddtrace/profiling/collector/test/build/|tests/profiling/collector/pprof_\d.*_pb2\.py

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exclude list adds ddtrace/internal/constants.py, _exceptions.py, and schema/*, but none of those files have any code changes in this branch. Looks like scope creep unrelated to the utils consolidation. Can this be reverted or split into its own PR with its own justification?

Comment thread mypy.ini
check_untyped_defs = false

[mypy-ddtrace.internal.threads]
[mypy-ddtrace.internal.utils.threads]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section points at ddtrace.internal.utils.threads, but that module does not exist on this branch (the threads move was reverted, ddtrace/internal/threads.py has zero diff). Looks like a leftover from the revert, can you remove this section along with the threads.py entry added to exclude above?

return isinstance(o, wrapt_class)


__all__ = list(locals().keys())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This overwrites the curated __all__ above and re-exports everything, including stdlib imports and typing names. It also rebinds dunder attributes like __name__ and __file__ in any module that does from ... import * on this file (verified locally). Not breaking anything today since the shim is a one liner, but a narrower explicit __all__ would be safer.

return False


__all__ = list(locals().keys())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same __all__ = list(locals().keys()) concern as in compat.py, this re-exports everything including stdlib imports and rebinds dunder attributes on star import.

_globals["__getattr__"] = __getattr__


__all__ = list(locals().keys())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same __all__ = list(locals().keys()) concern as in compat.py.

return None


__all__ = list(locals().keys())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same __all__ = list(locals().keys()) concern as in compat.py.

instrs[-1:] = ASYNC_GEN_ASSEMBLY.bind(lineno=lineno)


__all__ = list(locals().keys())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same __all__ = list(locals().keys()) concern as in compat.py.

return record.uwc if record is not None else None


__all__ = list(locals().keys())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same __all__ = list(locals().keys()) concern as in compat.py.

instrs[-1:] = GENERATOR_ASSEMBLY.bind(lineno=lineno)


__all__ = list(locals().keys())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same __all__ = list(locals().keys()) concern as in compat.py.

env["DD_TRACE_SPAN_ATTRIBUTE_SCHEMA"] = schema
env["PYTHONWARNINGS"] = "ignore::UserWarning:ddtrace.internal.module"
env["PYTHONWARNINGS"] = "ignore::UserWarning:ddtrace.internal.utils.module"
out, err, status, _ = ddtrace_run_python_code_in_subprocess(code, env=env)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assert err == b"", err.decode() check right after this line was deleted rather than fixed. That suggests the move causes something to print to stderr here that was not there before. Worth finding out what and fixing that instead of dropping the check.

@emmettbutler emmettbutler marked this pull request as draft July 13, 2026 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog/no-changelog A changelog entry is not required for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants