简体   繁体   中英

Attempted relative import with no known parent package error

Error: While importing "wsgi-contract-test", an ImportError was raised:

Traceback (most recent call last):
  File "/Users/karl/Development/tral/bin/tral-env/lib/python3.9/site-packages/flask/cli.py", line 236, in locate_app
    __import__(module_name)
  File "/Users/karl/Development/tral/test/contract/inject/wsgi-contract-test.py", line 8, in <module>
    from . import (
ImportError: attempted relative import with no known parent package

ERROR (run-tral): trap on error (rc=2) near line 121
make: *** [component.mk:114: tral-local-run-api-contract-test] Error 2

wsgi-contract-test.py:

from ...libs.tral import app, setup_from_env
from ...libs.tral import routes

I have the regular source files under the libs/tral directory, however the entry file for this test is located under test/contract/inject . I do NOT want to move this test file into libs since this file should be nowhere near production code as it is a rather hazardous file security wise.

In node.js this would of worked fine but there seems to be something with python imports I'm not grasping?

Since tests don't belong inside the src tree, there are basically two approaches. If you are testing a library you will frequently install it (in a virtualenv) and then just import it exactly the same way you would anywhere else. Frequently you also do this with a fresh install for the test: this has the great advantage that your tests mirror real setups, and helps to catch bugs like forgetting to commit/include files (guilty.) and only noticing once you've deployed...

The other option is to modify sys.path so that your test is effectively in the same place as your production code:

# /test/contract/inject.py
from pathlib import Path
import sys

sys.path.insert(0, str(Path(__file__).parent.parent.parent))

Since sys.path uses strs you may prefer to use os.path , like every single other example on SO. Personally I'm addicted to pathlib. Here:

  • __file__ is the absolute path of the current file
  • Path(str) wraps it in a pathlib.Path object
  • .parent gets you up the tree, and
  • str() casts back to the format sys.path expects, or it won't work.

Using a test runner

Normally we use a test runner for tests. But these tests need a running instance: No problem:

# tests/conftest.py
import pytest
from sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent))
# now pytest can find the src
from app import get_instance # dummy

@pytest.fixture
def instance():
    instance = get_instance(some_param)
    # maybe
    instance.do_some_setup()
    yield instance
    # maybe
    instance.do_some_cleanup()

If you don't need to do any cleanup, you can just return the instance rather than yielding it. Then in another file (for neatness) you write tests like this:

# tests/test_instance.py

def test_instance(instance): # note how we requested the fixture
    assert instance.some_method()

And you run your tests with pytest:

pytest tests/

Pytest will run the code in conftest.py , discover all test fns starting with test in files whose names start with test , run the tests (supplying all the fixtures you have defined) and report.

Fixture Lifetime

Sometimes spinning up a fixture can be expensive. See the docs on fixture scope for telling pytest to keep the fixture around and supply it to multiple tests.

Using a runner vs. just running a script

You don't have to use a runner. But they do have many advantages:

  • decent metrics eg code/branch coverage
  • everyone uses them
  • parallel testing against different envs (eg python versions)
  • tests tend to be a lot neater

I took for granted that Python was able to handle simple relative paths; not the case. Instead I just added the path to the packages I wanted to include in the PYTHONPATH variable and walla, it found everything.

export PYTHONPATH="${PYTHONPATH}:$(pwd)/libs"

Then running it from the root project directory.

I had to change the code to the following though:

from tral import app, setup_from_env
from tral import routes

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM