Skip to content

Commit

Permalink
Merge branch 'main' into emmett.butler/remove-propagate-calls
Browse files Browse the repository at this point in the history
  • Loading branch information
emmettbutler authored May 1, 2024
2 parents 6d85245 + cc1c101 commit 13240e7
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 27 deletions.
5 changes: 5 additions & 0 deletions ddtrace/internal/packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ def is_third_party(path: Path) -> bool:
return package.name in _third_party_packages()


@cached()
def is_user_code(path: Path) -> bool:
return not (is_stdlib(path) or is_third_party(path))


@cached()
def is_distribution_available(name: str) -> bool:
"""Determine if a distribution is available in the current environment."""
Expand Down
34 changes: 20 additions & 14 deletions ddtrace/internal/symbol_db/symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from dataclasses import field
import dis
from enum import Enum
import http
from http.client import HTTPResponse
from inspect import CO_VARARGS
from inspect import CO_VARKEYWORDS
from inspect import isasyncgenfunction
Expand Down Expand Up @@ -31,7 +31,6 @@
from ddtrace.internal.logger import get_logger
from ddtrace.internal.module import BaseModuleWatchdog
from ddtrace.internal.module import origin
from ddtrace.internal.packages import is_stdlib
from ddtrace.internal.runtime import get_runtime_id
from ddtrace.internal.safety import _isinstance
from ddtrace.internal.utils.cache import cached
Expand All @@ -50,10 +49,10 @@


@cached()
def is_from_stdlib(obj: t.Any) -> t.Optional[bool]:
def is_from_user_code(obj: t.Any) -> t.Optional[bool]:
try:
path = origin(sys.modules[object.__getattribute__(obj, "__module__")])
return is_stdlib(path) if path is not None else None
return packages.is_user_code(path) if path is not None else None
except (AttributeError, KeyError):
return None

Expand Down Expand Up @@ -182,9 +181,6 @@ def _(cls, module: ModuleType, data: ScopeData):
symbols = []
scopes = []

if is_stdlib(module_origin):
return None

for alias, child in object.__getattribute__(module, "__dict__").items():
if _isinstance(child, ModuleType):
# We don't want to traverse other modules.
Expand Down Expand Up @@ -224,7 +220,7 @@ def _(cls, obj: type, data: ScopeData):
return None
data.seen.add(obj)

if is_from_stdlib(obj):
if not is_from_user_code(obj):
return None

symbols = []
Expand Down Expand Up @@ -347,7 +343,7 @@ def _(cls, f: FunctionType, data: ScopeData):
return None
data.seen.add(f)

if is_from_stdlib(f):
if not is_from_user_code(f):
return None

code = f.__dd_wrapped__.__code__ if hasattr(f, "__dd_wrapped__") else f.__code__
Expand Down Expand Up @@ -416,7 +412,7 @@ def _(cls, pr: property, data: ScopeData):
data.seen.add(pr.fget)

# TODO: These names don't match what is reported by the discovery.
if pr.fget is None or is_from_stdlib(pr.fget):
if pr.fget is None or not is_from_user_code(pr.fget):
return None

path = func_origin(t.cast(FunctionType, pr.fget))
Expand Down Expand Up @@ -477,7 +473,7 @@ def to_json(self) -> dict:
"scopes": [_.to_json() for _ in self._scopes],
}

def upload(self) -> http.client.HTTPResponse:
def upload(self) -> HTTPResponse:
body, headers = multipart(
parts=[
FormData(
Expand Down Expand Up @@ -509,14 +505,24 @@ def __len__(self) -> int:


def is_module_included(module: ModuleType) -> bool:
# Check if module name matches the include patterns
if symdb_config._includes_re.match(module.__name__):
return True

package = packages.module_to_package(module)
if package is None:
# Check if it is user code
module_origin = origin(module)
if module_origin is None:
return False

return symdb_config._includes_re.match(package.name) is not None
if packages.is_user_code(module_origin):
return True

# Check if the package name matches the include patterns
package = packages.filename_to_package(module_origin)
if package is not None and symdb_config._includes_re.match(package.name):
return True

return False


class SymbolDatabaseUploader(BaseModuleWatchdog):
Expand Down
24 changes: 11 additions & 13 deletions tests/internal/symbol_db/test_symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,32 +203,30 @@ def test_symbols_upload_enabled():
assert remoteconfig_poller.get_registered("LIVE_DEBUGGING_SYMBOL_DB") is not None


@pytest.mark.subprocess(
ddtrace_run=True,
env=dict(
DD_SYMBOL_DATABASE_UPLOAD_ENABLED="1",
_DD_SYMBOL_DATABASE_FORCE_UPLOAD="1",
DD_SYMBOL_DATABASE_INCLUDES="tests.submod.stuff",
),
)
@pytest.mark.subprocess(ddtrace_run=True, env=dict(DD_SYMBOL_DATABASE_INCLUDES="tests.submod.stuff"))
def test_symbols_force_upload():
from ddtrace.internal.symbol_db.symbols import ScopeType
from ddtrace.internal.symbol_db.symbols import SymbolDatabaseUploader

assert SymbolDatabaseUploader.is_installed()

contexts = []

def _upload_context(context):
contexts.append(context)

SymbolDatabaseUploader._upload_context = staticmethod(_upload_context)

SymbolDatabaseUploader.install()

def get_scope(contexts, name):
for context in (_.to_json() for _ in contexts):
for scope in context["scopes"]:
if scope["name"] == name:
return scope
raise ValueError(f"Scope {name} not found in {contexts}")

import tests.submod.stuff # noqa
import tests.submod.traced_stuff # noqa

(context,) = contexts

(scope,) = context.to_json()["scopes"]
scope = get_scope(contexts, "tests.submod.stuff")
assert scope["scope_type"] == ScopeType.MODULE
assert scope["name"] == "tests.submod.stuff"

0 comments on commit 13240e7

Please sign in to comment.