简体   繁体   中英

How to make python file importable relatively and absolutely? I get either ModuleNotFoundError or ImportError

I have the following structure of python code:

.
├── my_main.py
└── my_pkg
    ├── my_dep.py
    ├── my_script.py

Both my_main.py and my_script.py should be callable (have a if __name__ == '__main__' ) section:

my_main.py:

import my_pkg.my_script 
if __name__ == '__main__':
    print(my_pkg.my_script.bar())

and my_script.py:

import my_dep
def bar():
    return my_dep.foo() + 1
if __name__ == '__main__':
    print(bar())

this imports... my_dep.py: , which looks like:

def foo():
    return 1

If you want to look at it all together, look here: https://github.com/ct2034/python_import_trouble

Problem:

  • If I run my_script.py , all works well.

    But if I run my_main.py , I get:

    ModuleNotFoundError: No module named 'my_dep'

  • If I change the import in my_script.py to from. import my_dep from. import my_dep , my_main.py works.

    But when I run my_script.py , I get:

    ImportError: attempted relative import with no known parent package

How can I make both of them work?

Note: This is on Python 3.8

And sorry for the long-winded explanation. Was not able to make it any more concise. Hope it is understandable.

Both my_main.py and my_script.py should be callable (have a if name == ' main ') section)

No they should not.

What you want to do is a non Pythonic design, and even if you manage to achieve it with some work you will later be bitten. And the later is the worse because it could break more things including in production code.

I strongly urge you to stick to the common rules:

  • modules can only be found if they are in sys.path
  • relative imports only make sense in packages
  • a module inside a package should never be called as a script

It will indeed force you to adapt your design, but it will work smoothly and could easily be packaged into a distribution.

My advice if you have multiple related modules is to design that as a package (with __init__.py files). You will then get natural relative imports.

If you additionaly need to start scripts, you can either build wrapper scripts using the package modules or use a __main__.py file in your top level package folder: it will be executed if your run python -m package . But please, please do not try to run the same file sometimes as a package module and sometimes as a script. Or at least do not expect me to help you on that way...

I found a way to make it work. Serge is absolutely right in saying that this should be done differently altogether ( https://stackoverflow.com/a/70594758/1493204 ). But if you still want it, this is how to do it:

my_script.py :

if __name__ != '__main__':
    from . import my_dep
def bar():
    return my_dep.foo() + 1
if __name__ == '__main__':
    import my_dep
    print(bar())

also here.. https://github.com/ct2034/python_import_trouble/tree/bad_solution

From the directory level of my_main.py you can call the script with python -m mypkg.my_script .

If this is meant to become an installable package, you can use the setup.py to create entry points or scripts, which will be installed into some system wide accessible bin directory anyway.

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