I recently started working on an existing project.
This project is composed of several git repositories that may use each other in some hierarchy.
All repositories should be cloned to the same root directory.
Here's an example of the structure:
root_dir:
repo_a:
module1:
- a.py
- b.py
repo_b:
module1:
- c.py
- d.py
repo_c:
module1:
- e.py
- f.py
Note that I've written "module1" three times on purpose, as it really is the same name.
Now for an example of a file, let's say a.py:
from module1.b import foo
from module1.d import goo
from module1.f import zoo
def func():
foo()
goo()
zoo()
When trying to run it from the root_dir I'm having trouble, I guess due to the ambiguities and not having relative paths.
Is there a way I can run this project properly without internally changing the code?
Based on the boundary having to stick with the given structure two ways using importlib:
Concise option:
from importlib.machinery import SourceFileLoader
repo_a_module1 = SourceFileLoader("module1",">YourSystemPath</repo_a/module1.py").load_module()
repo_b_module1 = SourceFileLoader("module1",">YourSystemPath</repo_b/module1.py").load_module()
repo_c_module1 = SourceFileLoader("module1",">YourSystemPath</repo_c/module1.py").load_module()
repo_a_module1.foo()
repo_b_module1.goo()
repo_c_module1.zoo()
Another option, but with more code:
import importlib.util
repo_a = importlib.util.spec_from_file_location(
"module1", ">YourSystemPath</repo_a/module1.py")
repo_a_module1 = importlib.util.module_from_spec(repo_a)
repo_a.loader.exec_module(repo_a_module1)
repo_b = importlib.util.spec_from_file_location(
"module1", ">YourSystemPath</repo_b/module1.py")
repo_b_module1 = importlib.util.module_from_spec(repo_b)
repo_b.loader.exec_module(repo_b_module1)
repo_c = importlib.util.spec_from_file_location(
"module1", ">YourSystemPath</repo_c/module1.py")
repo_c_module1 = importlib.util.module_from_spec(repo_c)
repo_c.loader.exec_module(repo_c_module1)
repo_a_module1.foo()
repo_b_module1.goo()
repo_c_module1.zoo()
Note: >YourSystemPath< can be absolute or relative in both options.
Both examples above have a change in the calls of the imported functions (e.g. foo() -> repo_a_module1.foo() ) which is specific and may help when working with the code later on.
If you have to keep the calls you could add further functions in between:
def foo():
repo_a_module1.foo()
foo()
Related
I can't import all of functions from own module in other directory
my project structure:
/
|-code
|-db_orm
|-__init__.py
|-db_interface.py
|-streamlit
|-pages
|-subpage1.py
|-subpage2.py
|-main.py
i want to add functions from db_interface.py module into subpage1.py & subpage2.py file with this code:
from db_orm.db_interface import *
but i can't do it !
i am using VS Code and python 3.9.13
thanks for any help.
A manual option to import modules from other directories is SourceFileLoader.
Not following the from syntax, but a quick solution for e.g. brief trials:
from importlib.machinery import SourceFileLoader
db_interface = SourceFileLoader("db_interface",">YourSystemPath</db_interface.py").load_module()
# call
db_interface.foo() # foo being a function in db_interface
Note: >YourSystemPath< can be absolute or relative.
If you want to keep the calls only on the functions you could add further functions in between:
def foo():
db_interface.foo()
foo()
Again that will get messy for larger projects but is a quick workaround.
The project has the same structure as in the picture: I'm trying to import from "mod.py " in "index.py "
from .. import mod
However, it gives the error: "ImportError: attempted relative import with no known parent package" If you use this option:
from pack1 import mod
Then error: "ModuleNotFoundError error: there is no module named 'pack1'"
enter image description here
PROJECT/
pack1/
__init__.py
mod.py
pack2/
__init__.py
index.py
What is the problem?
This is a recurring question on StackOverflow. And much of the confusion (in my opinion) comes from how Python interprets the files and folders it sees is based on where Python is run from. First, some terminology:
module: a file containing Python code.
package: a folder containing files with Python code and other folders.
When you start Python in a directory (folder), it doesn't "know" what the namespace of that directory should be. I.e., if you are working in Z:\path\to_my\project\ when you start Python:
it does NOT consider project to be a package.
any .py files you want to import from will be in their own namespace as modules.
any folders you want to import from will also be in their own namespace as packages.
What about __init__.py? Since version 3.3, Python has implicit namespace packages, which allows importing without needing to create an empty __init__.py file.
Consider #2: if you have two files: first.py and second.py:
path/
to_my/
project/
>>Python is running here<<
first.py
second.py
with these contents:
# first.py
first_var = 'hello'
# second.py
from .first import first_var
second_var = first_var + ' world'
if you try to import like this:
>>> import second
Python basically does the following:
"ok, I see second.py"
"Reading that in as a module, chief!"
"Ok, it wants to import .first
"The . means get the package (folder) that contains first.py"
"Wait, I don't have a parent package for first.py!"
"Better raise an error."
The same rules apply for #3 as well. If we add a few packages to the project like this:
path/
to_my/
project/
>>Python is running here<<
first.py
second.py
pack1/
mod.py
other_mod.py
pack2/
index.py
with the following contents:
# pack1/mod.py
mod_var = 1234
# pack1/other_mod.py
from .mod import mod_var
other_var = mod_var * 10
# pack2/index.py
from ..pack1 import mod
and when you try to import like this:
>>> from pack2 import index.py
The import in pack2/index.py is going to fail for the same reason second.py, Python will work its way up the import chain of dots like this:
"Reading in in index.py as a module."
"Looks like it wants to import mod from ..pack1.
"Ok, . is the pack2 parent package namespace of index.py, found that."
"So, .. is the parent package of pack2."
"But, I don't have a parent package for pack2!"
"Better raise an error."
How do we make it work? Two thing.
First, move where Python is running up one level so that all of the .py files and subfolders are considered to be part of the same package namespace, which allows the file to reference each other using relative references.
path/
to_my/
>>Python is running here now<<
project/
first.py
second.py
pack1/
mod.py
other_mod.py
pack2/
index.py
So now Python sees project as a package namespace, and all of the files within can use relative references up to that level.
This changes how you import when you are in the Python interpreter:
>>> from project.pack2 import index.py
Second, you make explicit references instead of relative references. That can make the import statements really long, but if you have several top-level modules that need to pull from one another, this is how you can do it. This is useful when you are defining your functions in one file and writing your script in another.
# first.py
first_var = 'hello'
# second.py
from first import first_var # we dropped the dot
second_var = first_var + ' world'
I hope this helps clear up some of the confusion about relative imports.
I want to create a Python module that works like NumPy. The methods are not only sub-modules in the leaves of the tree from the module source. There is a root module containing many methods that I can call directly, and there are also sub-modules. The problem is the root methods must be defined somewhere. I was thinking to have a directory structure:
module/
__init__.py
core.py
stuff1.py
submodule/
__init__.py
stuff2.py
stuff3.py
Now that I want is for everything inside "core" to be imported into the "module" namespace, as if it were a module.py file, and the contents of core.py were inside this module.py. The problem is that module is a directory instead of a file, so how do I define these methods that should sit in the root of the module?
I tried putting "from core import *" inside init.py, but that didn't work. (EDIT: Actually it does.)
Should I have the core methods inside a "module.py" file, and also a module directory? I don't know if that works, but it looks pretty awkward.
What I think you want is to be able to do this:
# some_other_script.py
import module
# Do things using routines defined in module.core
What happens when you ask Python to import module is (in a very basic sense), module/__init__.py is run, and a module object is created and imported into your namespace. This object (again, very basically) encompasses the things that happened when __init__.py was run: name definitions and so on. These can be accessed through module.something.
Now, if your setup looks like this:
# module/__init__.py
from module.core import Clazz
c = Clazz()
print c # Note: demo only! Module-level side-effects are usually a bad idea!
When you import module, you'll see a print statement like this:
<module.core.Clazz object at 0x00BBAA90>
Great. But if you then try to access c, you'll get a NameError:
# some_other_script.py
import module # prints "<module.core.Clazz object at 0x00BBAA90>"
print c # NameError (c is not defined)
This is because you haven't imported c; you've imported module. If instead your entry-point script looks like this:
# some_other_script.py
import module # prints "<module.core.Clazz object at 0x00BBAA90>"
print module.c # Access c *within module*
Everything will run fine. This will also work fine with from core import * and/or from module import *, but I (and PEP8) advise against that just because it's not very clear what's going on in the script when you start mucking around with wild imports. For clarity:
# module/core.py
def core_func():
return 1
# module/__init__.py
from core import *
def mod_func():
return 2
The above is really pretty much fine, although you might as well make core "private" (rename to _core) to indicate that there's no reason to touch it from outside the package anymore.
# some_other_script.py
from module import *
print core_func() # Prints 1
print mod_func() # Prints 2
Check out information about the __all__ list. It allows you to define what names are exported.
Tag it as such and you can setup a function to determine what to pull in from your submodules:
#property
all(self):
#Whatever introspective code you may want for your modules
__all__ += submodule.__all__
If you just want the whole damn shabang in module space, here's a way:
$ ipython
In [1]: from foomod import *
In [2]: printbar()
Out[2]: 'Imported from a foreign land'
In [3]: ^D
Do you really want to exit ([y]/n)?
$ ls foomod/
__init__.py __init__.pyc core.py core.pyc submodule
$ grep . foomod/*.py
foomod/__init__.py:from foomod.core import *
foomod/core.py:def printbar():
foomod/core.py: return "Imported from a foreign land"
... and if we make __init__.py empty:
$ echo > foomod/__init__.py
$ ipython
In [1]: from foomod import *
In [2]: printbar()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-2-ba5b6693441e> in <module>()
----> 1 printbar()
NameError: name 'printbar' is not defined
I have a Python project with following directory structure:
/(some files)
/model/(python files)
/tools/(more python files)
...
So, I have Python files in couple subdirectories and there are some
dependencies between directories as well: tools are used by model, etc. Now
my problem is that I want to make doctests for both models and tools,
and I want be able to run tests from command line like this: ./model/car.py .
I can make this work, but only with messy boilerplate code. I would like
to know what is the correct way, or is there any?
Question: How should I write my imports?
Thanx. Here is an example...
Content of tools/tool.py:
#!/usr/bin/env python
"""
>>> is_four(21)
False
>>> is_four(4)
True
"""
def is_four(val):
return val == 4
if __name__ == '__main__':
import doctest
doctest.testmod()
... and model/car.py:
#!/usr/bin/env python
"""
>>> car = Car()
>>> car.ok()
True
"""
from tools.tool import *
class Car(object):
def __init__(self):
self.tire_count = 4
def ok(self):
return is_four(self.tire_count)
if __name__ == '__main__':
import doctest
doctest.testmod()
By adding following lines in the begin of car.py it works, but doesn't look nice. :(
if __name__ == '__main__':
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname('..')))
What you are trying to do is a relative import. It works fine in Python, but on the module level, not on the file system level. I know, this is confusing.
It means that if you run a script in a subdir, it doesn't see the upper dirs because for the running script, the root of the module is the current dir: there is no upper module.
So what are relative imports for?
Well, module in subdirs car import module in upper dirs as long as they are themself imported from a upperdir.
In your case it means you must run your scripts from "/" so it becomes the root of the module, and the submodules are allowed to use relative import.
A possible solution to your problem is to remove your if __name__ == "__main__" block and create /tests.py:
import doctest
from model import car
from tools import tool
doctest.testmod(car)
doctest.testmod(tool)
Then run in too launch all the tests.
Ultimately you will want to automatize the process, a simple solution is to use unittest so you can create test suites and just add the module names you want to test:
import unittest
import doctest
modules = ("model.car",
"tools.tool")
suite = unittest.TestSuite()
for mod in modules:
suite.addTest(doctest.DocTestSuite(mod))
runner = unittest.TextTestRunner()
runner.run(suite)
Another solution (recommended) is to use a tool such as nose that automates this for you.
easy_install nose
nosetests --with-doctest # done :-)
And by the way, avoid from x import *. This works for quick scripts, but when your program will grow, you really will need to explicitly name what you import. Either import x or from x import y
Use packages. Add an __init__.py file to your working directory and all subfolders then your imports will search the parent directories if it doesn't find the module in the current directory.
See http://www.network-theory.co.uk/docs/pytut/Packages.html
Also this question is a duplicate of:
Import a module from a relative path
Don't frob sys.path in this manner. Instead either use $PYTHONPATH to force the base directory in when invoking python, or use python -m model.car from that directory.
In a big application I am working, several people import same modules differently e.g.
import x
or
from y import x
the side effects of that is x is imported twice and may introduce very subtle bugs, if someone is relying on global attributes
e.g. suppose I have a package mypakcage with three file mymodule.py, main.py and init.py
mymodule.py contents
l = []
class A(object): pass
main.py contents
def add(x):
from mypackage import mymodule
mymodule.l.append(x)
print "updated list",mymodule.l
def get():
import mymodule
return mymodule.l
add(1)
print "lets check",get()
add(1)
print "lets check again",get()
it prints
updated list [1]
lets check []
updated list [1, 1]
lets check again []
because now there are two lists in two different modules, similarly class A is different
To me it looks serious enough because classes itself will be treated differently
e.g. below code prints False
def create():
from mypackage import mymodule
return mymodule.A()
def check(a):
import mymodule
return isinstance(a, mymodule.A)
print check(create())
Question:
Is there any way to avoid this? except enforcing that module should be imported one way onyl. Can't this be handled by python import mechanism, I have seen several bugs related to this in django code and elsewhere too.
Each module namespace is imported only once. Issue is, you're importing them differently. On the first you're importing from the global package, and on the second you're doing a local, non-packaged import. Python sees modules as different. The first import is internally cached as mypackage.mymodule and the second one as mymodule only.
A way to solve this is to always use absolute imports. That is, always give your module absolute import paths from the top-level package onwards:
def add(x):
from mypackage import mymodule
mymodule.l.append(x)
print "updated list",mymodule.l
def get():
from mypackage import mymodule
return mymodule.l
Remember that your entry point (the file you run, main.py) also should be outside the package. When you want the entry point code to be inside the package, usually you use a run a small script instead. Example:
runme.py, outside the package:
from mypackage.main import main
main()
And in main.py you add:
def main():
# your code
I find this document by Jp Calderone to be a great tip on how to (not) structure your python project. Following it you won't have issues. Pay attention to the bin folder - it is outside the package. I'll reproduce the entire text here:
Filesystem structure of a Python project
Do:
name the directory something
related to your project. For example,
if your project is named "Twisted",
name the top-level directory for its
source files Twisted. When you do
releases, you should include a version
number suffix: Twisted-2.5.
create a directory Twisted/bin and
put your executables there, if you
have any. Don't give them a .py
extension, even if they are Python
source files. Don't put any code in
them except an import of and call to a
main function defined somewhere else
in your projects.
If your project
is expressable as a single Python
source file, then put it into the
directory and name it something
related to your project. For example,
Twisted/twisted.py. If you need
multiple source files, create a
package instead (Twisted/twisted/,
with an empty
Twisted/twisted/__init__.py) and
place your source files in it. For
example,
Twisted/twisted/internet.py.
put
your unit tests in a sub-package of
your package (note - this means that
the single Python source file option
above was a trick - you always need at
least one other file for your unit
tests). For example,
Twisted/twisted/test/. Of course,
make it a package with
Twisted/twisted/test/__init__.py.
Place tests in files like
Twisted/twisted/test/test_internet.py.
add Twisted/README and Twisted/setup.py to explain and
install your software, respectively,
if you're feeling nice.
Don't:
put your source in a directory
called src or lib. This makes it
hard to run without installing.
put
your tests outside of your Python
package. This makes it hard to run the
tests against an installed version.
create a package that only has a
__init__.py and then put all your
code into __init__.py. Just make a
module instead of a package, it's
simpler.
try to come up with
magical hacks to make Python able to
import your module or package without
having the user add the directory
containing it to their import path
(either via PYTHONPATH or some other
mechanism). You will not correctly
handle all cases and users will get
angry at you when your software
doesn't work in their environment.
I can only replicate this if main.py is the file you are actually running. In that case you will get the current directory of main.py on the sys path. But you apparently also have a system path set so that mypackage can be imported.
Python will in that situation not realize that mymodule and mypackage.mymodule is the same module, and you get this effect. This change illustrates this:
def add(x):
from mypackage import mymodule
print "mypackage.mymodule path", mymodule
mymodule.l.append(x)
print "updated list",mymodule.l
def get():
import mymodule
print "mymodule path", mymodule
return mymodule.l
add(1)
print "lets check",get()
add(1)
print "lets check again",get()
$ export PYTHONPATH=.
$ python mypackage/main.py
mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
mymodule path <module 'mymodule' from '/tmp/mypackage/mymodule.pyc'>
But add another mainfile, in the currect directory:
realmain.py:
from mypackage import main
and the result is different:
mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
So I suspect that you have your main python file within the package. And in that case the solution is to not do that. :-)