Skip to content

Commit

Permalink
Merge pull request #68 from datadriventests/fix-docstring-behavior
Browse files Browse the repository at this point in the history
Fix docstring behavior
  • Loading branch information
wswld authored Feb 24, 2019
2 parents 5e226e9 + f70d2f7 commit b92ef81
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 24 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ ddt.egg-info/
/docs/_build/
.tox
.ropeproject
venv/
4 changes: 1 addition & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
language: python
python:
#- 2.6
- 2.7
#- 3.2
- 3.3
- 3.4
- 3.5
install: pip install -r requirements/test.txt

script:
- nosetests --with-cov --cover-package=ddt
- flake8 ddt.py test
Expand Down
47 changes: 37 additions & 10 deletions ddt.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
else:
_have_yaml = True

__version__ = '1.2.0'
__version__ = '1.2.1'

# These attributes will not conflict with any real python attribute
# They are added to the decorated test method and processed later
Expand Down Expand Up @@ -135,7 +135,7 @@ def mk_test_name(name, value, index=0):
return re.sub(r'\W|^(?=\d)', '_', test_name)


def feed_data(func, new_name, test_docstring, *args, **kwargs):
def feed_data(func, new_name, test_data_docstring, *args, **kwargs):
"""
This internal method decorator feeds the test data item to the test.
Expand All @@ -146,8 +146,8 @@ def wrapper(self):
wrapper.__name__ = new_name
wrapper.__wrapped__ = func
# set docstring if exists
if test_docstring is not None:
wrapper.__doc__ = test_docstring
if test_data_docstring is not None:
wrapper.__doc__ = test_data_docstring
else:
# Try to call format on the docstring
if func.__doc__:
Expand Down Expand Up @@ -177,7 +177,6 @@ def add_test(cls, test_name, test_docstring, func, *args, **kwargs):
def process_file_data(cls, name, func, file_attr):
"""
Process the parameter in the `file_data` decorator.
"""
cls_path = os.path.abspath(inspect.getsourcefile(cls))
data_file_path = os.path.join(os.path.dirname(cls_path), file_attr)
Expand Down Expand Up @@ -223,7 +222,6 @@ def func(*args):
def _add_tests_from_data(cls, name, func, data):
"""
Add tests from data loaded from the data file into the class
"""
for i, elem in enumerate(data):
if isinstance(data, dict):
Expand All @@ -238,6 +236,23 @@ def _add_tests_from_data(cls, name, func, data):
add_test(cls, test_name, test_name, func, value)


def _is_primitive(obj):
"""Finds out if the obj is a "primitive". It is somewhat hacky but it works.
"""
return not hasattr(obj, '__dict__')


def _get_test_data_docstring(func, value):
"""Returns a docstring based on the following resolution strategy:
1. Passed value is not a "primitive" and has a docstring, then use it.
2. In all other cases return None, i.e the test name is used.
"""
if not _is_primitive(value) and value.__doc__:
return value.__doc__
else:
return None


def ddt(cls):
"""
Class decorator for subclasses of ``unittest.TestCase``.
Expand Down Expand Up @@ -266,15 +281,27 @@ def ddt(cls):
if hasattr(func, DATA_ATTR):
for i, v in enumerate(getattr(func, DATA_ATTR)):
test_name = mk_test_name(name, getattr(v, "__name__", v), i)
test_docstring = getattr(v, "__doc__", None)
test_data_docstring = _get_test_data_docstring(func, v)
if hasattr(func, UNPACK_ATTR):
if isinstance(v, tuple) or isinstance(v, list):
add_test(cls, test_name, test_docstring, func, *v)
add_test(
cls,
test_name,
test_data_docstring,
func,
*v
)
else:
# unpack dictionary
add_test(cls, test_name, test_docstring, func, **v)
add_test(
cls,
test_name,
test_data_docstring,
func,
**v
)
else:
add_test(cls, test_name, test_docstring, func, v)
add_test(cls, test_name, test_data_docstring, func, v)
delattr(cls, name)
elif hasattr(func, FILE_ATTR):
file_attr = getattr(func, FILE_ATTR)
Expand Down
41 changes: 31 additions & 10 deletions test/test_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,17 @@ def test_ddt_data_doc_attribute():
Test the ``__doc__`` attribute handling of ``data`` items with ``ddt``
"""

def hello():
def func_w_doc():
"""testFunctionDocstring {6}
:param: None
:return: None
"""
pass

def func_wo_doc():
pass

class Myint(int):
pass

Expand All @@ -263,29 +266,47 @@ class Mytest(object):
d2 = Myint(2)
d2.__name__ = 'case2'

data_hello = data(d1, d2)(hello)
setattr(Mytest, 'test_hello', data_hello)
data_hello = data(d1, d2, {'test': True})(func_w_doc)
data_hello2 = data(d1, d2, {'test': True})(func_wo_doc)

setattr(Mytest, 'first_test', data_hello)
setattr(Mytest, 'second_test', data_hello2)
ddt_mytest = ddt(Mytest)

assert_equal(
getattr(
getattr(ddt_mytest, 'test_hello_1_case1'), '__doc__'), d1.__doc__
getattr(ddt_mytest, 'first_test_1_case1'), '__doc__'), d1.__doc__
)
assert_equal(
getattr(
getattr(ddt_mytest, 'first_test_2_case2'), '__doc__'),
func_w_doc.__doc__
)
assert_equal(
getattr(
getattr(ddt_mytest, 'first_test_3'), '__doc__'),
func_w_doc.__doc__
)
assert_equal(
getattr(
getattr(ddt_mytest, 'second_test_1_case1'), '__doc__'), d1.__doc__
)
assert_equal(
getattr(
getattr(ddt_mytest, 'test_hello_2_case2'), '__doc__'),
hello.__doc__
getattr(ddt_mytest, 'second_test_2_case2'), '__doc__'),
None
)
assert_equal(
getattr(
getattr(ddt_mytest, 'second_test_3'), '__doc__'),
None
)


def test_ddt_data_unicode():
"""
Test that unicode strings are converted to function names correctly
"""

def hello():
pass

# We test unicode support separately for python 2 and 3

if six.PY2:
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py27, py33, py34, py35
envlist = py27, py34, py35

[testenv]
deps =
Expand Down

0 comments on commit b92ef81

Please sign in to comment.