Importing class in same directory - python

I have a project named AsyncDownloaderTest with main.py and AsyncDownloader.py in same directory.I have just started learning python but it seems issue is with the import.
main.py
from .AsyncDownloader import AsyncDownloader
ad = AsyncDownloader()
ad.setSourceCSV("https://people.sc.fsu.edu/~jburkardt/data/csv/grades.csv","First name")
print(ad.printURLs)
AsyncDownloader.py
import pandas as pd
class AsyncDownloader:
"""Download files asynchronously"""
__urls = None
def setSourceCSV(self, source_path, column_name):
self.source_path = source_path
self.column_name = column_name
# TODO Check if path is a valid csv
# TODO Store the urls found in column in a list
my_csv = pd.read_csv(source_path, usecols=[column_name], chunksize=10)
for chunk in my_csv:
AsyncDownloader.urls += chunk.column_name
def printURLs(self):
print(AsyncDownloader.urls)
I am getting the following error
ModuleNotFoundError: No module named '__main__.AsyncDownloader'; '__main__' is not a package

Do you have __init__.py in the same directory as AsyncDownloader.py? That should do it.
__init__.py is an empty file that signals that the directory contains packages and makes functions and classes importable from .py files in that directory.
You can probably lose the leading . in from .AsyncDownloader as well. If you like, you can make the import absolute by changing it to:
from enclosing_folder.AsyncDownloader import AsyncDownloader

Related

Import module with relative imports using importlib

I'm writing a python script to import a setting file from another project.
This is the structure of the project:
- root
- ...
- folder_1
- setting_folder
- __init__.py
- setting_1.py
- setting_2.py
- setting_3.py
Here the file contents:
init.py
from .setting_1 import *
from .setting_2 import *
setting_1.py
foo = "foo1"
setting_2.py
foo = "foo2"
try:
from .setting_3 import *
except ImportError:
pass
setting_3.py
foo = "foo3"
What I need to do is, from a script outside the project, load setting_2.py and get the value of foo variable (foo3 due to relative import).
Suppose that I run my script from directory "C:\Users\bar\Desktop".
My idea to achieve this goal is to copy setting_2.py in another directory outside the project (let's say a), create an empty file init.py in a, append to PYTHONPATH "C:\Users\bar\Desktop" and then import the module.
Here the code:
import os
import importlib.util
with open(setting_2_path, "r") as f:
test_file_content = f.read()
setting_tmp_path = r"C:\Users\bar\Desktop\a\setting_2.py"
with open(setting_tmp_path, "w") as f:
f.write(test_file_content)
init_tmp_path = r"C:\Users\bar\Desktop\a\__init__.py"
with open(init_tmp_path, "w") as f:
f.write("")
current_env = os.environ.copy()
current_env.update({'PYTHONPATH': r"C:\Users\bar\Desktop"})
spec_module = importlib.import_module('a.setting_2')
print(getattr(spec_module, "foo"))
This is working well, but in production this script will be in another project and I cannot create a folder at the same level of the script.
I can create the folder but in another directory.
To simulate this scenario, suppose that I run the script from C:\Users\bar\Desktop\bar2 and the folder is C:\Users\bar\Desktop\a.
In this case I have the following error:
ImportError: No module named 'a'
How can I fix the problem?
import os
import sys
cwd = os.getcwd() # This is current working directory, as in your assumption it is: C:\Users\bar\Desktop\bar2
path_to_settings_files = os.path.join(os.path.dirname(cwd), 'a') # This is where settings are, as in your assumption it is: C:\Users\bar\Desktop\a
sys.path.append(path_to_settings_files) # This line guides interpreter to find settings.py in loading modules
import setting_1
import setting_2
import setting_3
print(setting_1.foo)
print(setting_2.foo)
print(setting_3.foo)

Package __init__.py import all subfiles, but only load one from another script?

I have created a package with the following file structure:
- package
- __init__.py
- load.py
- train.py
- test.py
My __init__.py file is simply an import of classes for these files:
from package.load import Load
from package.train import Train
from package.test import Test
Most of the time, I want to load all three, however on occasion I only want to load one of these classes specifically. For example in an ad hoc script (outside of the package) I want to be able to call only the Load class like so:
from package import Load
While all of the above works in this design, I have an issue where dependencies from train/test are also loaded when I import Load like the above. How can I setup the __init__.py file such that I can make the same import call without getting the dependency to load from train/test?
Additional explanation:
Why I am doing this: I have an issue where I want some people to be able to use the Load class, which only uses base python, however the Train/Test files include specialized dependencies which users of just the Load class wont want to utilize or even install.
Here's a way to do something very close what you want. Instead of unconditionally importing all the package's classes in your __init__.py, you can define a function in it to explicitly import any of the ones desired (or all of them if none are specified).
__init__.py:
from pathlib import Path
import sys
print(f'In {Path(__file__).name}')
package_name = Path(__file__).parent.name
package_prefix = package_name + '.'
class_to_module_map = {'Load': 'load', 'Train': 'train', 'Test': 'test'}
def import_classes(*class_names):
namespace = sys._getframe(1).f_globals # Caller's globals.
if not class_names:
class_names = class_to_module_map.keys() # Import them all.
for class_name in class_names:
module = class_to_module_map[class_name]
temp = __import__(package_prefix+module, globals(), locals(), [class_name])
namespace[class_name] = getattr(temp, class_name) # Add to caller's namespace.
For testing purposes, here's what I put in the load.py script:
(I also put something similar in the other two modules in order to verify whether or not they were getting imported.)
load.py:
from pathlib import Path
print(f'In {Path(__file__).name}')
class Load: pass
And finally here's a example of using it to only import the Load class:
ad_hoc.py:
from my_package import import_classes
#from my_package import Load
import_classes('Load')
test = Load()
print(test)
Along with the output produced:
In __init__.py
In load.py
<my_package.load.Load object at 0x001FE4A8>
Inside a folder oranges\, this is our __init__.py file:
__all__ = []
from pathlib import Path
from importlib import import_module
from sys import modules
package = modules[__name__]
initfile = Path(__file__)
for entry in initfile.parent.iterdir():
is_file = entry.is_file()
is_pyfile = entry.name.endswith('.py')
is_not_initpy = (entry != initfile)
if is_file and is_pyfile and is_not_initpy:
module_name = entry.name.removesuffix('.py')
module_path = __name__ + '.' + module_name
module = import_module(module_path)
setattr(package, module_name, module)
__all__.append(module_name)
When we do from oranges import *, the code inside oranges\__init__.py cycles through the *.py files inside oranges\ (except __init__.py), and for each .py file does the following:
imports the .py file as a module into the variable module using Python's importlib.import_module
sets the module as a variable within the __init__.py file (or, more correctly, within the oranges package) using Python's setattr
finally, appends the module to the __all__ list

Python - Get custom modules from current file

I am trying to get a list of the imported custom modules (modules I created by myself) in my current file but I could not find a proper way to achieve it.
For example:
<test.py>
import sys
import foo
import bar
I would like to get a list of [foo, bar] excluding the sys module.
Lets say, that if file located near curent file, or in some subfolder, when it's "our" module, not system one.
For this example, I've created three files: main.py, other.py and another.py, and placed whem into one folder.
The code of main.py is:
# os and sys are needed to work
import sys
import os
import shutil
import io
import datetime
import other
import another
def get_my_modules():
# Get list of modules loaded
modules = list(sys.modules.keys())
mymodules = []
# Get current dir
curdir = os.path.realpath(os.path.dirname(__file__))
for m in modules:
try:
# if some module's file path located in current folder or in some subfolder
# lets sey, that it's our self-made module
path = sys.modules[m].__file__
if path.startswith(curdir):
mymodules.append(m)
except Exception:
# Exception could happen if module doesn't have any __file__ property
pass
# Return list of our moudles
return mymodules
print(get_my_modules())
And this code actually outputs ["other", "another"]
This approach has a problem: If you import module, that somehow located in upper folder, it woudln't be detected.

Manually import a package and sub-modules in python given their name

I have this problem and do not know how to solve it efficiently.
I've this file structure
THE NUMBER OF FOLDERS NOR THE NAMES ARE GIVEN, IT'S ALL UNKNOWN
app/
__init__.py
modules/
__init__.py
ModuleA/
__init__.py
file.py
otherfile.py
config.ini
ModuleB/
__init__.py
file.py
otherfile.py
config.ini
ModuleC/
__init__.py
file.py
otherfile.py
config.ini
**arbitrary number of modules with same structure*
As you can notiche, app is the main package of my app, but I need an efficient way to import the mods folder and its' content
* My actual solution *
from app import modules ad mods
def load_modules_from_packages(self, pkg = mods):
pkgname = pkg.__name__
pkgpath = dirname(pkg.__file__)
for loader,name,ispkg in pkgutil.walk_packages(pkg.__path__, pkgname+'.'):
if ispkg is True:
__import__(name,globals(),locals(),[],0)
elif ispkg is False:
__import__(name,globals(),locals(),[],0)
This works since pkgutil iterate the structure with the dot notation for names, so import works well.
But now I want load infos in the config file if I am in one of the somemodule folder(the one with own init.py and config.ini
I want to do this to recreate the structure of module package and output it in a JSON rapresentation for another thing
* my other solution does not works*
def load_modules_from_packages(directory)
dir_path = dirname(directory.__file__)
dir_name = directory.__name__
for filename in glob.glob(dir_path + '/**/*.ini', recursive=True):
plugin = {}
plugin['name'] = filename.split('/')[-2]
plugin['path'] = dirname(filename)
plugin['config_file'] = filename
for pyname in glob.glob(dirname(filename)+ '/**/*.py', recursive=True):
importlib.import_module(pyname)
I cant use the solution posted in this thread
How to import a module given the full path?
since I do not know the module name, ad pointed without solutions in the comment.
spec = importlib.util.spec_from_file_location('what.ever', 'foo.py')
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
I know 'foo.py' but I cant figure out 'what.ever' like pkgutil.walk_package does.
In fact the modules imported in this way have the package and name entry wrong. With this approach I cant figure out where I am in the file structure to create modules dictionary and the relative modules (for the JSON output)
Any help?

How to structure a Python package so others can add modules easily

I have a component-oriented Python (3.3) project. I have a base Component class. I want others to be able to add modules that contain subclasses of Component just by copying their .py file(s) into some folder, without having to edit anything. Running the main program should then simply import all the .py files found in that folder. All accesses from my main program to these subclasses are via Component.__subclasses__(), not by explicit name. I am not especially worried about name clashes between code in different user-written modules, but of course I would like to avoid it if possible without screwing up the simple drop-file-into-folder inclusion.
How do I structure a package to achieve this?
I would structure the package like this:
myPackage
+ -- __init__.py
+ -- Component.py
+ -- user_defined_packages
+ -- __init__.py # 1
+ -- example.py
Ideas:
let the users drop into a different folder so that they do not mix up your code and theirs
The init file in user_defined_packages can load all the subpackages once user_defined_packages is imported. It must print all errors.
__init__.py # 1
import os
import traceback
import sys
def import_submodules():
def import_submodule(name):
module_name = __name__ + '.' + name
try:
__import__(module_name)
except:
traceback.print_exc() # no error should pass silently
else:
module = sys.modules[module_name]
globals()[name] = module # easier access
directory = os.path.dirname(__file__)
for path_name in os.listdir(directory):
path = os.path.join(directory, path_name)
if path_name.startswith('_'):
# __pycache__, __init__.py and others
continue
if os.path.isdir(path):
import_submodule(path_name)
if os.path.isfile(path):
name, extension = os.path.splitext(path_name)
if extension in ('.py', '.pyw'):
import_submodule(name)
import_submodules()

Categories

Resources