diff --git a/CHANGES.rst b/CHANGES.rst index 2ffb8b3..937e63f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,10 @@ Breaking changes: New features: +- When environment variable ``Z3C_AUTOINCLUDE_DEBUG`` is set, + log which packages are being automatically included. + Do this in a form that you can copy to a ``configure.zcml`` file. + - Add support for Python 3.8. Bug fixes: diff --git a/src/z3c/autoinclude/api.py b/src/z3c/autoinclude/api.py index 31cbd91..8cb6ca8 100644 --- a/src/z3c/autoinclude/api.py +++ b/src/z3c/autoinclude/api.py @@ -2,6 +2,7 @@ DEP_KEY = 'Z3C_AUTOINCLUDE_DEPENDENCIES_DISABLED' PLUGIN_KEY = 'Z3C_AUTOINCLUDE_PLUGINS_DISABLED' +DEBUG_KEY = 'Z3C_AUTOINCLUDE_DEBUG' def dependencies_disabled(): @@ -13,7 +14,7 @@ def disable_dependencies(): def enable_dependencies(): - del os.environ[DEP_KEY] + os.environ.pop(DEP_KEY, None) def plugins_disabled(): @@ -25,4 +26,16 @@ def disable_plugins(): def enable_plugins(): - del os.environ[PLUGIN_KEY] + os.environ.pop(PLUGIN_KEY, None) + + +def debug_enabled(): + return DEBUG_KEY in os.environ + + +def disable_debug(): + os.environ.pop(DEBUG_KEY, None) + + +def enable_debug(): + os.environ[DEBUG_KEY] = 'True' diff --git a/src/z3c/autoinclude/api.txt b/src/z3c/autoinclude/api.txt new file mode 100644 index 0000000..62cc511 --- /dev/null +++ b/src/z3c/autoinclude/api.txt @@ -0,0 +1,71 @@ +============= +API functions +============= + +The ``api.py`` module has some helpful functions. + + +Plugins +======= + +We can disable and enable autoincluding the plugins:: + + >>> from z3c.autoinclude import api + >>> api.plugins_disabled() + False + >>> api.disable_plugins() + >>> api.plugins_disabled() + True + >>> api.disable_plugins() # called twice to see if this breaks + >>> api.plugins_disabled() + True + >>> api.enable_plugins() + >>> api.plugins_disabled() + False + >>> api.enable_plugins() # called twice to see if this breaks + >>> api.plugins_disabled() + False + + +Dependencies +============ + +We can disable and enable autoincluding the dependencies:: + + >>> from z3c.autoinclude import api + >>> api.dependencies_disabled() + False + >>> api.disable_dependencies() + >>> api.dependencies_disabled() + True + >>> api.disable_dependencies() # called twice to see if this breaks + >>> api.dependencies_disabled() + True + >>> api.enable_dependencies() + >>> api.dependencies_disabled() + False + >>> api.enable_dependencies() # called twice to see if this breaks + >>> api.dependencies_disabled() + False + + +Debug +===== + +We can disable and enable the debug report of autoincluded packages:: + + >>> from z3c.autoinclude import api + >>> api.debug_enabled() + False + >>> api.enable_debug() + >>> api.debug_enabled() + True + >>> api.enable_debug() # called twice to see if this breaks + >>> api.debug_enabled() + True + >>> api.disable_debug() + >>> api.debug_enabled() + False + >>> api.disable_debug() # called twice to see if this breaks + >>> api.debug_enabled() + False diff --git a/src/z3c/autoinclude/dependency.py b/src/z3c/autoinclude/dependency.py index 2c0b1ae..fdeb012 100644 --- a/src/z3c/autoinclude/dependency.py +++ b/src/z3c/autoinclude/dependency.py @@ -4,8 +4,10 @@ from pkg_resources import resource_exists from pkg_resources import get_provider from pkg_resources import get_distribution +from z3c.autoinclude.api import debug_enabled from z3c.autoinclude.utils import DistributionManager from z3c.autoinclude.utils import ZCMLInfo +from z3c.autoinclude.utils import create_report logger = logging.getLogger("z3c.autoinclude") @@ -42,6 +44,15 @@ def includableInfo(self, zcml_to_look_for): ) if os.path.isfile(candidate_path): result[candidate].append(dotted_name) + + if debug_enabled(): + report = create_report(result) + if "overrides.zcml" in zcml_to_look_for: + report.insert(0, "includeDependenciesOverrides found in zcml of %s." % self.context.project_name) + else: + report.insert(0, "includeDependencies found in zcml of %s." % self.context.project_name) + logger.info("\n".join(report)) + return result diff --git a/src/z3c/autoinclude/plugin.py b/src/z3c/autoinclude/plugin.py index 5d7d5a1..75aace4 100644 --- a/src/z3c/autoinclude/plugin.py +++ b/src/z3c/autoinclude/plugin.py @@ -1,10 +1,16 @@ +import logging import os from pkg_resources import iter_entry_points from pkg_resources import resource_filename +from z3c.autoinclude.api import debug_enabled +from z3c.autoinclude.utils import create_report from z3c.autoinclude.utils import DistributionManager from z3c.autoinclude.utils import ZCMLInfo +logger = logging.getLogger("z3c.autoinclude") + + class PluginFinder(object): def __init__(self, platform_dottedname): self.dottedname = platform_dottedname @@ -18,6 +24,15 @@ def includableInfo(self, zcml_to_look_for): groups = zcml_to_include(plugin_dottedname, zcml_to_look_for) for zcml_group in groups: includable_info[zcml_group].append(plugin_dottedname) + + if debug_enabled(): + report = create_report(includable_info) + if "overrides.zcml" in zcml_to_look_for: + report.insert(0, "includePluginsOverrides found in zcml of %s." % self.dottedname) + else: + report.insert(0, "includePlugins found in zcml of %s." % self.dottedname) + logger.info("\n".join(report)) + return includable_info diff --git a/src/z3c/autoinclude/tests/tests.py b/src/z3c/autoinclude/tests/tests.py index ad75e38..7491f78 100644 --- a/src/z3c/autoinclude/tests/tests.py +++ b/src/z3c/autoinclude/tests/tests.py @@ -118,7 +118,14 @@ def test_suite(): from pprint import pprint - suite = doctest.DocFileSuite( + simple_suite = doctest.DocFileSuite( + '../api.txt', + globs={'pprint': pprint}, + checker=IgnoreCaseChecker(), + optionflags=doctest.ELLIPSIS, + ) + + advanced_suite = doctest.DocFileSuite( '../utils.txt', '../dependency.txt', '../plugin.txt', @@ -129,7 +136,7 @@ def test_suite(): optionflags=doctest.ELLIPSIS, ) - return unittest.TestSuite((suite,)) + return unittest.TestSuite((simple_suite, advanced_suite)) if __name__ == '__main__': diff --git a/src/z3c/autoinclude/utils.py b/src/z3c/autoinclude/utils.py index 9502789..81fdffc 100644 --- a/src/z3c/autoinclude/utils.py +++ b/src/z3c/autoinclude/utils.py @@ -43,6 +43,12 @@ def __init__(self, zcml_to_look_for): for zcml_group in zcml_to_look_for: self[zcml_group] = [] + def __bool__(self): + return any(self.values()) + + # For Python 2: + __nonzero__ = __bool__ + def subpackageDottedNames(package_path, ns_dottedname=None): # we do not look for subpackages in zipped eggs @@ -206,3 +212,29 @@ def find_packages(where='.', exclude=()): out = [item for item in out if not fnmatchcase(item, pat)] return out + + +def create_report(info): + """Create a report with a list of auto included zcml.""" + if not info: + # Return a comment. Maybe someone wants to automatically include this + # in a zcml file, so make it a proper xml comment. + return [""] + report = [] + # Try to report meta.zcml first. + filenames = list(info) + meta = "meta.zcml" + if meta in filenames: + filenames.remove(meta) + filenames.insert(0, meta) + for filename in filenames: + dotted_names = info[filename] + for dotted_name in dotted_names: + if filename == "overrides.zcml": + line = ' ' % (dotted_name, filename) + elif filename == "configure.zcml": + line = ' '% dotted_name + else: + line = ' '% (dotted_name, filename) + report.append(line) + return report diff --git a/src/z3c/autoinclude/utils.txt b/src/z3c/autoinclude/utils.txt index 0256647..8eac153 100644 --- a/src/z3c/autoinclude/utils.txt +++ b/src/z3c/autoinclude/utils.txt @@ -45,3 +45,54 @@ package contained within it:: >>> import F.G >>> distributionForPackage(F.G) # doctest: +IGNORECASE SiblingPackage 0.0 (...SiblingPackage-0.0...egg) + +We have a helper class ZCMLInfo which is a dictionary which automatically gets keys. +Perhaps a defaultdict would work too now, but let's not change the class at this time. + + >>> from z3c.autoinclude.utils import ZCMLInfo + >>> info = ZCMLInfo(['meta.zcml', 'configure.zcml']) + >>> sorted(info.keys()) + ['configure.zcml', 'meta.zcml'] + >>> list(info.values()) + [[], []] + +When no values are filled in, the boolean should be False. + + >>> bool(info) + False + +We can create a report of the auto included zcml. + + >>> from z3c.autoinclude.utils import create_report + >>> create_report(info) + [''] + +We add information: + + >>> info['configure.zcml'].append('sample_package') + >>> bool(info) + True + >>> create_report(info) + [' '] + >>> info['configure.zcml'].append('package2') + >>> info['meta.zcml'].append('meta_package') + >>> report = create_report(info) + >>> len(report) + 3 + >>> report[0] + ' ' + >>> report[1] + ' ' + >>> report[2] + ' ' + +We can only add known filenames: + + >>> info['overrides.zcml'].append('overrides_package') + Traceback (most recent call last): + ... + KeyError: 'overrides.zcml' + >>> overrides = ZCMLInfo(['overrides.zcml']) + >>> overrides['overrides.zcml'].append('overrides_package') + >>> create_report(overrides) + [' ']