Why python finds module instead of package if they have the same name?

后端 未结 2 1249
无人及你
无人及你 2020-12-16 21:15

Here is my directory structure:

/home/dmugtasimov/tmp/name-res
    root
        tests
            __init__.py
            test_1.py
        __init__.py
              


        
相关标签:
2条回答
  • 2020-12-16 21:23

    I simplified the example from the question to demostrate that only four solutions are possible:

    • Explicit relative import from . import some_module or with more commas from ..
    • Relative import (without "xyz." if used in the package "xyz")
    • Absolute import using from __future__ import absolute_import (or use Python 3)
    • Never repeat the top level importable name of the package in any path inside it.

    What solution is the best? It depends on personal preference of Python 2 or 3. Only the last one is nice and universal for all Pythons. It was really a useful question.


    xyz/tests/__init__.py: import xyz.tests.t

    xyz/tests/t.py:

    import sys
    print('sys.path = %s' % sys.path) # see that the parent of "xyz" is on sys.path
    print("importing xyz.tests")
    import xyz.a
    

    xyz/a.py:

    # solution A: absolute_import by __future__  (or use Python 3)
    #from __future__ import absolute_import
    print("importing xyz.a")
    # solution B: explicit relative import
    #from . import b    # and remove "import xyz.b"
    # solution C: relative import (not recommended)
    #import b           # and remove "import xyz.b"
    import xyz.b
    

    xyz/b.py: print("imported xyz.b")

    xyz/xyz.py: print("Imported xyz.xyz !!!")

    xyz/__init__.py: empty file


    Everything possible fails, e.g.

    parent_of_xyz=...  # The parent directory of "xyz" - absolute path
    cd $parent_of_xyz
    python -m xyz.tests.t
    PYTHONPATH=$parent_of_xyz/xyz python -m unittest tests
    PYTHONPATH=$parent_of_xyz     python xyz/tests/t.py
    

    with messages like

    Imported xyz.xyz  !!!
    ...
    ImportError...
    

    If any solution is applied (uncommented), all three examples work.

    It can be more simplified without using any subdirectory.

    EDIT: I tried yesterday many tests but I wrote it inconsitently from different versions. Excuse me that it was unreproducible from the answer. Now it is fixed.

    0 讨论(0)
  • 2020-12-16 21:39

    Do not modify sys.path it leads to the issues when the same module is available under different names. See Traps for the Unwary.

    Use absolute or explicit relative imports instead in the code and run your scripts from the project directory. Run the tests using the full name:

    $ python -munittest root.tests.test_1
    

    Some packages do modify sys.path internally e.g., see how twisted uses _preamble.py or pypy's autopath.py. You could decide whether their downsides (introduction of obscure import problems) are worth the convience (more ways to run your scripts are allowed). Avoid modifying sys.path in a code that is used as a library i.e., limit it to test modules and your command-line scripts.

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