Repository: pybuilder/pybuilder
Issue: 0.11.* coverage plugin causes issues during build deleting sys modules

Hello,

I think the new coverage plugin should be refactored, as it's a potential source of problems.
Looks like it's deleting a bunch of global modules, sparing only the ones that it consider "essential".
We have a custom python installation, the coverage plugin gets a bit confused and considers non essential things like datetime. We end up with errors like:

Traceback (most recent call last):
  File "/tmp/test/env-infra/bin/pyb", line 25, in <module>
    sys.exit(pybuilder.cli.main(*sys.argv[1:]))
  File "/tmp/test/env-infra/lib/python2.7/site-packages/pybuilder/cli.py", line 366, in main
    end = datetime.datetime.now()
AttributeError: 'module' object has no attribute 'datetime'

Because of this issue we are unable to update to 0.11.1
I know there should be a way to fix on our side by reverse-engineering your code and put modules in strategic locations, but honestly nothing in pybuilder (nothing in anything IMHO) should play with sys.modules, as it will cause issues that are a nightmare to debug.

Downgrading to 0.10.63 fixes the issue obviously, but the install task would be nice to have for our purposes.


Changed files:
build.py, src/main/python/pybuilder/plugins/python/coverage_plugin.py

diff --git a/build.py b/build.py
index dd5b5b2..b874000 100755
--- a/build.py
+++ b/build.py
@@ -96,6 +96,7 @@ def initialize(project):
     project.set_property("verbose", True)
 
     project.set_property("coverage_break_build", False)
+    project.set_property("coverage_reset_modules", True)
     project.get_property("coverage_exceptions").append("pybuilder.cli")
     project.get_property("coverage_exceptions").append("pybuilder.plugins.core_plugin")
 
diff --git a/src/main/python/pybuilder/plugins/python/coverage_plugin.py b/src/main/python/pybuilder/plugins/python/coverage_plugin.py
index 9ac1221..07c0e8b 100644
--- a/src/main/python/pybuilder/plugins/python/coverage_plugin.py
+++ b/src/main/python/pybuilder/plugins/python/coverage_plugin.py
@@ -43,6 +43,7 @@ def init_coverage_properties(project):
     project.set_property_if_unset("coverage_branch_partial_threshold_warn", 0)
     project.set_property_if_unset("coverage_break_build", True)
     project.set_property_if_unset("coverage_reload_modules", None)  # deprecated, unused
+    project.set_property_if_unset("coverage_reset_modules", False)
     project.set_property_if_unset("coverage_exceptions", [])
     project.set_property_if_unset("coverage_fork", None)  # deprecated, unused
 
@@ -88,12 +89,13 @@ def do_coverage(project, logger, reactor, execution_prefix, execution_name, targ
     It's best to simple let this method exit and the fork die rather than to try to recover.
     """
     source_tree_path = project.get_property("dir_source_main_python")
+    reset_modules = project.get_property("%s_reset_modules" % execution_prefix)
     module_names = _discover_modules_to_cover(project)
 
     for module_name in module_names:
         logger.debug("Module '%s' coverage to be verified", module_name)
 
-    if not is_windows():
+    if reset_modules and not is_windows():
         _delete_non_essential_modules()
         __import__("pybuilder.plugins.python")  # Reimport self
 
@@ -330,7 +332,7 @@ def _get_system_assets():
     @return: tuple(packages, modules) to ignore
     """
     canon_sys_path = [os.path.realpath(package_dir) for package_dir in sys.path]
-    std_lib = sysconfig.get_python_lib(standard_lib=True)
+    std_lib = os.path.realpath(sysconfig.get_python_lib(standard_lib=True))
     canon_sys_path = [package_dir for package_dir in canon_sys_path if package_dir.startswith(std_lib)]
 
     packages = []