Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(ci_visibility): revert sub-plugin strategy and move v2 functions to try-except blocks #10196

Merged
merged 44 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
d4307ea
WIP v2 of CI Visibility plugin
romainkomorn-exdatadog Aug 2, 2024
a0c606e
Merge branch 'refs/heads/main' into romain.komorn/SDTEST-225/introduc…
romainkomorn-exdatadog Aug 2, 2024
748eb8c
properly deal with adding children
romainkomorn-exdatadog Aug 2, 2024
7dc408b
dont restart items that have already started
romainkomorn-exdatadog Aug 2, 2024
36991ba
optimize not finishing suites/modules unnecessarily
romainkomorn-exdatadog Aug 2, 2024
72447ab
don't log warning if item already exists
romainkomorn-exdatadog Aug 5, 2024
ee7e202
Merge branch 'refs/heads/main' into romain.komorn/SDTEST-225/pytest_p…
romainkomorn-exdatadog Aug 6, 2024
d78bbfd
Add inital ITR support
romainkomorn-exdatadog Aug 6, 2024
811ddc2
make children private
romainkomorn-exdatadog Aug 7, 2024
5e379fc
fix should collect coverage
romainkomorn-exdatadog Aug 7, 2024
817b235
add telemetry and imported lines dependency
romainkomorn-exdatadog Aug 7, 2024
51b23cc
Merge branch 'main' into romain.komorn/SDTEST-225/pytest_plugin_v2_it…
romainkomorn-exdatadog Aug 7, 2024
3c99d36
comment
romainkomorn-exdatadog Aug 7, 2024
9e5c6c9
Merge branch 'romain.komorn/SDTEST-225/pytest_plugin_v2_itr_support' …
romainkomorn-exdatadog Aug 7, 2024
84f5cdf
factor out source file info to util, move utils to _utils
romainkomorn-exdatadog Aug 8, 2024
626c427
Merge branch 'main' into romain.komorn/SDTEST-225/pytest_plugin_v2_it…
romainkomorn-exdatadog Aug 8, 2024
a2811df
update tests
romainkomorn-exdatadog Aug 8, 2024
6ba4880
fix parent relationship, add bunch of return type hints
romainkomorn-exdatadog Aug 9, 2024
361c0b9
stash
romainkomorn-exdatadog Aug 11, 2024
44de0d8
fix tags
romainkomorn-exdatadog Aug 11, 2024
f4eca82
Merge branch 'refs/heads/main' into romain.komorn/SDTEST-225/add_test…
romainkomorn-exdatadog Aug 12, 2024
e33d972
fix more tests
romainkomorn-exdatadog Aug 12, 2024
c099289
stash
romainkomorn-exdatadog Aug 12, 2024
817b852
more test fixes
romainkomorn-exdatadog Aug 12, 2024
0e10d27
add suite and v2 snapshots
romainkomorn-exdatadog Aug 12, 2024
8806ed2
fix hatch env name
romainkomorn-exdatadog Aug 12, 2024
e8f39dd
quote hatch env
romainkomorn-exdatadog Aug 12, 2024
6b8514a
single quote inside
romainkomorn-exdatadog Aug 12, 2024
d4282dd
Only v2
romainkomorn-exdatadog Aug 12, 2024
7749bae
3.7 fixes
romainkomorn-exdatadog Aug 12, 2024
9012b28
remove unnecessary test
romainkomorn-exdatadog Aug 13, 2024
cf90292
telemetry tweaks and log fix
romainkomorn-exdatadog Aug 13, 2024
f394ac8
fix telemetry (again)
romainkomorn-exdatadog Aug 13, 2024
f2a14f5
Merge branch 'refs/heads/main' into romain.komorn/SDTEST-225/add_test…
romainkomorn-exdatadog Aug 13, 2024
5c93c19
don't print stack
romainkomorn-exdatadog Aug 13, 2024
cc27394
another comment fix
romainkomorn-exdatadog Aug 13, 2024
69578db
move v2 functions to try-except blocks, revert sub-plugin strategy
romainkomorn-exdatadog Aug 13, 2024
9cc3811
remove test hook
romainkomorn-exdatadog Aug 13, 2024
392e5d7
remove docstring
romainkomorn-exdatadog Aug 13, 2024
a1ac5a1
Update _plugin_v2.py
romainkomorn-exdatadog Aug 13, 2024
1d71b25
Merge branch 'main' into romain.komorn/SDTEST-225/remove_sub_plugin_a…
romainkomorn-exdatadog Aug 14, 2024
d927635
Merge branch 'main' into romain.komorn/SDTEST-225/remove_sub_plugin_a…
romainkomorn-exdatadog Aug 14, 2024
4b27df4
fmt
romainkomorn-exdatadog Aug 14, 2024
32c568f
fix missed merge, apparently, and add return statements
romainkomorn-exdatadog Aug 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .circleci/config.templ.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,14 @@ jobs:
pattern: 'pytest'
snapshot: true

pytest_v2:
<<: *machine_executor
parallelism: 10
steps:
- run_hatch_env_test:
env: 'pytest_plugin_v2'
snapshot: true

unittest:
<<: *machine_executor
steps:
Expand Down
856 changes: 422 additions & 434 deletions ddtrace/contrib/pytest/_plugin_v1.py

Large diffs are not rendered by default.

419 changes: 262 additions & 157 deletions ddtrace/contrib/pytest/_plugin_v2.py

Large diffs are not rendered by default.

46 changes: 32 additions & 14 deletions ddtrace/contrib/pytest/_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass
import json
import os
from pathlib import Path
import re
import typing as t

Expand All @@ -11,16 +12,21 @@
from ddtrace.ext.ci_visibility.api import CISessionId
from ddtrace.ext.ci_visibility.api import CISourceFileInfo
from ddtrace.ext.ci_visibility.api import CISuiteId
from ddtrace.ext.ci_visibility.api import CITest
from ddtrace.ext.ci_visibility.api import CITestId
from ddtrace.internal.ci_visibility.constants import ITR_UNSKIPPABLE_REASON
from ddtrace.internal.ci_visibility.utils import get_source_lines_for_test_method
from ddtrace.internal.logger import get_logger
from ddtrace.internal.utils.cache import cached
from ddtrace.internal.utils.formats import asbool
from ddtrace.internal.utils.inspection import undecorated


log = get_logger(__name__)

_NODEID_REGEX = re.compile("^((?P<module>.*)/(?P<suite>[^/]*?))::(?P<name>.*?)$")
_NODEID_REGEX = re.compile("^(((?P<module>.*)/)?(?P<suite>[^/]*?))::(?P<name>.*?)$")

_USE_PLUGIN_V2 = asbool(os.environ.get("_DD_CIVISIBILITY_USE_PYTEST_V2", "false"))


@dataclass
Expand All @@ -47,13 +53,16 @@ def _get_names_from_item(item: pytest.Item) -> TestNames:

matches = re.match(_NODEID_REGEX, item.nodeid)
if not matches:
return TestNames(module="", suite="", test="")
return TestNames(module="unknown_module", suite="unknown_suite", test=item.name)

return TestNames(
module=matches.group("module").replace("/", "."), suite=matches.group("suite"), test=matches.group("name")
)
module_name = (matches.group("module") or "").replace("/", ".")
suite_name = matches.group("suite")
test_name = matches.group("name")

return TestNames(module=module_name, suite=suite_name, test=test_name)


@cached()
def _get_test_id_from_item(item: pytest.Item) -> CITestId:
"""Converts an item to a CITestId, which recursively includes the parent IDs

Expand All @@ -72,20 +81,25 @@ def _get_test_id_from_item(item: pytest.Item) -> CITestId:
# Test parameters are part of the test ID
parameters_json: t.Optional[str] = None
if getattr(item, "callspec", None):
parameters = {"arguments": {}, "metadata": {}} # type: Dict[str, Dict[str, str]]
parameters: t.Dict[str, t.Dict[str, str]] = {"arguments": {}, "metadata": {}}
for param_name, param_val in item.callspec.params.items():
try:
parameters["arguments"][param_name] = _encode_test_parameter(param_val)
except Exception:
parameters["arguments"][param_name] = "Could not encode"
log.warning("Failed to encode %r", param_name, exc_info=True)

parameters_json = json.dumps(parameters)

test_id = CITestId(suite_id, test_name, parameters_json)

return test_id


def _get_module_path_from_item(item: pytest.Item) -> Path:
return Path(item.nodeid.rpartition("/")[0]).absolute()


def _get_session_command(session: pytest.Session):
"""Extract and re-create pytest session command from pytest config."""
command = "pytest"
Expand Down Expand Up @@ -130,17 +144,21 @@ def _pytest_marked_to_skip(item: pytest.Item) -> bool:
if item.get_closest_marker("skip") is not None:
return True

return any([True for marker in item.iter_markers(name="skipif") if marker.args[0] is True])
return any(marker.args[0] for marker in item.iter_markers(name="skipif"))


def _is_test_unskippable(item: pytest.Item) -> bool:
"""Returns True if a test has a skipif marker with value false and reason ITR_UNSKIPPABLE_REASON"""
return any(
[
True
for marker in item.iter_markers(name="skipif")
if marker.args[0] is False
and "reason" in marker.kwargs
and marker.kwargs["reason"] is ITR_UNSKIPPABLE_REASON
]
(marker.args[0] is False and marker.kwargs.get("reason") is ITR_UNSKIPPABLE_REASON)
for marker in item.iter_markers(name="skipif")
)


def _extract_span(item):
"""Extract span from `pytest.Item` instance."""
if _USE_PLUGIN_V2:
test_id = _get_test_id_from_item(item)
return CITest.get_span(test_id)

return getattr(item, "_datadog_span", None)
54 changes: 35 additions & 19 deletions ddtrace/contrib/pytest/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

import pytest

from ddtrace.contrib.pytest._utils import _USE_PLUGIN_V2
from ddtrace.contrib.pytest._utils import _extract_span
from ddtrace.contrib.pytest._utils import _pytest_version_supports_itr
from ddtrace.internal.utils.formats import asbool


DDTRACE_HELP_MSG = "Enable tracing of pytest functions."
Expand Down Expand Up @@ -100,7 +103,6 @@ def pytest_load_initial_conftests(early_config, parser, args):
# Enables experimental use of ModuleCodeCollector for coverage collection.
from ddtrace.internal.ci_visibility.coverage import USE_DD_COVERAGE
from ddtrace.internal.logger import get_logger
from ddtrace.internal.utils.formats import asbool

log = get_logger(__name__)

Expand All @@ -113,7 +115,7 @@ def pytest_load_initial_conftests(early_config, parser, args):

try:
workspace_path = Path(extract_workspace_path())
except ValueError:
except (ValueError, FileNotFoundError):
workspace_path = Path(os.getcwd())

log.warning("Installing ModuleCodeCollector with include_paths=%s", [workspace_path])
Expand All @@ -128,25 +130,37 @@ def pytest_load_initial_conftests(early_config, parser, args):
)


# Version-specific pytest hooks
if _USE_PLUGIN_V2:
from ddtrace.contrib.pytest._plugin_v2 import pytest_collection_finish # noqa: F401
from ddtrace.contrib.pytest._plugin_v2 import pytest_configure as _versioned_pytest_configure
from ddtrace.contrib.pytest._plugin_v2 import pytest_ddtrace_get_item_module_name # noqa: F401
from ddtrace.contrib.pytest._plugin_v2 import pytest_ddtrace_get_item_suite_name # noqa: F401
from ddtrace.contrib.pytest._plugin_v2 import pytest_ddtrace_get_item_test_name # noqa: F401
from ddtrace.contrib.pytest._plugin_v2 import pytest_runtest_makereport # noqa: F401
from ddtrace.contrib.pytest._plugin_v2 import pytest_runtest_protocol # noqa: F401
from ddtrace.contrib.pytest._plugin_v2 import pytest_sessionfinish # noqa: F401
from ddtrace.contrib.pytest._plugin_v2 import pytest_sessionstart # noqa: F401
else:
from ddtrace.contrib.pytest._plugin_v1 import pytest_collection_modifyitems # noqa: F401
from ddtrace.contrib.pytest._plugin_v1 import pytest_configure as _versioned_pytest_configure
from ddtrace.contrib.pytest._plugin_v1 import pytest_ddtrace_get_item_module_name # noqa: F401
from ddtrace.contrib.pytest._plugin_v1 import pytest_ddtrace_get_item_suite_name # noqa: F401
from ddtrace.contrib.pytest._plugin_v1 import pytest_ddtrace_get_item_test_name # noqa: F401
from ddtrace.contrib.pytest._plugin_v1 import pytest_runtest_makereport # noqa: F401
from ddtrace.contrib.pytest._plugin_v1 import pytest_runtest_protocol # noqa: F401
from ddtrace.contrib.pytest._plugin_v1 import pytest_sessionfinish # noqa: F401
from ddtrace.contrib.pytest._plugin_v1 import pytest_sessionstart # noqa: F401

# Internal coverage is only used for ITR at the moment, so the hook is only added if the pytest version supports it
if _pytest_version_supports_itr():
from ddtrace.contrib.pytest._plugin_v1 import pytest_terminal_summary # noqa: F401


def pytest_configure(config):
config.addinivalue_line("markers", "dd_tags(**kwargs): add tags to current span")
if is_enabled(config):
from ddtrace.internal.utils.formats import asbool

if asbool(os.environ.get("_DD_CIVISIBILITY_USE_PYTEST_V2", "false")):
from ddtrace.internal.logger import get_logger

log = get_logger(__name__)

log.warning("The new ddtrace pytest plugin is in beta and is not currently supported")
from ._plugin_v2 import _PytestDDTracePluginV2

config.pluginmanager.register(_PytestDDTracePluginV2(), "_datadog-pytest-v2")
return

from ._plugin_v1 import _PytestDDTracePluginV1

config.pluginmanager.register(_PytestDDTracePluginV1(), "_datadog-pytest-v1")
_versioned_pytest_configure(config)


@pytest.hookimpl
Expand All @@ -161,7 +175,6 @@ def ddspan(request):
"""Return the :class:`ddtrace._trace.span.Span` instance associated with the
current test when Datadog CI Visibility is enabled.
"""
from ddtrace.contrib.pytest._plugin_v1 import _extract_span
from ddtrace.internal.ci_visibility import CIVisibility as _CIVisibility

if _CIVisibility.enabled:
Expand Down Expand Up @@ -190,3 +203,6 @@ def patch_all(request):

if request.config.getoption("ddtrace-patch-all") or request.config.getini("ddtrace-patch-all"):
ddtrace.patch_all()


# from ddtrace.contrib.pytest._plugin_v2 import pytest_collection_modifyitems
2 changes: 1 addition & 1 deletion ddtrace/contrib/pytest_bdd/_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest

from ddtrace.contrib.pytest._plugin_v1 import _extract_span as _extract_feature_span
from ddtrace.contrib.pytest._utils import _extract_span as _extract_feature_span
from ddtrace.contrib.pytest_bdd import get_version
from ddtrace.contrib.pytest_bdd.constants import FRAMEWORK
from ddtrace.contrib.pytest_bdd.constants import STEP_KIND
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/contrib/pytest_benchmark/_plugin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from ddtrace.contrib.pytest._plugin_v1 import _extract_span
from ddtrace.contrib.pytest._utils import _extract_span
from ddtrace.contrib.pytest_benchmark.constants import BENCHMARK_INFO
from ddtrace.contrib.pytest_benchmark.constants import PLUGIN_METRICS
from ddtrace.contrib.pytest_benchmark.constants import PLUGIN_OUTLIERS
Expand Down
4 changes: 4 additions & 0 deletions ddtrace/ext/ci_visibility/_ci_visibility_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ def get_parent_id(self) -> PT:


class _CIVisibilityAPIBase(abc.ABC):
class GetTagArgs(NamedTuple):
item_id: Union[_CIVisibilityChildItemIdBase, _CIVisibilityRootItemIdBase]
name: str

class SetTagArgs(NamedTuple):
item_id: Union[_CIVisibilityChildItemIdBase, _CIVisibilityRootItemIdBase]
name: str
Expand Down
Loading
Loading