Structuring a python project - python

I need to know how to set up the __init__.py and imports in order to structure a python project where I can use fully qualified names throughout the package.
The package will contain a number of sub packages which may contain clashing names. The classes contained within the package will sub class each other and contain references to each other. The project will be generated so the use of fully qualified names would make life a lot simpler.
This sample project represents the structure I am aiming at, but only contains a single sub project, while the IDE seems happy with it fails when its run.
MyPackage/__init__.py
import SubPackage as SubPackage
MyPackage/SubPackage/__init__.py
from .FileB import ClassB
from .FileA import ClassA
MyPackage/SubPackage/FileA.py
from __future__ import absolute_import
import MyPackage
class ClassA(MyPackage.SubPackage.ClassB):
thingA: 'MyPackage.SubPackage.ClassA'
thingB: MyPackage.SubPackage.ClassB
def __init__(self):
self.thingA = None
self.thingB = None
def test(self):
self.thingA = MyPackage.SubPackage.ClassA()
self.thingB = MyPackage.SubPackage.ClassB()
MyPackage/SubPackage/FileB.py
from __future__ import absolute_import
import MyPackage
class ClassB(object):
nextB: 'MyPackage.SubPackage.ClassB'
def __init__(self):
self.nextB= None
def test(self):
self.nextB= MyPackage.SubPackage.ClassB()
test.py
import MyPackage
x = MyPackage.SubPackage.ClassA()
Error
File "C:\temp\Test.py", line 3, in <module>
import GeneratedLx
File "C:\temp\MyPackage\__init__.py", line 1, in <module>
import Bs as Bs
File "C:\temp\MyPackage\SubPackage\__init__.py", line 12, in <module>
from .FileA import ClassA
File "C:\temp\MyPackage\SubPackage\FileA.py", line 5, in <module>
class ClassA(MyPackage.SubPackage.ClassB):
AttributeError: module 'MyPackage' has no attribute 'SubPackage'

You already cannot have name conflicts at the SubPackage level, so adding MyPackage is entirely redundant, and doesn't work quite the way you're trying to use it. This may be due to when names are bound or something else, but ultimately there should be no instance when you need it. This leaves you to slightly edit the files: "FileA.py", and "FileB.py":
FileA.py
from __future__ import absolute_import
from MyPackage import SubPackage
class ClassA(SubPackage.ClassB):
thingA: 'SubPackage.ClassA'
thingB: SubPackage.ClassB
def __init__(self):
self.thingA = None
self.thingB = None
def test(self):
self.thingA = SubPackage.ClassA()
self.thingB = SubPackage.ClassB()
FileB.py
from __future__ import absolute_import
from MyPackage import SubPackage
class ClassB(object):
nextB: 'SubPackage.ClassB'
def __init__(self):
self.nextB= None
def test(self):
self.nextB= SubPackage.ClassB()
The import statement is also equivalent to from .. import Subpackage using relative imports rather than absolute if desired. Stylistically I tend to use relative imports to help me quickly pick out which imports are part of my project, and which ones are external dependencies.

Related

Set a module's class method as an attribute of that module from outside that module

The objective:
I have a package with submodules that I would like to be accessible in the most straightforward way possible. The submodules contain classes to take advantage of the class structure, but don't need to be initialized (as they contain static and class methods). So, ideally, I would like to access them as follows:
from myPackage.subModule import someMethod
print (someMethod)
from myPackage import subModule
print (subModule.someMethod)
import myPackage
print(myPackage.subModule.someMethod)
Here is the package structure:
myPackage ─┐
__init__.py
subModule
subModule2
etc.
Example of a typical submodule:
# submodule.py
class SomeClass():
someAttr = list(range(10))
#classmethod
def someMethod(cls):
pass
#staticmethod
def someMethod2():
pass
Here is the code I have in my '__init __.py': In order to achieve the above; it attempts to set attributes for each class at the package level, and the same for it's methods at the sub-module level.
# __init__.py
def import_submodules(package, filetypes=('py', 'pyc', 'pyd'), ignoreStartingWith='_'):
'''Import submodules to the given package, expose any classes at the package level
and their respective class methods at submodule level.
:Parameters:
package (str)(obj) = A python package.
filetypes (str)(tuple) = Filetype extension(s) to include.
ignoreStartingWith (str)(tuple) = Ignore submodules starting with given chars.
'''
if isinstance(package, str):
package = sys.modules[package]
if not package:
return
pkg_dir = os.path.dirname(os.path.abspath(package.__file__))
sys.path.append(pkg_dir) #append this dir to the system path.
for mod_name in os.listdir(pkg_dir):
if mod_name.startswith(ignoreStartingWith):
continue
elif os.path.isfile(os.path.join(pkg_dir, mod_name)):
mod_name, *mod_ext = mod_name.rsplit('.', 1)
if filetypes:
if not mod_ext or mod_ext[0] not in filetypes:
continue
mod = importlib.import_module(mod_name)
vars(package)[mod_name] = mod
classes = inspect.getmembers(mod, inspect.isclass)
for cls_name, clss in classes:
vars(package)[cls_name] = clss
methods = inspect.getmembers(clss, inspect.isfunction)
for method_name, method in methods:
vars(mod)[method_name] = method
del mod_name
import_submodules(__name__)
At issue is this line:
vars(mod)[method_name] = method
Which ultimately results in: (indicating that the attribute was not set)
from myPackage.subModule import someMethod
ImportError: cannot import name 'someMethod' from 'myPackage.subModule'
I am able to set the methods as attributes to the module within that module, but setting them from outside (ie. in the package __init __), isn't working as written. I understand this isn't ideal to begin with, but my current logic is; that the ease of use, outweighs any perceived issues with namespace pollution. I am, of course, always open to counter-arguments.
I just checked it on my machine.
Created a package myPackage with a module subModule that has a function someMethod.
I run a python shell with working directory in the same directory that the myPackage is in, and to get these 3 import statements to work:
from myPackage.subModule import someMethod
from myPackage import subModule
import myPackage
All I had to do was to create an __init__.py with this line in it:
from . import subModule
Found a nice "hacky" solution -
subModule.py:
class myClass:
#staticmethod
def someMethod():
print("I have a bad feeling about this")
myInstance = myClass()
someMethod = myInstance.someMethod
init.py is empty
Still scratching my head of why I am unable to do this from the package __init __, but this solution works with the caveat it has to be called at the end of each submodule. Perhaps someone, in the future, someone can chime in as to why this wasn't working when completely contained in the __init __.
def addMembers(module, ignoreStartingWith='_'):
'''Expose class members at module level.
:Parameters:
module (str)(obj) = A python module.
ignoreStartingWith (str)(tuple) = Ignore class members starting with given chars.
ex. call: addMembers(__name__)
'''
if isinstance(module, str):
module = sys.modules[module]
if not module:
return
classes = inspect.getmembers(module, inspect.isclass)
for cls_name, clss in classes:
cls_members = [(o, getattr(clss, o)) for o in dir(clss) if not o.startswith(ignoreStartingWith)]
for name, mem in cls_members:
vars(module)[name] = mem
This is the solution I ended up going with. It needs to be put at the end of each submodule of your package. But, it is simple and in addition to all the standard ways of importing, allows you to import a method directly:
def __getattr__(attr):
'''Attempt to get a class attribute.
:Parameters:
attr (str): A name of a class attribute.
:Return:
(obj) The attribute.
'''
try:
return getattr(Someclass, attr)
except AttributeError as error:
raise AttributeError(f'{__file__} in __getattr__\n\t{error} ({type(attr).__name__})')
from somePackage.someModule import someMethod

ModuleNotFoundError: No module named 'sharedFunctions'

I have a Python project in which I have the following folder structure:
> root
> download_module
> __init__.py
> downloadProcess.py
> sharedFunctions.py
> someHelper.py
> useSharedFunction.py
The download_module/__init__.py has the following code:
from .sharedFunctions import stringArgumentToDate
from .downloadProcess import downloadProcessMethod
The sharedFunctions.py file contains the following function:
def stringArgumentToDate(arg):
dateformat = "%m/%d/%Y"
date = None
if arg.isnumeric():
date = datetime.fromtimestamp(int(arg))
if date == None:
date = datetime.strptime(arg, dateformat)
return date
Then on the useSharedFunction.py I try to import the shared function and use it like this.
from download_module import stringArgumentToDate
from download_module import downloadProcessMethod
def main():
arg = '03/14/2022'
dateArg = stringArgumentToDate(arg)
if __name__ == '__main__':
main()
When I try to run this by using python3 useSharedFunction.py I got the following error:
Traceback (most recent call last):
File "useSharedFunction.py", line 4, in <module>
from download_module import stringArgumentToDate
File "/Users/jacobo/Documents/project/download_module/__init__.py", line 2, in <module>
from .download_module import downloadAndProcessMethod
File "/Users/jacobo/Documents/project/download_module/downloadProcess.py", line 10, in <module>
from sharedFunctions import stringArgumentToDate, otherFunction
ModuleNotFoundError: No module named 'sharedFunctions'
I do believe the error is in downloadProcess since at the beggining of the file we got this import:
from sharedFunctions import stringArgumentToDate, otherFunction
from someHelper import Helper
Which refers to sibling files.
However I'm unsure what will be a proper fix to allow to run the downloadProcess.py main independently but also, being able to call it one of its method from a root or any other file out of the module.
Consider this structure:
┬ module
| ├ __init__.py
| ├ importing_submodule.py
| └ some_submodule.py
├ __main__.py
├ some_submodule.py
└ module_in_parent_dir.py
with content:
__main__.py
import module
/module/__init__.py
from . import importing_submodule
/module/importing_submodule.py
from some_submodule import SomeClass
/module/some_submodule.py
print("you imported from module")
class SomeClass:
pass
/some_submodule.py
print("you imported from root")
class SomeClass:
pass
/module_in_parent_dir.py
class SomeOtherClass:
pass
How sibling import works
(skip this section if you know already)
Now lets run __main__.py and it will say "you imported from root".
But if we change code a bit..
/module/importing_submodule.py
from module.some_submodule import SomeClass
It now says "You imported from module" as we wanted, probably with scary red line in IDE saying "Unresolved reference" if you didn't config working directory in IDE.
How this happen is simple: script root(Current working directory) is decided by main script(first script that's running), and python uses namespaces.
Python's import system uses 2 import method, and for convenience let's call it absolute import and relative import.
Absolute import: Import from dir listed in sys.path and current working directory
Relative import: Import relative to the very script that called import
And what decide the behavior is whether we use . at start of module name or not.
Since we imported by from some_submodule without preceeding dot, python take it as 'Absolute import'(the term we decided earlier).
And then when we also specified module name like from module.some_submodule python looks for module in path list or in current working directory.
Of course, this is never a good idea; script root can change via calls like os.chdir() then submodules inside module folder may get lost.
Therefore, the best practices for sibling import is using relative import inside module folder.
/module/importing_submodule.py
from .some_submodule import SomeClass
Making script that work in both way
To make submodule import it's siblings when running as main script, yet still work as submodule when imported by other script, then use try - except and look for ImportError.
For importing_submodule.py as an example:
/module/importing_submodule.py
try:
from .some_submodule import SomeClass
except ImportError:
# attempted relative import with no known parent package
# because this is running as main script, there's no parent package.
from some_submodule import SomeClass
Importing modules from parent directory is a bit more tricky.
Since submodule is now main script, relative import to parent level directory doesn't work.
So we need to add the parent directory to sys.path, when the script is running as main script.
/module/importing_submodule.py
try:
from .some_submodule import SomeClass
except ImportError:
# attempted relative import with no known parent package
# because this is running as main script, there's no parent package.
from some_submodule import SomeClass
# now since we don't have parent package, we just append the path.
from sys import path
import pathlib
path.append(pathlib.Path(__file__).parent.parent.as_posix())
print("Importing module_in_parent_dir from sys.path")
else:
print("Importing module_in_parent_dir from working directory")
# Now either case we have parent directory of `module_in_parent_dir`
# in working dir or path, we can import it
# might need to suppress false IDE warning this case.
# noinspection PyUnresolvedReferences
from module_in_parent_dir import SomeOtherClass
Output:
"C:\Program Files\Python310\python.exe" .../module/importing_module.py
you imported from module
Importing module_in_parent_dir from sys.path
Process finished with exit code 0
"C:\Program Files\Python310\python.exe" .../__main__.py
you imported from module
Importing module_in_parent_dir from working directory
Process finished with exit code 0

How to import module without class in Python?

I have 4 files in my project:
project/__init__.py
project/app.py
project/mod_x.py
project/mod_y.py
In mod_x.py I have a class (e.g. ModX)
In mod_y.py I have just one function.
I import modules from app.py as follows:
from .mod_x import ModX
import .mod_y
I get an error:
ImportError: No module named 'mod_y'
Before I created init.py I didn't have that kind of problems (of course, I dont put "." before module name).
How to import module which doesn't have the class inside in Python3 with init.py file inside the current directory?
Relative imports are only available for from...import syntax.
You could import that function this way:
from .mod_y import FUNCTION_NAME
Module could be imported this way:
from . import mod_y

Import classes from child directory python

I've been trying to import some python classes which are defined in a child directory. The directory structure is as follows:
workspace/
__init__.py
main.py
checker/
__init__.py
baseChecker.py
gChecker.py
The baseChecker.py looks similar to:
import urllib
class BaseChecker(object):
# SOME METHODS HERE
The gChecker.py file:
import baseChecker # should import baseChecker.py
class GChecker(BaseChecker): # gives a TypeError: Error when calling the metaclass bases
# SOME METHODS WHICH USE URLLIB
And finally the main.py file:
import ?????
gChecker = GChecker()
gChecker.someStuff() # which uses urllib
My intention is to be able to run main.py file and call instantiate the classes under the checker/ directory. But I would like to avoid importing urllib from each file (if it is possible).
Note that both the __init__.py are empty files.
I have already tried calling from checker.gChecker import GChecker in main.py but a ImportError: No module named checker.gChecker shows.
In the posted code, in gChecker.py, you need to do
from baseChecker import BaseChecker
instead of import baseChecker
Otherwise you get
NameError: name 'BaseChecker' is not defined
Also with the mentioned folders structure you don't need checker module to be in the PYTHONPATH in order to be visible by main.py
Then in main.y you can do:
from checker import gChecker.GChecker

Can a Python module use the imports from another file?

I have something like this:
# a.py
import os
class A:
...
# b.py
import a
class B(A):
...
In class B (b.py) I'd like to be able to use the modules imported in a.py (os in this case). Is it possible to achieve this behavior in Python or should I import the modules in both files?
Edit: I'm not worried about the import times, my problem is the visual clutter that the block of imports puts on the files. I end up having stuff like this in every controller (RequestHandler):
from django.utils import simplejson
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext import db
That's what I'd like to avoid.
Yes you can use the imports from the other file by going a.os.
However, the pythonic way is to just import the exact modules you need without making a chain out of it (which can lead to circular references).
When you import a module, the code is compiled and inserted into a dictionary of names -> module objects. The dictionary is located at sys.modules.
import sys
sys.modules
>>> pprint.pprint(sys.modules)
{'UserDict': <module 'UserDict' from 'C:\python26\lib\UserDict.pyc'>,
'__builtin__': <module '__builtin__' (built-in)>,
'__main__': <module '__main__' (built-in)>,
'_abcoll': <module '_abcoll' from 'C:\python26\lib\_abcoll.pyc'>,
# the rest omitted for brevity
When you try to import the module again, Python will check the dictionary to see if its already there. If it is, it will return the already compiled module object to you. Otherwise, it will compile the code, and insert it in sys.modules.
Since dictionaries are implemented as hash tables, this lookup is very quick and takes up negligible time compared to the risk of creating circular references.
Edit: I'm not worried about the import
times, my problem is the visual
clutter that the block of imports puts
on the files.
If you only have about 4 or 5 imports like that, its not too cluttery. Remember, "Explicit is better than implicit". However if it really bothers you that much, do this:
<importheaders.py>
from django.utils import simplejson
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext import db
<mycontroller.py>
from importheaders import *
Just import the modules again.
Importing a module in python is a very lightweight operation. The first time you import a module, python will load the module and execute the code in it. On any subsequent imports, you will just get a reference to the already-imported module.
You can verify this yourself, if you like:
# module_a.py
class A(object):
pass
print 'A imported'
# module_b.py
import module_a
class B(object):
pass
print 'B imported'
# at the interactive prompt
>>> import module_a
A imported
>>> import module_a # notice nothing prints out this time
>>> import module_b # notice we get the print from B, but not from A
B imported
>>>
You should import it separately. However, if you really need to forward some functionality, you can return a module from a function. Just:
import os
def x:
return os
But it seems like a plugin functionality - objects + inheritance would solve that case a bit better.
Sounds like you are wanting to use python packages. Look into those.
Yep. Once you import a module, that module becomes a property of the current module.
# a.py
class A(object):
...
# b.py
import a
class B(a.A):
...
In Django, for example, many of the packages simply import the contents of other modules. Classes and functions are defined in separate files just for the separation:
# django/db/models/fields/__init__.py
class Field(object):
...
class TextField(Field):
...
# django/db/models/__init__.py
from django.db.models.fields import *
# mydjangoproject/myapp/models.py
from django.db import models
class MyModel(models.Model):
myfield = models.TextField(...)
....
First you can shorten it to:
from django.utils import simplejson
from google.appengine.ext import webapp, db
from webapp import template
Secondly suppose you have those ^ imports in my_module.py
In my_module2.py you can do:
from my_module2.py import webapp, db, tempate
example:
In [5]: from my_module2 import MyMath2, MyMath
In [6]: m2 = MyMath2()
In [7]: m2.my_cos(3)
Out[7]: 0.94398413915231416
In [8]: m = MyMath()
In [9]: m.my_sin(3)
Out[9]: 0.32999082567378202
where my_module2 is:
from my_module import math, MyMath
class MyMath2(object):
the_meaning_of_life = 42
def my_cos(self, number):
return math.cos(number * 42)
and my_module1 is:
import math
class MyMath(object):
some_number = 42
def my_sin(self, num):
return math.sin(num * self.some_number)
Cheers,
Hope it helps
AleP

Categories

Resources