Skip to content

Commit ca4470c

Browse files
fix(pyramid): use correct package (backport #2959) (#2965)
* fix(pyramid): use correct package (#2959) * Add pserve pyramid application This should reproduce a bug exposed by including routes from another module in the package. * Fix Pyramid package The Pyramid package needs to be set to the frame above what it normally is because our patching introduces an additional frame in the call stack. * Add comments (cherry picked from commit 5391e58) * Fix snapshot conflict error field is no longer encoded when zero Co-authored-by: Kyle Verhoog <[email protected]> Co-authored-by: Kyle Verhoog <[email protected]>
1 parent 0e36210 commit ca4470c

11 files changed

+158
-3
lines changed

ddtrace/contrib/pyramid/patch.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@ def traced_init(wrapped, instance, args, kwargs):
6262
# If the tweens are explicitly set with 'pyramid.tweens', we need to
6363
# explicitly set our tween too since `add_tween` will be ignored.
6464
insert_tween_if_needed(trace_settings)
65+
66+
# The original Configurator.__init__ looks up two levels to find the package
67+
# name if it is not provided. This has to be replicated here since this patched
68+
# call will occur at the same level in the call stack.
69+
if not kwargs.get("package", None):
70+
from pyramid.path import caller_package
71+
72+
kwargs["package"] = caller_package(level=2)
73+
6574
kwargs["settings"] = trace_settings
6675
wrapped(*args, **kwargs)
6776
trace_pyramid(instance)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
Set the correct package name in the Pyramid instrumentation. This should
5+
fix an issue where the incorrect package name was being used which would
6+
crash the application when trying to do relative imports within Pyramid
7+
(e.g. when including routes from a relative path).

riotfile.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,7 @@ def select_pys(min_version=MIN_PYTHON_VERSION, max_version=MAX_PYTHON_VERSION):
944944
pkgs={
945945
"requests": [latest],
946946
"webtest": [latest],
947+
"tests/contrib/pyramid/pserve_app": [latest],
947948
"pyramid": [
948949
"~=1.7",
949950
"~=1.8",

tests/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import contextlib
2+
import os
23
import sys
34

45
import pytest
@@ -61,7 +62,7 @@ def snapshot(request):
6162
assert len(marks) < 2, "Multiple snapshot marks detected"
6263
if marks:
6364
snap = marks[0]
64-
token = _request_token(request)
65+
token = _request_token(request).replace(" ", "_").replace(os.path.sep, "_")
6566
with _snapshot_context(token, *snap.args, **snap.kwargs) as snapshot:
6667
yield snapshot
6768
else:
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from pyramid.config import Configurator
2+
from pyramid.response import Response
3+
4+
from ddtrace import tracer
5+
from ddtrace.filters import TraceFilter
6+
7+
8+
class PingFilter(TraceFilter):
9+
def process_trace(self, trace):
10+
# Filter out all traces with trace_id = 1
11+
# This is done to prevent certain traces from being included in snapshots and
12+
# accomplished by propagating an http trace id of 1 with the request to the webserver.
13+
return None if trace and trace[0].trace_id == 1 else trace
14+
15+
16+
tracer.configure(
17+
settings={
18+
"FILTERS": [PingFilter()],
19+
}
20+
)
21+
22+
23+
def tracer_shutdown(request):
24+
tracer.shutdown()
25+
return Response("shutdown")
26+
27+
28+
def main(global_config, **settings):
29+
"""This function returns a Pyramid WSGI application."""
30+
with Configurator(settings=settings) as config:
31+
config.add_route("tracer-shutdown", "/shutdown-tracer")
32+
config.add_view(tracer_shutdown, route_name="tracer-shutdown")
33+
# This will trigger usage of the package name reproing an issue
34+
# reported in #2942.
35+
config.include(".routes")
36+
config.scan()
37+
return config.make_wsgi_app()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from pyramid.response import Response
2+
3+
4+
def hello_world(request):
5+
return Response("Hello World!")
6+
7+
8+
def includeme(config):
9+
config.add_route("hello", "/")
10+
config.add_view(hello_world, route_name="hello")
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
###
2+
# app configuration
3+
# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
4+
###
5+
6+
[app:main]
7+
use = egg:pserve_test_app
8+
9+
[server:main]
10+
use = egg:waitress#main
11+
listen = localhost:8000
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from setuptools import setup, find_packages
2+
3+
setup(
4+
name="pserve_test_app",
5+
packages=find_packages(),
6+
include_package_data=True,
7+
zip_safe=False,
8+
entry_points={
9+
"paste.app_factory": [
10+
"main = app:main",
11+
],
12+
},
13+
)

tests/contrib/pyramid/test_pyramid.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,13 @@ def test_distributed_tracing_disabled(self):
113113
assert span.get_tag(ORIGIN_KEY) != "synthetics"
114114

115115

116+
@pytest.fixture
117+
def pyramid_app():
118+
return "ddtrace-run python tests/contrib/pyramid/app/app.py"
119+
120+
116121
@pytest.fixture(scope="function")
117-
def pyramid_client(snapshot):
122+
def pyramid_client(snapshot, pyramid_app):
118123
"""Runs a Pyramid app in a subprocess and returns a client which can be used to query it.
119124
120125
Traces are flushed by invoking a tracer.shutdown() using a /shutdown-tracer route
@@ -124,7 +129,7 @@ def pyramid_client(snapshot):
124129
env = os.environ.copy()
125130
env["SERVER_PORT"] = str(SERVER_PORT)
126131

127-
cmd = ["ddtrace-run", "python", "tests/contrib/pyramid/app/app.py"]
132+
cmd = pyramid_app.split(" ")
128133
proc = subprocess.Popen(
129134
cmd,
130135
stdout=subprocess.PIPE,
@@ -146,6 +151,13 @@ def pyramid_client(snapshot):
146151
proc.terminate()
147152

148153

154+
@pytest.mark.parametrize(
155+
"pyramid_app",
156+
[
157+
"ddtrace-run pserve tests/contrib/pyramid/pserve_app/development.ini",
158+
"ddtrace-run python tests/contrib/pyramid/app/app.py",
159+
],
160+
)
149161
@pytest.mark.snapshot()
150162
def test_simple_pyramid_app_endpoint(pyramid_client):
151163
r = pyramid_client.get("/")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[[
2+
{
3+
"name": "pyramid.request",
4+
"service": "pyramid",
5+
"resource": "GET hello",
6+
"trace_id": 0,
7+
"span_id": 1,
8+
"parent_id": 0,
9+
"type": "web",
10+
"error": 0,
11+
"meta": {
12+
"http.method": "GET",
13+
"http.status_code": "200",
14+
"http.url": "http://localhost:8000/",
15+
"pyramid.route.name": "hello",
16+
"runtime-id": "3cfe7c1618be4fc4a090f2a7dab9dcc9"
17+
},
18+
"metrics": {
19+
"_dd.agent_psr": 1.0,
20+
"_dd.measured": 1,
21+
"_dd.tracer_kr": 1.0,
22+
"_sampling_priority_v1": 1,
23+
"system.pid": 656011
24+
},
25+
"duration": 128359,
26+
"start": 1635529254779423958
27+
}]]

0 commit comments

Comments
 (0)