Skip to content

Commit 80a63b6

Browse files
committed
Merge branch 'release/1.1'
* release/1.1: bump 1.1 add ability to print generic attributes (--echo-attr))
2 parents 5afad29 + cea982f commit 80a63b6

File tree

7 files changed

+203
-53
lines changed

7 files changed

+203
-53
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

MANIFEST.in

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
graft docs
12
include CHANGELOG
3+
include LICENSE
4+
include pytest_echo.py
25
include README.rst
36
include setup.py
47
include test_echo.py
58
include tox.ini
6-
include LICENSE
7-
graft docs

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: 87 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,88 @@
33
from pprint import pformat
44

55

6-
__version__ = '1.0'
6+
__version__ = '1.1'
7+
8+
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)
788

889

990
def _get_version(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: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,111 @@
11
import os
2-
import pytest
2+
import sys
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

2029
def test_echo_env(testdir):
2130
os.environ['PYTESTECHO'] = '123'
2231
result = testdir.runpytest('--echo-env=PYTESTECHO')
23-
assert "PYTESTECHO: 123" in result.stdout.lines
32+
result.stdout.fnmatch_lines([
33+
"PYTESTECHO: 123",
34+
])
2435

2536

2637
def test_echo_version(testdir):
2738
result = testdir.runpytest('--echo-version=pytest_echo')
28-
assert "pytest_echo: %s" % pytest_echo.__version__ in result.stdout.lines
39+
result.stdout.fnmatch_lines(["pytest_echo: %s" % pytest_echo.__version__])
2940

3041

3142
def test_echo_all(testdir):
3243
os.environ['PYTESTECHO'] = '123'
33-
result = testdir.runpytest('--echo-version=pytest_echo', '--echo-env=PYTESTECHO')
34-
assert "PYTESTECHO: 123" in result.stdout.lines
35-
assert "pytest_echo: %s" % pytest_echo.__version__ in result.stdout.lines
44+
result = testdir.runpytest('--echo-version=pytest_echo',
45+
'--echo-env=PYTESTECHO')
46+
result.stdout.fnmatch_lines(["PYTESTECHO: 123"])
47+
result.stdout.fnmatch_lines(["pytest_echo: %s" % pytest_echo.__version__])
48+
49+
50+
def test_echo_attr(testdir):
51+
result = testdir.runpytest('--echo-attr=test_echo.ATTR_INT')
52+
result.stdout.fnmatch_lines(['test_echo.ATTR_INT: 111'])
53+
54+
55+
def test_echo_attr_dict(testdir):
56+
result = testdir.runpytest('--echo-attr=test_echo.ATTR_DICT.key')
57+
result.stdout.fnmatch_lines(["test_echo.ATTR_DICT.key: 'value'"])
58+
59+
60+
def test_echo_attr_list(testdir):
61+
result = testdir.runpytest('--echo-attr=test_echo.ATTR_LIST.2')
62+
result.stdout.fnmatch_lines([u"test_echo.ATTR_LIST.2: 13"])
63+
64+
65+
def test_echo_attr_list_inner(testdir):
66+
result = testdir.runpytest('--echo-attr=test_echo.ATTR_LIST.3.1')
67+
assert u"test_echo.ATTR_LIST.3.1: 22" in result.stdout.lines
68+
69+
70+
def test_echo_attr_list_composite(testdir):
71+
result = testdir.runpytest('--echo-attr=test_echo.ATTR_COMPOSITE.key1',
72+
'--echo-attr=test_echo.ATTR_COMPOSITE.key2.3')
73+
assert u"test_echo.ATTR_COMPOSITE.key1: 'value1'" in result.stdout.lines
74+
assert u"test_echo.ATTR_COMPOSITE.key2.3: 14" in result.stdout.lines
75+
76+
77+
def test_echo_attr_list_callable(testdir):
78+
result = testdir.runpytest('--echo-attr=test_echo.FUNC')
79+
result.stdout.fnmatch_lines([
80+
"test_echo.FUNC: <function FUNC*",
81+
])
82+
83+
84+
def test_echo_attr_object_attr(testdir):
85+
result = testdir.runpytest('--echo-attr=test_echo.dummy.attr')
86+
result.stdout.fnmatch_lines([
87+
"test_echo.dummy.attr: 1",
88+
])
89+
90+
91+
def test_echo_attr_module_object_attr(testdir):
92+
result = testdir.runpytest('--echo-attr=linecache.cache.__class__')
93+
if sys.version_info[0] == 2:
94+
match = "linecache.cache.__class__: <type 'dict'>"
95+
elif sys.version_info[0] == 3:
96+
match = "linecache.cache.__class__: <class 'dict'>"
3697

98+
result.stdout.fnmatch_lines([match])
3799

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
42100

101+
def test_django_settings(testdir):
102+
testdir.makeconftest("""
103+
def pytest_configure(config):
104+
import django
105+
from django.conf import settings # noqa
106+
settings.configure()
107+
""")
108+
result = testdir.runpytest('--echo-attr=django.conf.settings.DEBUG')
109+
result.stdout.fnmatch_lines([
110+
"django.conf.settings.DEBUG: False",
111+
])

tox.ini

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@ envlist=py27,py33
33

44
[testenv]
55
deps = pytest
6+
django
7+
pip
8+
69
commands =
10+
pip install -e {toxinidir}
711
py.test --junitxml={envlogdir}/junit-{envname}.xml {posargs:test_echo.py}
812

913
[pytest]
1014
pep8ignore = E128 E302
1115
doc/conf.py ALL
16+
1217
addopts =
13-
-vvv
1418
--tb=short
1519
--capture=no
1620

21+
[flake8]
22+
exclude = .tox,docs
23+

0 commit comments

Comments
 (0)