Created
October 7, 2017 19:36
-
-
Save Enteee/73d2a0ed56cdc9d6bd1efaf730ffcb0d to your computer and use it in GitHub Desktop.
Check eval(repr(cls())) == cls() for whole package
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# vim: set fenc=utf8 ts=4 sw=4 et : | |
""" | |
Tests the follwoing rules for a whole package: | |
1. Repr is unique: All classes must implement __repr__ with eval(repr(cls())) == cls() | |
""" | |
import unittest | |
import sys | |
import inspect | |
import pkgutil | |
import importlib | |
# modules under test | |
MODULE_NAMES_TO_TEST=['pgc'] | |
class CodeQuality(unittest.TestCase): | |
# empty for now, add test during run time | |
pass | |
def _import_submodules(package, recursive=True): | |
""" Import all submodules of a module, recursively, including subpackages | |
:param package: package (name or actual module) | |
:type package: str | module | |
:rtype: dict[str, types.ModuleType] | |
""" | |
if isinstance(package, str): | |
package = importlib.import_module(package) | |
results = {} | |
for loader, name, is_pkg in pkgutil.walk_packages(package.__path__): | |
full_name = package.__name__ + '.' + name | |
results[full_name] = importlib.import_module(full_name) | |
if recursive and is_pkg: | |
results.update(_import_submodules(full_name)) | |
return results | |
def _test_all_classes(package, make_test): | |
"""Adds test for all classes in package.""" | |
for module_name, module in _import_submodules(package).items(): | |
for cls_name, cls in inspect.getmembers( | |
module, | |
lambda x: inspect.isclass(x) and x.__module__ == module_name # is class and define in module | |
): | |
test = make_test(cls) | |
test_name = 'test_{}_{}_{}'.format( | |
module_name, | |
cls_name, | |
test.__name__ | |
) | |
setattr( | |
CodeQuality, | |
test_name, | |
test | |
) | |
def _make_repr_test(cls): | |
""" Returns test which cheks repr for uniqueness.""" | |
def _repr(self): | |
# can eval? | |
try: | |
eval(repr(cls())), | |
except Exception as e: | |
raise AssertionError('eval(repr({}()))'.format( | |
cls.__name__ | |
)) from e | |
# is repr unambiguous? | |
self.assertEqual( | |
eval(repr(cls())), | |
cls() | |
) | |
return _repr | |
def _add_tests_for_modules(module_names_to_test): | |
for module_name in module_names_to_test: | |
module = importlib.import_module(module_name) | |
globals()[module_name] = module | |
# add code quality tests | |
_test_all_classes(module, _make_repr_test) | |
if __name__ == '__main__': | |
import argparse | |
parser = argparse.ArgumentParser(description='Unit test code quality') | |
parser.add_argument( | |
'modules', | |
metavar='module', | |
type=str, | |
nargs='*', | |
help='module to test', | |
default=[] | |
) | |
args = parser.parse_args() | |
_add_tests_for_modules(args.modules) | |
unittest.main(argv=[sys.argv[0]]) # hide command line arguments | |
else: | |
_add_tests_for_modules(MODULE_NAMES_TO_TEST) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment