Skip to content
This repository has been archived by the owner on Oct 1, 2024. It is now read-only.

ModuleNotFoundError/ImportError within ward #152

Closed
CodeMouse92 opened this issue Mar 28, 2020 · 8 comments · Fixed by #207
Closed

ModuleNotFoundError/ImportError within ward #152

CodeMouse92 opened this issue Mar 28, 2020 · 8 comments · Fixed by #207
Labels
bug Something isn't working

Comments

@CodeMouse92
Copy link
Contributor

CodeMouse92 commented Mar 28, 2020

I have ward installed in a virtual environment on Python 3.7.5, and I'm getting the following error when executing the ward command in the root of my repository. My code lives in a package omission directly in this repository, and that package contains subpackages and submodules configured as tests.

Here are the versions of the packages installed in my virtual environment:

  • GitPython-3.1.0
  • PyYAML-5.3.1
  • appdirs-1.4.3
  • bandit-1.6.2
  • click-7.1.1
  • colorama-0.3.9
  • cucumber-tag-expressions-2.0.4
  • entrypoints-0.3
  • flake8-3.7.9
  • gitdb-4.0.2
  • mccabe-0.6.1
  • pbr-5.4.4
  • pprintpp-0.4.0
  • pycodestyle-2.5.0
  • pyflakes-2.1.1
  • pygments-2.6.1
  • pyside2-5.14.1
  • shiboken2-5.14.1
  • six-1.14.0
  • smmap-3.0.1
  • stevedore-1.32.0
  • termcolor-1.1.0
  • toml-0.9.6
  • ward-0.42.0b0

Here's the error I'm encountering:

(venv) jason@tardis:omission$ ward
Traceback (most recent call last):
  File "/home/jason/Code/Repositories/omission/venv/bin/ward", line 10, in <module>
    sys.exit(run())
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/decorators.py", line 21, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/ward/run.py", line 115, in run
    modules = list(load_modules(mod_infos))
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/ward/collect.py", line 109, in load_modules
    m.__loader__.exec_module(m)
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "venv/lib/python3.7/site-packages/git/test/test_diff.py", line 7, in <module>
    import ddt
ModuleNotFoundError: No module named 'ddt'

After installing ddt manually (ddt-1.3.1), I get this error instead when running ward:

Traceback (most recent call last):
  File "/home/jason/Code/Repositories/omission/venv/bin/ward", line 10, in <module>
    sys.exit(run())
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/decorators.py", line 21, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/ward/run.py", line 115, in run
    modules = list(load_modules(mod_infos))
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/ward/collect.py", line 109, in load_modules
    m.__loader__.exec_module(m)
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "venv/lib/python3.7/site-packages/git/test/performance/test_commit.py", line 11, in <module>
    from .lib import TestBigRepoRW
ImportError: attempted relative import with no known parent package

Interestingly, if I install pytest as well, the error on ward changes yet again:

Traceback (most recent call last):
  File "/home/jason/Code/Repositories/omission/venv/bin/ward", line 10, in <module>
    sys.exit(run())
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/click/decorators.py", line 21, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/ward/run.py", line 115, in run
    modules = list(load_modules(mod_infos))
  File "/home/jason/Code/Repositories/omission/venv/lib/python3.7/site-packages/ward/collect.py", line 109, in load_modules
    m.__loader__.exec_module(m)
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "venv/lib/python3.7/site-packages/importlib_metadata/tests/test_api.py", line 5, in <module>
    from . import fixtures
ImportError: attempted relative import with no known parent package

For the record, installing pytest as well installs the following:

  • attrs-19.3.0
  • importlib-metadata-1.6.0
  • more-itertools-8.2.0
  • packaging-20.3
  • pluggy-0.13.1
  • py-1.8.1
  • pyparsing-2.4.6
  • pytest-5.4.1
  • wcwidth-0.1.9
  • zipp-3.1.0
@CodeMouse92 CodeMouse92 changed the title ImportError within ward ModuleNotFoundError within ward Mar 28, 2020
@CodeMouse92 CodeMouse92 changed the title ModuleNotFoundError within ward ModuleNotFoundError/ImportError within ward Mar 28, 2020
@darrenburns darrenburns added the bug Something isn't working label Mar 29, 2020
@darrenburns
Copy link
Owner

darrenburns commented Mar 29, 2020

Hi Jason, thanks for the report. I think if you call ward with ward --path omission it'll get around this issue for now. Let me know if that works.

It's running into some issues as it's finding some modules inside your virtualenv prefixed with test_, and trying to execute them to see if there are Ward tests inside them. This is partly a problem with the packages your project depends on themselves including test files in their distribution rather than excluding them.

For example, ward finds venv/lib/python3.7/site-packages/git/test/test_diff.py which imports ddt which is presumably a "test dependency" of GitPython, meaning it won't be installed by default, so when ward executes that module, it'll raise the ModuleNotFoundError.

I haven't had time to implement it yet, but #81 should hopefully resolve most cases of this issue. I will also make another issue to not fail the full test run, and instead print a warning when something like this is encountered.

The ImportError is a separate issue which means you can't use relative imports in any of your test modules, you must stick to absolute imports.

@thebigmunch
Copy link
Contributor

thebigmunch commented Mar 29, 2020

Hi Jason, thanks for the report. I think if you call ward with ward --path omission it'll get around this issue for now. Let me know if that works.

Alternatively, set the path in pyproject.toml as I've been doing.

This is partly a problem with the packages your project depends on themselves including test files in their distribution rather than excluding them.

I hate this practice so much ^_^

The ImportError is a separate issue which means you can't use relative imports in any of your test modules, you must stick to absolute imports.

I find this to be a bit problematic. For example, in ward, you put the test suite inside your package directory and then exclude them from being packaged. That doesn't really seem right. Right now, I work around this by adding an __init__.py to my tests directory and invoking ward as a module (python -m ward), allowing me to use from tests.fixtures import ... and such. Still can't use relative imports, though. Just invoking ward does not work, of course.

Pytest does some magic to allow most of this, but not sure that's something you'd want to do.

@CodeMouse92
Copy link
Contributor Author

Yeah, most of this should be fine for adapting, but the lack of relative path support is going to be problematic long term. We should definitely fix that.

@thebigmunch
Copy link
Contributor

For reference, here are the docs for the way pytest works with sys.path/PYTHONPATH.

@darrenburns
Copy link
Owner

As of 0.44.0b0, Ward no longer looks in site-packages when discovering tests. I'll keep this issue open as I believe there may still be issues if you use relative imports in your own project.

@darrenburns
Copy link
Owner

darrenburns commented Apr 1, 2021

Interestingly, pytest 6 has an option to use importlib for imports, and it looks like the pytest folk are having similar issues with relative imports: pytest-dev/pytest#7245 (comment) -- this is going to become default in pytest in the future.

I've tested this out in pytest and ward, and in both cases relative imports cause errors regardless of whether the relative import statements appear in the test modules or in under-test project code.

There are some other issues being reported with importlib mode in pytest around using @dataclass and pickle, but those work fine in ward.

A PR open in pytest discussing possible solutions that may come in handy in the future: pytest-dev/pytest#7936

@darrenburns
Copy link
Owner

darrenburns commented Apr 2, 2021

Relative imports should be fixed in 0.53.0b0, which is now on PyPI

@AndydeCleyre
Copy link
Contributor

My test cases had been importing from adjacent modules (from commands import ...), but Pyright didn't love that, so I did the minimum to turn my test folder into a package: added __init__.py and changed relative imports to from .commands import ...). Ward doesn't like this:

ModuleNotFoundError: No module named 'test.commands'

but I noticed it works fine in CI where it's wrapped by coverage:

coverage run -p -m ward

So I checked and see it also works fine if ward is invoked as:

python -m ward

It would be great to have the plain ward invocation work here as well, unless I'm missing something wrong with that.

For reference, this project is here and the change that introduced this problem for ward is:

commit c755f39298e6063377e3debdd3061247aa461cf3
Author: Andy Kluger <[email protected]>
Date:   Tue Sep 17 14:17:13 2024 -0400

    Make test dir a package for pyright-friendly relative imports

diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 0000000..598d40e
--- /dev/null
+++ b/test/__init__.py
@@ -0,0 +1 @@
+"""Test suite for NestedTextTo."""
diff --git a/test/test_json.py b/test/test_json.py
index 6ae29ea..2f3f3dd 100644
--- a/test/test_json.py
+++ b/test/test_json.py
@@ -2,11 +2,12 @@
 
 from typing import cast
 
-from commands import json2nt, nt2json
 from plumbum import LocalPath, local
-from utils import assert_file_content, casting_args_from_schema_file
 from ward import test
 
+from .commands import json2nt, nt2json
+from .utils import assert_file_content, casting_args_from_schema_file
+
 SAMPLES = local.path(__file__).up() / 'samples' / 'json'  # type: ignore
 
 
diff --git a/test/test_toml.py b/test/test_toml.py
index 66ab146..aef6551 100644
--- a/test/test_toml.py
+++ b/test/test_toml.py
@@ -2,11 +2,12 @@
 
 from typing import cast
 
-from commands import nt2toml, toml2nt
 from plumbum import LocalPath, local
-from utils import assert_file_content, casting_args_from_schema_file
 from ward import skip, test
 
+from .commands import nt2toml, toml2nt
+from .utils import assert_file_content, casting_args_from_schema_file
+
 try:
     import tomli  # noqa: F401
     import tomli_w  # noqa: F401
diff --git a/test/test_yaml.py b/test/test_yaml.py
index 773e44a..1e64f12 100644
--- a/test/test_yaml.py
+++ b/test/test_yaml.py
@@ -2,11 +2,12 @@
 
 from typing import cast
 
-from commands import nt2yaml, yaml2nt
 from plumbum import LocalPath, local
-from utils import assert_file_content, casting_args_from_schema_file
 from ward import test
 
+from .commands import nt2yaml, yaml2nt
+from .utils import assert_file_content, casting_args_from_schema_file
+
 SAMPLES = local.path(__file__).up() / 'samples' / 'yaml'  # type: ignore

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants