beyond top level package error in relative import

后端 未结 13 2262
醉梦人生
醉梦人生 2020-11-22 12:50

It seems there are already quite some questions here about relative import in python 3, but after going through many of them I still didn\'t find the answer for my issue. s

相关标签:
13条回答
  • 2020-11-22 13:07

    if you have an __init__.py in an upper folder, you can initialize the import as import file/path as alias in that init file. Then you can use it on lower scripts as:

    import alias
    
    0 讨论(0)
  • 2020-11-22 13:08

    EDIT: There are better/more coherent answers to this question in other questions:

    • Sibling package imports
    • Relative imports for the billionth time

    Why doesn't it work? It's because python doesn't record where a package was loaded from. So when you do python -m test_A.test, it basically just discards the knowledge that test_A.test is actually stored in package (i.e. package is not considered a package). Attempting from ..A import foo is trying to access information it doesn't have any more (i.e. sibling directories of a loaded location). It's conceptually similar to allowing from ..os import path in a file in math. This would be bad because you want the packages to be distinct. If they need to use something from another package, then they should refer to them globally with from os import path and let python work out where that is with $PATH and $PYTHONPATH.

    When you use python -m package.test_A.test, then using from ..A import foo resolves just fine because it kept track of what's in package and you're just accessing a child directory of a loaded location.

    Why doesn't python consider the current working directory to be a package? NO CLUE, but gosh it would be useful.

    0 讨论(0)
  • 2020-11-22 13:09

    This is very tricky in Python.

    I'll first comment on why you're having that problem and then I will mention two possible solutions.

    • What's going on?

    You must take this paragraph from the Python documentation into consideration:

    Note that relative imports are based on the name of the current module. Since the name of the main module is always "main", modules intended for use as the main module of a Python application must always use absolute imports.

    And also the following from PEP 328:

    Relative imports use a module's name attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to 'main') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.

    Relative imports work from the filename (__name__ attribute), which can take two values:

    1. It's the filename, preceded by the folder strucutre, separated by dots. For eg: package.test_A.test Here Python knows the parent directories: before test comes test_A and then package. So you can use the dot notation for relative import.
    #  package.test_A/test.py
    from ..A import foo
    

    You can then have like a root file in the root directory which calls test.py:

    #  root.py
    from package.test_A import test
    
    1. When you run the module (test.py) directly, it becomes the entry point to the program , so __name__ == __main__. The filename has no indication of the directory structure, so Python doesn't know how to go up in the directory. For Python, test.py becomes the top-level script, there is nothing above it. That's why you cannot use relative import.

    • Possible Solutions

    A) One way to solve this is to have a root file (in the root directory) which calls the modules/packages, like this:

    • root.py imports test.py. (entry point, __name__ == __main__).
    • test.py (relative) imports foo.py.
    • foo.py says the module has been imported.

    The output is:

    package.A.foo has been imported
    Module's name is:  package.test_A.test
    

    B) If you want to execute the code as a module and not as a top-level script, you can try this from the command line:

    python -m package.test_A.test
    

    Any suggestions are welcomed.

    You should also check: Relative imports for the billionth time , specially BrenBarn's answer.

    0 讨论(0)
  • 2020-11-22 13:12

    Not sure in python 2.x but in python 3.6, assuming you are trying to run the whole suite, you just have to use -t

    -t, --top-level-directory directory Top level directory of project (defaults to start directory)

    So, on a structure like

    project_root
      |
      |----- my_module
      |          \
      |           \_____ my_class.py
      |
      \ tests
          \___ test_my_func.py
    
    

    One could for example use:

    python3 unittest discover -s /full_path/project_root/tests -t /full_path/project_root/

    And still import the my_module.my_class without major dramas.

    0 讨论(0)
  • 2020-11-22 13:15

    In my humble opinion, I understand this question in this way:

    [CASE 1] When you start an absolute-import like

    python -m test_A.test
    

    or

    import test_A.test
    

    or

    from test_A import test
    

    you're actually setting the import-anchor to be test_A, in other word, top-level package is test_A . So, when we have test.py do from ..A import xxx, you are escaping from the anchor, and Python does not allow this.

    [CASE 2] When you do

    python -m package.test_A.test
    

    or

    from package.test_A import test
    

    your anchor becomes package, so package/test_A/test.py doing from ..A import xxx does not escape the anchor(still inside package folder), and Python happily accepts this.

    In short:

    • Absolute-import changes current anchor (=redefines what is the top-level package);
    • Relative-import does not change the anchor but confines to it.

    Furthermore, we can use full-qualified module name(FQMN) to inspect this problem.

    Check FQMN in each case:

    • [CASE2] test.__name__ = package.test_A.test
    • [CASE1] test.__name__ = test_A.test

    So, for CASE2, an from .. import xxx will result in a new module with FQMN=package.xxx, which is acceptable.

    While for CASE1, the .. from within from .. import xxx will jump out of the starting node(anchor) of test_A, and this is NOT allowed by Python.

    0 讨论(0)
  • 2020-11-22 13:18

    Assumption:
    If you are in the package directory, A and test_A are separate packages.

    Conclusion:
    ..A imports are only allowed within a package.

    Further notes:
    Making the relative imports only available within packages is useful if you want to force that packages can be placed on any path located on sys.path.

    EDIT:

    Am I the only one who thinks that this is insane!? Why in the world is the current working directory not considered to be a package? – Multihunter

    The current working directory is usually located in sys.path. So, all files there are importable. This is behavior since Python 2 when packages did not yet exist. Making the running directory a package would allow imports of modules as "import .A" and as "import A" which then would be two different modules. Maybe this is an inconsistency to consider.

    0 讨论(0)
提交回复
热议问题