Skip to content

Make "py.test --help" more friendly if DSM is set, but PYTHONPATH is not #235

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
blueyed opened this issue May 8, 2015 · 2 comments · Fixed by #238
Closed

Make "py.test --help" more friendly if DSM is set, but PYTHONPATH is not #235

blueyed opened this issue May 8, 2015 · 2 comments · Fixed by #238

Comments

@blueyed
Copy link
Contributor

blueyed commented May 8, 2015

pytest-django might cause py.test --help to behave very unfriendly, when
DJANGO_SETTINGS_MODULE is set, but PYTHONPATH is not set correctly. See the traceback below.

It would be nice if it would behave better, especially when py.test --help is used.

% py.test --help
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
…/django17/django/conf/__init__.py in __init__(self, settings_module)
     93         try:
---> 94             mod = importlib.import_module(self.SETTINGS_MODULE)
     95         except ImportError as e:

…/pyenv/project/lib/python3.4/importlib/__init__.py in import_module(name, package)
    108             level += 1
--> 109     return _bootstrap._gcd_import(name[level:], package, level)
    110 

…/pyenv/project/lib/python3.4/importlib/_bootstrap.py in _gcd_import(name, package, level)

…/pyenv/project/lib/python3.4/importlib/_bootstrap.py in _find_and_load(name, import_)

…/pyenv/project/lib/python3.4/importlib/_bootstrap.py in _find_and_load_unlocked(name, import_)

…/pyenv/project/lib/python3.4/importlib/_bootstrap.py in _call_with_frames_removed(f, *args, **kwds)

…/pyenv/project/lib/python3.4/importlib/_bootstrap.py in _gcd_import(name, package, level)

…/pyenv/project/lib/python3.4/importlib/_bootstrap.py in _find_and_load(name, import_)

…/pyenv/project/lib/python3.4/importlib/_bootstrap.py in _find_and_load_unlocked(name, import_)

ImportError: No module named 'config'

During handling of the above exception, another exception occurred:

ImportError                               Traceback (most recent call last)
…/pytest-django/pytest_django/plugin.py in _handle_import_error(extra_message)
     91     try:
---> 92         yield
     93     except ImportError as e:

…/pytest-django/pytest_django/plugin.py in pytest_load_initial_conftests(early_config, parser, args)
    197         with _handle_import_error(_django_project_scan_outcome):
--> 198             settings.DATABASES
    199 

…/django17/django/conf/__init__.py in __getattr__(self, name)
     45         if self._wrapped is empty:
---> 46             self._setup(name)
     47         return getattr(self._wrapped, name)

…/django17/django/conf/__init__.py in _setup(self, name)
     41 
---> 42         self._wrapped = Settings(settings_module)
     43 

…/django17/django/conf/__init__.py in __init__(self, settings_module)
     97                 "Could not import settings '%s' (Is it on sys.path? Is there an import error in the settings file?): %s"
---> 98                 % (self.SETTINGS_MODULE, e)
     99             )

ImportError: Could not import settings 'config.settings' (Is it on sys.path? Is there an import error in the settings file?): No module named 'config'

During handling of the above exception, another exception occurred:

ImportError                               Traceback (most recent call last)
…/pyenv/project/bin/py.test in <module>()
      9 if __name__ == '__main__':
     10     sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
---> 11     sys.exit(main())

…/pyenv/project/lib/python3.4/site-packages/_pytest/config.py in main(args, plugins)
     30     """
     31     try:
---> 32         config = _prepareconfig(args, plugins)
     33     except ConftestImportFailure:
     34         e = sys.exc_info()[1]

…/pyenv/project/lib/python3.4/site-packages/_pytest/config.py in _prepareconfig(args, plugins)
     83                 pluginmanager.register(plugin)
     84         return pluginmanager.hook.pytest_cmdline_parse(
---> 85                 pluginmanager=pluginmanager, args=args)
     86     except Exception:
     87         pluginmanager.ensure_shutdown()

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in __call__(self, **kwargs)
    519 
    520     def __call__(self, **kwargs):
--> 521         return self._docall(self.methods, kwargs)
    522 
    523     def callextra(self, methods, **kwargs):

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in _docall(self, methods, kwargs)
    526     def _docall(self, methods, kwargs):
    527         return MultiCall(methods, kwargs,
--> 528                          firstresult=self.firstresult).execute()
    529 
    530 

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in execute(self)
    391             args = [all_kwargs[argname] for argname in varnames(method)]
    392             if hasattr(method, "hookwrapper"):
--> 393                 return wrapped_call(method(*args), self.execute)
    394             res = method(*args)
    395             if res is not None:

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in wrapped_call(wrap_controller, func)
    107     call_outcome = CallOutcome(func)
    108     try:
--> 109         wrap_controller.send(call_outcome)
    110         raise_wrapfail(wrap_controller, "has second yield")
    111     except StopIteration:

…/pyenv/project/lib/python3.4/site-packages/_pytest/helpconfig.py in pytest_cmdline_parse()
     26 def pytest_cmdline_parse():
     27     outcome = yield
---> 28     config = outcome.get_result()
     29     if config.option.debug:
     30         path = os.path.abspath("pytestdebug.log")

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in get_result(self)
    135             ex = self.excinfo
    136             if py3:
--> 137                 raise ex[1].with_traceback(ex[2])
    138             py.builtin._reraise(*ex)
    139 

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in __init__(self, func)
    121     def __init__(self, func):
    122         try:
--> 123             self.result = func()
    124         except Exception:
    125             self.excinfo = sys.exc_info()

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in execute(self)
    392             if hasattr(method, "hookwrapper"):
    393                 return wrapped_call(method(*args), self.execute)
--> 394             res = method(*args)
    395             if res is not None:
    396                 self.results.append(res)

…/pyenv/project/lib/python3.4/site-packages/_pytest/config.py in pytest_cmdline_parse(self, pluginmanager, args)
    634     def pytest_cmdline_parse(self, pluginmanager, args):
    635         assert self == pluginmanager.config, (self, pluginmanager.config)
--> 636         self.parse(args)
    637         return self
    638 

…/pyenv/project/lib/python3.4/site-packages/_pytest/config.py in parse(self, args)
    744                 "can only parse cmdline args at most once per Config object")
    745         self._origargs = args
--> 746         self._preparse(args)
    747         # XXX deprecated hook:
    748         self.hook.pytest_cmdline_preparse(config=self, args=args)

…/pyenv/project/lib/python3.4/site-packages/_pytest/config.py in _preparse(self, args, addopts)
    716         try:
    717             self.hook.pytest_load_initial_conftests(early_config=self,
--> 718                     args=args, parser=self._parser)
    719         except ConftestImportFailure:
    720             e = sys.exc_info()[1]

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in __call__(self, **kwargs)
    519 
    520     def __call__(self, **kwargs):
--> 521         return self._docall(self.methods, kwargs)
    522 
    523     def callextra(self, methods, **kwargs):

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in _docall(self, methods, kwargs)
    526     def _docall(self, methods, kwargs):
    527         return MultiCall(methods, kwargs,
--> 528                          firstresult=self.firstresult).execute()
    529 
    530 

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in execute(self)
    391             args = [all_kwargs[argname] for argname in varnames(method)]
    392             if hasattr(method, "hookwrapper"):
--> 393                 return wrapped_call(method(*args), self.execute)
    394             res = method(*args)
    395             if res is not None:

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in wrapped_call(wrap_controller, func)
    111     except StopIteration:
    112         pass
--> 113     return call_outcome.get_result()
    114 
    115 

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in get_result(self)
    135             ex = self.excinfo
    136             if py3:
--> 137                 raise ex[1].with_traceback(ex[2])
    138             py.builtin._reraise(*ex)
    139 

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in __init__(self, func)
    121     def __init__(self, func):
    122         try:
--> 123             self.result = func()
    124         except Exception:
    125             self.excinfo = sys.exc_info()

…/pyenv/project/lib/python3.4/site-packages/_pytest/core.py in execute(self)
    392             if hasattr(method, "hookwrapper"):
    393                 return wrapped_call(method(*args), self.execute)
--> 394             res = method(*args)
    395             if res is not None:
    396                 self.results.append(res)

…/pytest-django/pytest_django/plugin.py in pytest_load_initial_conftests(early_config, parser, args)
    196 
    197         with _handle_import_error(_django_project_scan_outcome):
--> 198             settings.DATABASES
    199 
    200         _setup_django()

…/pyenv/3.4.3/lib/python3.4/contextlib.py in __exit__(self, type, value, traceback)
     75                 value = type()
     76             try:
---> 77                 self.gen.throw(type, value, traceback)
     78                 raise RuntimeError("generator didn't stop after throw()")
     79             except StopIteration as exc:

…/pytest-django/pytest_django/plugin.py in _handle_import_error(extra_message)
     94         django_msg = (e.args[0] + '\n\n') if e.args else ''
     95         msg = django_msg + extra_message
---> 96         raise ImportError(msg)
     97 
     98 

ImportError: Could not import settings 'config.settings' (Is it on sys.path? Is there an import error in the settings file?): No module named 'config'

pytest-django could not find a Django project (no manage.py file could be found). You must explicitly add your Django project to the Python path to have it picked up.
@hpk42
Copy link

hpk42 commented May 8, 2015

I think plugin.py could try to use a autouse session-scoped fixture for setting up django instead of the current pytest_configure hook here https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/plugin.py#L204 . pytest_configure is called before --help is called whereas fixtures are not touched.

@blueyed
Copy link
Contributor Author

blueyed commented May 8, 2015

Thanks for looking into this!

It's also being called through pytest_load_initial_conftests though.

I've experimented with moving it from there, too - but it appears a session-scoped autouse fixture is too late, in case you import Django models in your project's conftest.py: Django then complains that models/apps are not ready yet.
(edit I think that we could require a project to call django.setup then manually)

We need to call django.setup() before a project's conftest.py is being loaded/handled, but after --help would be displayed.

I've seen that there's pytest_collectstart, but that's also too late.

Here is the traceback when importing the Django models in the project's conftest.py:

Traceback (most recent call last):
  File "…/pyenv/project/lib/python3.4/site-packages/_pytest/config.py", line 513, in getconftestmodules
    return self._path2confmods[path]
KeyError: local('…/project')

During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "…/pyenv/project/lib/python3.4/site-packages/_pytest/config.py", line 537, in importconftest
    return self._conftestpath2mod[conftestpath]
KeyError: local('…/project/conftest.py')

During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "…/pyenv/project/lib/python3.4/site-packages/_pytest/config.py", line 543, in importconftest
    mod = conftestpath.pyimport()
  File "…/pyenv/project/lib/python3.4/site-packages/py/_path/local.py", line 641, in pyimport
    __import__(modname)
  File "…/project/conftest.py", line 16, in <module>
    from app.factories import ModelFactory, VideoFactory
  File "…/project/app/factories.py", line 27, in <module>
    class VideoFactory(factory.django.DjangoModelFactory):
  File "…/factory_boy/factory/base.py", line 123, in __new__
    base_factory=base_factory,
  File "…/factory_boy/factory/base.py", line 210, in contribute_to_class
    self.model = self.factory._load_model_class(self.model)
  File "…/factory_boy/factory/django.py", line 109, in _load_model_class
    return get_model(app, model)
  File "…/django18/django/apps/registry.py", line 199, in get_model
    self.check_models_ready()
  File "…/django18/django/apps/registry.py", line 131, in check_models_ready
    raise AppRegistryNotReady("Models aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.
ERROR: could not load …/project/conftest.py

blueyed added a commit to blueyed/pytest-django that referenced this issue May 9, 2015
With `pytest --version` and `pytest --help` pytest-django should not
throw an error (ImportError) in case of invalid DSM settings etc.

Fixes pytest-dev#235
blueyed added a commit to blueyed/pytest-django that referenced this issue May 9, 2015
With `pytest --version` and `pytest --help` pytest-django should not
throw an error (ImportError) in case of invalid DSM settings etc.

This removes hooking into `pytest_configure` altogether, and moves the
late call do `_setup_django` into the session-scoped autoload fixture instead.

Fixes pytest-dev#235
blueyed added a commit to blueyed/pytest-django that referenced this issue May 9, 2015
With `pytest --version` and `pytest --help` pytest-django should not
throw an error (ImportError) in case of invalid DSM settings etc.

This removes hooking into `pytest_configure` altogether, and moves the
late call do `_setup_django` into the session-scoped autoload fixture instead.

Fixes pytest-dev#235
mfa pushed a commit to aexeagmbh/pytest-django that referenced this issue May 17, 2017
With `pytest --version` and `pytest --help` pytest-django should not
throw an error (ImportError) in case of invalid DSM settings etc.

This removes hooking into `pytest_configure` altogether, and moves the
late call do `_setup_django` into the session-scoped autoload fixture instead.

Fixes pytest-dev#235
beyondgeeks pushed a commit to beyondgeeks/django-pytest that referenced this issue Sep 6, 2022
With `pytest --version` and `pytest --help` pytest-django should not
throw an error (ImportError) in case of invalid DSM settings etc.

This removes hooking into `pytest_configure` altogether, and moves the
late call do `_setup_django` into the session-scoped autoload fixture instead.

Fixes pytest-dev/pytest-django#235
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants