From 528d56b95c0390f48f6d694152018b2703870411 Mon Sep 17 00:00:00 2001 From: Maurits van Rees Date: Fri, 6 Mar 2020 12:59:59 +0100 Subject: [PATCH 1/7] Added boolean operator to ZCMLInfo. Report as False when none of the keys have values. --- src/z3c/autoinclude/utils.py | 9 +++++++++ src/z3c/autoinclude/utils.txt | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/z3c/autoinclude/utils.py b/src/z3c/autoinclude/utils.py index 9502789..2b61648 100644 --- a/src/z3c/autoinclude/utils.py +++ b/src/z3c/autoinclude/utils.py @@ -43,6 +43,15 @@ def __init__(self, zcml_to_look_for): for zcml_group in zcml_to_look_for: self[zcml_group] = [] + def __bool__(self): + for value in self.values(): + if value: + return True + return False + + # For Python 2: + __nonzero__ = __bool__ + def subpackageDottedNames(package_path, ns_dottedname=None): # we do not look for subpackages in zipped eggs diff --git a/src/z3c/autoinclude/utils.txt b/src/z3c/autoinclude/utils.txt index 0256647..2ab6843 100644 --- a/src/z3c/autoinclude/utils.txt +++ b/src/z3c/autoinclude/utils.txt @@ -45,3 +45,21 @@ 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 + >>> info['configure.zcml'].append('sample_package') + >>> bool(info) + True From 29b19ff455e0708b200729ace1ee99ef6d04d146 Mon Sep 17 00:00:00 2001 From: Maurits van Rees Date: Fri, 6 Mar 2020 14:45:36 +0100 Subject: [PATCH 2/7] Added create_report command in utils.py. This creates a report of the auto included zcml. It is a list of zcml snippets that would do the same thing. When there is no zcml auto included, we include a comment. --- src/z3c/autoinclude/utils.py | 26 ++++++++++++++++++++++++++ src/z3c/autoinclude/utils.txt | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/z3c/autoinclude/utils.py b/src/z3c/autoinclude/utils.py index 2b61648..2c71269 100644 --- a/src/z3c/autoinclude/utils.py +++ b/src/z3c/autoinclude/utils.py @@ -215,3 +215,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 = info.keys() + meta = "meta.zcml" + if meta in filenames: + filenames.pop(filenames.index(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 2ab6843..8eac153 100644 --- a/src/z3c/autoinclude/utils.txt +++ b/src/z3c/autoinclude/utils.txt @@ -60,6 +60,39 @@ 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) + [' '] From b0c02b359ecdc45c91e5af3c22a2bbcdc890cf51 Mon Sep 17 00:00:00 2001 From: Maurits van Rees Date: Fri, 6 Mar 2020 12:16:23 +0100 Subject: [PATCH 3/7] With env var Z3C_AUTOINCLUDE_DEBUG, log which packages are being automatically included. Do this in a form that you can copy to a `configure.zcml` file. --- CHANGES.rst | 4 ++++ src/z3c/autoinclude/api.py | 13 +++++++++++++ src/z3c/autoinclude/dependency.py | 11 +++++++++++ src/z3c/autoinclude/plugin.py | 15 +++++++++++++++ 4 files changed, 43 insertions(+) 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..e261d2d 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(): @@ -26,3 +27,15 @@ def disable_plugins(): def enable_plugins(): del os.environ[PLUGIN_KEY] + + +def debug_enabled(): + return DEBUG_KEY in os.environ + + +def disable_debug(): + os.environ[DEBUG_KEY] = 'True' + + +def enable_debug(): + del os.environ[DEBUG_KEY] 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 From 7086cd3058b3369d3172b36acd44dd1db77fc577 Mon Sep 17 00:00:00 2001 From: Maurits van Rees Date: Fri, 6 Mar 2020 16:16:41 +0100 Subject: [PATCH 4/7] Fixed Python 3 problems in the new code. --- src/z3c/autoinclude/utils.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/z3c/autoinclude/utils.py b/src/z3c/autoinclude/utils.py index 2c71269..7dc5ce7 100644 --- a/src/z3c/autoinclude/utils.py +++ b/src/z3c/autoinclude/utils.py @@ -44,10 +44,7 @@ def __init__(self, zcml_to_look_for): self[zcml_group] = [] def __bool__(self): - for value in self.values(): - if value: - return True - return False + return any(self.values()) # For Python 2: __nonzero__ = __bool__ @@ -225,7 +222,7 @@ def create_report(info): return [""] report = [] # Try to report meta.zcml first. - filenames = info.keys() + filenames = list(info) meta = "meta.zcml" if meta in filenames: filenames.pop(filenames.index(meta)) From 1170288f280607f799e665a7b2e34891ce5f0728 Mon Sep 17 00:00:00 2001 From: Maurits van Rees Date: Fri, 6 Mar 2020 17:45:30 +0100 Subject: [PATCH 5/7] easier way for removing an item from a list --- src/z3c/autoinclude/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/z3c/autoinclude/utils.py b/src/z3c/autoinclude/utils.py index 7dc5ce7..81fdffc 100644 --- a/src/z3c/autoinclude/utils.py +++ b/src/z3c/autoinclude/utils.py @@ -225,7 +225,7 @@ def create_report(info): filenames = list(info) meta = "meta.zcml" if meta in filenames: - filenames.pop(filenames.index(meta)) + filenames.remove(meta) filenames.insert(0, meta) for filename in filenames: dotted_names = info[filename] From 5cbe07eb05eec44c482c7c01f124ad48c521aeb1 Mon Sep 17 00:00:00 2001 From: Maurits van Rees Date: Fri, 6 Mar 2020 17:44:35 +0100 Subject: [PATCH 6/7] Fixed logic in disable/enable_debug that was the wrong way around. --- src/z3c/autoinclude/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/z3c/autoinclude/api.py b/src/z3c/autoinclude/api.py index e261d2d..719e5c1 100644 --- a/src/z3c/autoinclude/api.py +++ b/src/z3c/autoinclude/api.py @@ -34,8 +34,8 @@ def debug_enabled(): def disable_debug(): - os.environ[DEBUG_KEY] = 'True' + del os.environ[DEBUG_KEY] def enable_debug(): - del os.environ[DEBUG_KEY] + os.environ[DEBUG_KEY] = 'True' From e040f7cfc21610521c2b7529fe4118a998fe2294 Mon Sep 17 00:00:00 2001 From: Maurits van Rees Date: Fri, 6 Mar 2020 18:13:12 +0100 Subject: [PATCH 7/7] Fixed deleting non-existing environment variables. Added simple tests for these. --- src/z3c/autoinclude/api.py | 6 +-- src/z3c/autoinclude/api.txt | 71 ++++++++++++++++++++++++++++++ src/z3c/autoinclude/tests/tests.py | 11 ++++- 3 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 src/z3c/autoinclude/api.txt diff --git a/src/z3c/autoinclude/api.py b/src/z3c/autoinclude/api.py index 719e5c1..8cb6ca8 100644 --- a/src/z3c/autoinclude/api.py +++ b/src/z3c/autoinclude/api.py @@ -14,7 +14,7 @@ def disable_dependencies(): def enable_dependencies(): - del os.environ[DEP_KEY] + os.environ.pop(DEP_KEY, None) def plugins_disabled(): @@ -26,7 +26,7 @@ def disable_plugins(): def enable_plugins(): - del os.environ[PLUGIN_KEY] + os.environ.pop(PLUGIN_KEY, None) def debug_enabled(): @@ -34,7 +34,7 @@ def debug_enabled(): def disable_debug(): - del os.environ[DEBUG_KEY] + os.environ.pop(DEBUG_KEY, None) def enable_debug(): 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/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__':