Skip to content

Commit 4d67a79

Browse files
committed
add ability to print generic attributes (--echo-attr))
1 parent 5afad29 commit 4d67a79

File tree

6 files changed

+184
-45
lines changed

6 files changed

+184
-45
lines changed

CHANGELOG

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
1.1
2+
---
3+
* removed dump of django settings and related options (--echo-settings)
4+
* add dump generic attributes (--echo-attr)
5+
16
1.0
27
---
38

README.rst

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
pytest-echo
22
===========
33

4-
Echoing environment variables, package version and django settings
4+
Print environment variables, package version and django settings.
55

6-
Usage
7-
-----
6+
Useful in the continuous integration to dump env configuration.
7+
8+
9+
Install
10+
-------
811

912
install via::
1013

@@ -21,7 +24,7 @@ Examples
2124
Dump environment variables
2225
~~~~~~~~~~~~~~~~~~~~~~~~~~
2326

24-
.. code-block:: python
27+
.. code-block:: sh
2528
2629
$ py.test --echo-env=HOME
2730
============================= test session starts =========================
@@ -33,7 +36,7 @@ Dump environment variables
3336
Dump package version
3437
~~~~~~~~~~~~~~~~~~~~
3538

36-
.. code-block:: python
39+
.. code-block:: sh
3740
3841
$ py.test --echo-version=pytest_echo
3942
============================= test session starts =========================
@@ -42,17 +45,19 @@ Dump package version
4245
plugins: echo, pydev, cov, cache, django
4346
4447
45-
Dump django settings
48+
Dump attributes
4649
~~~~~~~~~~~~~~~~~~~~
4750

48-
.. code-block:: python
51+
.. code-block:: sh
4952
50-
$ py.test --echo-settings=DEBUG
53+
$ py.test --echo-attr=django.conf.settings.DEBUG
5154
============================= test session starts =========================
5255
platform linux2 -- Python 2.7.4 -- py-1.4.22 -- pytest-2.6.0 -- /bin/python
5356
DEBUG: False
5457
plugins: echo, pydev, cov, cache, django
5558
59+
.. warning:: Be careful when use ``--echo-attr``. It load any module in the path and this will
60+
execute any module's level code
5661

5762

5863

pytest_echo.py

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,87 @@
66
__version__ = '1.0'
77

88

9+
class RetrieveException(Exception):
10+
pass
11+
12+
13+
def get_attr(obj, attr, default='NOT FOUND'):
14+
"""Recursive get object's attribute. May use dot notation.
15+
16+
>>> class C(object): pass
17+
>>> a = C()
18+
>>> a.b = C()
19+
>>> a.b.c = 4
20+
>>> get_attr(a, 'b.c')
21+
4
22+
23+
>>> get_attr(a, 'b.c.y', None)
24+
25+
>>> get_attr(a, 'b.c.y', 1)
26+
1
27+
>>> get_attr([0,1,2], '2')
28+
2
29+
>>> get_attr([0,1,(21, 22)], '2.1')
30+
22
31+
>>> get_attr({'key': 11}, 'key')
32+
11
33+
>>> get_attr({'key': {'key': 11}}, 'key.key')
34+
11
35+
"""
36+
37+
if '.' not in attr:
38+
try:
39+
if hasattr(obj, attr):
40+
return getattr(obj, attr, default)
41+
elif isinstance(obj, (list, tuple, set)):
42+
return obj[int(attr)]
43+
elif isinstance(obj, dict):
44+
return obj[attr]
45+
else:
46+
return default
47+
except Exception as e:
48+
return str(e)
49+
else:
50+
L = attr.split('.')
51+
return get_attr(get_attr(obj, L[0], default), '.'.join(L[1:]), default)
52+
53+
54+
def get_module_attribute(path):
55+
"""
56+
Returns a attribute value base on it's full path.
57+
The `attribute` can be either a module attribute (ie. os.path.curdir)
58+
or a object attribute (ie. linecache.cache.__class__)
59+
60+
Warning: Be careful when use thi function as it load any module in the path
61+
and this will execute any module's level code
62+
63+
:param path: full path to the attribute
64+
:return:
65+
66+
>>> print get_module_attribute('linecache.cache.__class__')
67+
<type 'dict'>
68+
>>> print get_module_attribute('os.path.curdir')
69+
'.'
70+
"""
71+
parts = path.split('.')
72+
parent = ""
73+
pkg = None
74+
try:
75+
for i, el in enumerate(parts):
76+
try:
77+
if parent:
78+
a = "{}.{}".format(parent, parts[i])
79+
else:
80+
a = parts[i]
81+
pkg = __import__(a, fromlist=[parent])
82+
parent = a
83+
except ImportError:
84+
if hasattr(pkg, el):
85+
return pformat(get_attr(pkg, ".".join(parts[i:])))
86+
except Exception as e:
87+
return str(e)
88+
89+
990
def _get_version(package_name):
1091
try:
1192
pkg = __import__(package_name)
@@ -20,30 +101,11 @@ def _get_version(package_name):
20101
return attr
21102

22103

23-
def _get_settings(entry):
24-
try:
25-
from django.core.exceptions import ImproperlyConfigured
26-
27-
try:
28-
from django.conf import settings
29-
30-
getattr(settings, 'DEBUG') # force settings loading
31-
except ImproperlyConfigured:
32-
settings.configure()
33-
try:
34-
entry = getattr(settings, entry)
35-
return pformat(entry)
36-
except AttributeError as e:
37-
return "ERROR: %s" % str(e)
38-
except ImportError:
39-
return ""
40-
41-
42104
def pytest_report_header(config):
43105
ret = []
44-
if config.option.echo_settings:
45-
ret.append("\n".join(["%s: %s" % (k, _get_settings(k))
46-
for k in config.option.echo_settings]))
106+
if config.option.echo_attribues:
107+
ret.append("\n".join(["%s: %s" % (k, get_module_attribute(k))
108+
for k in config.option.echo_attribues]))
47109
if config.option.echo_envs:
48110
ret.append("\n".join(["%s: %s" % (k, os.environ.get(k, "<not set>"))
49111
for k in config.option.echo_envs]))
@@ -60,5 +122,5 @@ def pytest_addoption(parser):
60122
default=[], help="environment to print")
61123
group.addoption('--echo-version', action='append', dest="echo_versions",
62124
default=[], help="package version to print")
63-
group.addoption('--echo-settings', action='append', dest="echo_settings",
64-
default=[], help="django settings to print")
125+
group.addoption('--echo-attr', action='append', dest="echo_attribues",
126+
default=[], help="attribute to print (full path)")

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
setup(
77
name='pytest-echo',
8-
description='pytest plugin with mechanisms for echoing environment variables, package version and django settings',
8+
description='pytest plugin with mechanisms for echoing environment '
9+
'variables, package version and django settings',
910
long_description=open("README.rst").read(),
1011
version=__version__,
1112
author='Stefano Apostolico',

test_echo.py

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,26 @@
33
import pytest_echo
44

55
pytest_plugins = "pytester",
6-
try:
7-
import django
86

9-
django_present = True
10-
except ImportError:
11-
django_present = False
7+
ATTR_INT = 111
8+
ATTR_DICT = {'key': 'value'}
9+
ATTR_LIST = [11, 12, 13, (21, 22)]
10+
ATTR_COMPOSITE = {'key1': 'value1', 'key2': [11, 12, 13, 14], 'key3': 99}
11+
12+
13+
class Dummy():
14+
attr = 1
15+
16+
17+
def FUNC():
18+
pass
19+
20+
21+
dummy = Dummy()
1222

1323

1424
def test_version():
1525
import pytest_echo
16-
1726
assert pytest_echo.__version__
1827

1928

@@ -30,13 +39,68 @@ def test_echo_version(testdir):
3039

3140
def test_echo_all(testdir):
3241
os.environ['PYTESTECHO'] = '123'
33-
result = testdir.runpytest('--echo-version=pytest_echo', '--echo-env=PYTESTECHO')
42+
result = testdir.runpytest('--echo-version=pytest_echo',
43+
'--echo-env=PYTESTECHO')
3444
assert "PYTESTECHO: 123" in result.stdout.lines
3545
assert "pytest_echo: %s" % pytest_echo.__version__ in result.stdout.lines
3646

3747

38-
@pytest.mark.skipif('not django_present')
39-
def test_echo_settings(testdir):
40-
result = testdir.runpytest('--echo-settings=DEBUG')
41-
assert 'DEBUG: False' in result.stdout.lines
48+
def test_echo_attr(testdir):
49+
result = testdir.runpytest('--echo-attr=test_echo.ATTR_INT')
50+
assert 'test_echo.ATTR_INT: 111' in result.stdout.lines
51+
52+
53+
def test_echo_attr_dict(testdir):
54+
result = testdir.runpytest('--echo-attr=test_echo.ATTR_DICT.key')
55+
assert u"test_echo.ATTR_DICT.key: 'value'" in result.stdout.lines
56+
57+
58+
def test_echo_attr_list(testdir):
59+
result = testdir.runpytest('--echo-attr=test_echo.ATTR_LIST.2')
60+
assert u"test_echo.ATTR_LIST.2: 13" in result.stdout.lines
61+
62+
63+
def test_echo_attr_list_inner(testdir):
64+
result = testdir.runpytest('--echo-attr=test_echo.ATTR_LIST.3.1')
65+
assert u"test_echo.ATTR_LIST.3.1: 22" in result.stdout.lines
66+
67+
68+
def test_echo_attr_list_composite(testdir):
69+
result = testdir.runpytest('--echo-attr=test_echo.ATTR_COMPOSITE.key1',
70+
'--echo-attr=test_echo.ATTR_COMPOSITE.key2.3')
71+
assert u"test_echo.ATTR_COMPOSITE.key1: 'value1'" in result.stdout.lines
72+
assert u"test_echo.ATTR_COMPOSITE.key2.3: 14" in result.stdout.lines
73+
74+
75+
def test_echo_attr_list_callable(testdir):
76+
result = testdir.runpytest('--echo-attr=test_echo.FUNC')
77+
result.stdout.fnmatch_lines([
78+
"test_echo.FUNC: <function FUNC*",
79+
])
80+
81+
82+
def test_echo_attr_object_attr(testdir):
83+
result = testdir.runpytest('--echo-attr=test_echo.dummy.attr')
84+
result.stdout.fnmatch_lines([
85+
"test_echo.dummy.attr: 1",
86+
])
87+
88+
89+
def test_echo_attr_module_object_attr(testdir):
90+
result = testdir.runpytest('--echo-attr=linecache.cache.__class__')
91+
result.stdout.fnmatch_lines([
92+
"linecache.cache.__class__: <type 'dict'>",
93+
])
94+
4295

96+
def test_django_settings(testdir):
97+
p = testdir.makeconftest("""
98+
def pytest_configure(config):
99+
import django
100+
from django.conf import settings # noqa
101+
settings.configure()
102+
""")
103+
result = testdir.runpytest('--echo-attr=django.conf.settings.DEBUG')
104+
result.stdout.fnmatch_lines([
105+
"django.conf.settings.DEBUG: False",
106+
])

tox.ini

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ commands =
1010
pep8ignore = E128 E302
1111
doc/conf.py ALL
1212
addopts =
13-
-vvv
1413
--tb=short
1514
--capture=no
1615

16+
[flake8]
17+
exclude = .tox,docs
18+

0 commit comments

Comments
 (0)