Skip to content

Commit 27aa328

Browse files
committed
added special handling for partials in get_name_from_func (#294)
fixes #293 closes #294
1 parent bfebda0 commit 27aa328

File tree

3 files changed

+81
-2
lines changed

3 files changed

+81
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
* fixed an issue with detecting names of wrapped functions that are partials (#294)
6+
37
## v3.0.1
48

59
[Check the diff](https://github.com/elastic/apm-agent-python/compare/v3.0.0...v3.0.1)

elasticapm/utils/__init__.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,19 @@
99
:license: BSD, see LICENSE for more details.
1010
"""
1111
import os
12+
from functools import partial
1213

1314
from elasticapm.utils import compat, encoding
1415

16+
try:
17+
from functools import partialmethod
18+
19+
partial_types = (partial, partialmethod)
20+
except ImportError:
21+
# Python 2
22+
partial_types = (partial,)
23+
24+
1525
default_ports = {"https": 433, "http": 80, "postgresql": 5432}
1626

1727

@@ -38,7 +48,12 @@ def varmap(func, var, context=None, name=None):
3848

3949

4050
def get_name_from_func(func):
41-
# If no view was set we ignore the request
51+
# partials don't have `__module__` or `__name__`, so we use the values from the "inner" function
52+
if isinstance(func, partial_types):
53+
return "partial({})".format(get_name_from_func(func.func))
54+
elif hasattr(func, "_partialmethod") and hasattr(func._partialmethod, "func"):
55+
return "partial({})".format(get_name_from_func(func._partialmethod.func))
56+
4257
module = func.__module__
4358

4459
if hasattr(func, "__name__"):

tests/utils/tests.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1-
from elasticapm.utils import get_url_dict
1+
from functools import partial
2+
3+
import pytest
4+
5+
from elasticapm.utils import get_name_from_func, get_url_dict
26
from elasticapm.utils.deprecation import deprecated
37

8+
try:
9+
from functools import partialmethod
10+
except ImportError:
11+
# Python 2
12+
partialmethod = None
13+
414

515
@deprecated("alternative")
616
def deprecated_function():
@@ -51,3 +61,53 @@ def test_get_url_dict():
5161
}
5262
for url, expected in data.items():
5363
assert get_url_dict(url) == expected
64+
65+
66+
def test_get_name_from_func():
67+
def x():
68+
pass
69+
70+
assert "tests.utils.tests.x" == get_name_from_func(x)
71+
72+
73+
def test_get_name_from_func_class():
74+
class X(object):
75+
def x(self):
76+
pass
77+
78+
assert "tests.utils.tests.x" == get_name_from_func(X.x)
79+
assert "tests.utils.tests.x" == get_name_from_func(X().x)
80+
81+
82+
def test_get_name_from_func_partial():
83+
def x(x):
84+
pass
85+
86+
p = partial(x, "x")
87+
assert "partial(tests.utils.tests.x)" == get_name_from_func(p)
88+
89+
90+
@pytest.mark.skipif(partialmethod is None, reason="partialmethod not available on Python 2")
91+
def test_get_name_from_func_partialmethod_unbound():
92+
class X(object):
93+
def x(self, x):
94+
pass
95+
96+
p = partialmethod(x, "x")
97+
98+
assert "partial(tests.utils.tests.x)" == get_name_from_func(X.p)
99+
100+
101+
@pytest.mark.skipif(partialmethod is None, reason="partialmethod not available on Python 2")
102+
def test_get_name_from_func_partialmethod_bound():
103+
class X(object):
104+
def x(self, x):
105+
pass
106+
107+
p = partialmethod(x, "x")
108+
109+
assert "partial(tests.utils.tests.x)" == get_name_from_func(X().p)
110+
111+
112+
def test_get_name_from_func_lambda():
113+
assert "tests.utils.tests.<lambda>" == get_name_from_func(lambda x: "x")

0 commit comments

Comments
 (0)