Django - importing dynamically - python

I'm trying to do a dynamic import of a python module in django. I have two different apps that I want to import from, and I want to replace these import statements:
from app1.forms import App1ProfileForm
from app2.forms import App2ProfileForm
I am dynamically able to create the strings App1ProfileForm and App2ProfileForm and then instantiate them like so:
globals()[form]()
I tried following some of the instructions in this post: Dynamically import class by name for static access
and so I tried doing this:
theModule = __import__("app1.forms.App1ProfileForm")
but I'm getting an error that says No module named App1ProfileForm
EDIT:::
Ok I tried this code:
theModule = __import__("app1")
print theModule
theClass = getattr(theModule,'forms')
print theClass
theForm = getattr(theClass,'App1ProfileForm')
print theForm
theForm.initialize()
but I get an error that type object 'App1ProfileForm' has no attribute 'initialize'

You don't want to do this. Imports are done when the relevant code is first executed - in the case of module-level imports, it's when the module itself is imported. If you're depending on something in the request, or some other run-time element, to determine what class you want, then this will not work.
Instead, just import them both, and get the code to choose which one you need:
from app1.forms import App1ProfileForm
from app2.forms import App2ProfileForm
forms = {'app1': App1ProfileForm,
'app2': App2ProfileForm}
relevant_form = forms[whatever_the_dependent_value_is]

I don't quite know how you're generting the string to import. I'll assume you generate the whole "path". Try this:
def import_from_strings(paths):
ret = []
for path in paths:
module_name, class_name = path.rsplit('.', 1)
module = __import__(module_name, globals(), locals(), [class_name], -1)
ret.append(getattr(module, class_name))
return ret

Aren't you trying to import a class, and not a module ? I'm not an expert, but I think you must import the module using __import__, then select it's App1ProfileForm class with something like yourmodule.App1ProfileForm

I figured it out. Here's how to do it:
theModule = __import__(module_name+".forms") # for some reason need the .forms part
theClass = getattr(theModule,'forms')
theForm = getattr(theClass,form_name)
then to initialize:
theForm() or theForm(request.POST)

Related

Python: always import the last revision in the directory

Imagine that we have the following Data Base structure with the data stored in python files ready to be imported:
data_base/
foo_data/
rev_1.py
rev_2.py
bar_data/
rev_1.py
rev_2.py
rev_3.py
In my main script, I would like to import the last revision of the data available in the folder. For example, instead of doing this:
from data_base.foo_data.rev_2 import foofoo
from data_base.bar_data.rev_3 import barbar
I want to call a method:
import_from_db(path='data_base.foo_data', attr='foofoo', rev='last')
import_from_db(path='data_base.bar_data', attr='barbar', rev='last')
I could take a relative path to the Data Base and use glob.glob to search the last revision, but for this, I should know the path to the data_base folder, which complicates things (imagine that the parent folder of the data_base is in sys.path so the from data_base.*** import will work)
Is there an efficient way to maybe retrieve a full path knowing only part of it (data_base.foo_data)? Other ideas?
I think it's better to install the last version.
but going on with your flow, you may use getattr on the module:
from data_base import foo_data
i = 0
while True:
try:
your_module = getattr(foo_data, f'rev_{i}')
except AttributeError:
break
i += 1
# Now your_module is the latest rev
#JohnDoriaN 's idea led me to a quite simple solution:
import os, glob
def import_from_db(import_path, attr, rev_id=None):
"""
"""
# Get all the modules/folders names
dir_list = import_path.split('.')
# Import the last module
exec(f"from {'.'.join(dir_list[:-1])} import {dir_list[-1]}")
db_parent = locals()[dir_list[-1]]
# Get an absolute path to corresponding to the db_parent folder
abs_path = db_parent.__path__._path[0]
rev_path = os.path.join(abs_path, 'rev_*.py')
rev_names = [os.path.basename(x) for x in glob.glob(rev_path)]
if rev_id is None:
revision = rev_names[-1]
else:
revision = rev_names[rev_id]
revision = revision.split('.')[0]
# import attribute
exec(f'from {import_path}.{revision} import {attr}', globals())
Some explanations:
Apparently (I didn't know this), we can import a folder as a module; this module has a __path__ attribute (found out using the built-in dir method).
glob.glob allows us to use regex expressions to search for a required pattern for files in the directory.
using exec without parameters will import only in the local namespace (namespace of the method) so without polluting the global namespace.
using exec with globals() allows us to import in the global namespace.

Use inspect module to grab the name of inherited object

I want to look at a file and get the names of classes and check if the "Runconfig" name is inherited. So if a file has
class some_function(RunConfig):
I want to return true.
My code looks like this right now:
for file in list_of_files:
if file in ['some_file.py']:
for name,obj in inspect.getmembers(file):
if inspect.isclass(obj):
print("NAME",name,"obj",obj)
This returns objects but I don't see anything that says 'RunConfig' on it.
What am I missing here?
Thank you so much in advance!
You can do something like:
import importlib
import inspect
def is_class_inherited_in_file(file_name, class_ref):
module = importlib.import_module(file_name.split('.')[0])
module_members = inspect.getmembers(module)
for member in module_members:
if type(member[1]) == type and issubclass(member[1], class_ref):
return True
return False
>>> is_class_inherited_in_file('some_file.py', RunConfig)
True
Assumption:
The filename is in the working directory. If you would like to import from any directory, then do something like: How to import a module given the full path?

Possible to generate auto-complete from a namespace object?

Imagine I have a .env file that looks like this.
EARTH_SYNONYM1 = "World"
EARTH_SYNONYM2 = "Planet"
EARTH_SYNONYM3 = "Globe"
I've managed to load it into a namespace.
import json
from pathlib import Path
from types import SimpleNamespace
from dotenv.main import dotenv_values # dotenv package needs to be installed.
def json_to_python(json_str):
return json.loads(json_str, object_hook=lambda d: SimpleNamespace(**d))
dotenv_path = Path(".") / ".env"
dotenv_vars_list = dotenv_values(dotenv_path)
dotenv_vars_as_json = json.dumps(dotenv_vars_list)
dotenv_vars = json_to_python(dotenv_vars_as_json)
print(dotenv_vars)
Which prints
namespace(EARTH_SYNONYM1='World', EARTH_SYNONYM2='Planet', EARTH_SYNONYM3='Globe')
Now I can do things like
print(f"Hello {dotenv_vars.EARTH_SYNONYM1}")
Which prints
Hello World
What I would love to accomplish is turning this into a class module (I think).
I'd like to
import dotenv_vars
And then type
dotenv_vars.
and be presented with a list of auto-complete options.
Continuing this example, each of the EARTH_SYNONYMx would show as auto-complete options.
How can I make the namespace object provide auto-complete?
I always thought dotenv is a pretty silly library and overcomplicating something that is so simple. Load your vars into a module namespace with Python's import system.
import imp
dotenv_vars = imp.load_source('dotenv_vars', '.env')

how to avoid importing self, intra-package relationships

The directory structure is like so:
AppCenter/
main.pyw
|
|
_apps/
__init__.py
TabularApp.py
UserAdministrationApp.py
RegisterApp.py
FnAdminApp.py
PyUi/
The contents of __init__.py:
import sys
sys.path.insert(1, '.')
__all__ = ['TabularApp',
'UserAdministrationApp',
'RegisterApp',
'FnAdminApp']
The problems pop up:
When main.pyw tries to from _apps import *.
In UserAdministrationApp.py i am trying to dynamically add tooltips to some QListWidget items like so:
for app in self.__APPS__:
app_icon = str(os.path.join(app_icons, f"{app}.png")).replace('\\', '/')
icon = QIcon(app_icon)
if app != self.__class__:
ttip_txt = eval(f'_apps.{app}.__doc__')
else:
ttip_txt = self.__doc__
item = QListWidgetItem(icon, app)
item.setText(app)
item.setToolTip(ttip_txt)
wdg.addItem(item)
The self.__APPS__ is just a copy of _apps.__all__.
The first problem I encountered was that i would get an AttributeError saying module x has no attribute y in ttip_txt = eval(f'_apps.{app}.__doc__') I resolved this by from _apps import * in UserAdministrationApp module. At this point I had already renamed this module for testing purposes and everything worked, but when I changed the name back to UserAdministrationApp.py I got another AttributeError saying module __apps has no attribute UserAdministrationApp.
Questions
I tried reading the python import docs but nothing in it really spoke to me.
I am sensing it has something to do with the script trying to import itself.
But i am still intrigued by these questions:
Why did the import fail in the first case, when i have import _apps?
Why in the second case does it not at least see itself and then produce an ImportError instead of AtributeError?
What is the optimal way to handle these types of situations?
Okay I found a solution, and though i think it is a bit dirty and not in best style, it works.
First
remove the from _apps import * and just from _apps import __all__.
Then
In initialization of the main class from the module UserAdministrationApp import in a loop skipping self.__class_.__name__
self.__APPS__ = _apps.__all__
self.class_name = self.__class__.__name__
for app in self.__APPS__:
if self.class_name != app:
exec(f'import _apps.{app}')
Finally
for app in self.__APPS__:
app_icon = str(os.path.join(app_icons, f"{app}.png")).replace('\\', '/')
icon = QIcon(app_icon)
if app != self.class_name:
ttip_txt = eval(f'_apps.{app}.__doc__')
else:
ttip_txt = self.__doc__
Having found the solution, I would still like to hear why the error was in the first place, for educational purposes.
So if anybody at any time glances over this and knows how to...you are more than welcome.

Reference another class in python

I'm just starting out with Python for Google App Engine. I have a file notifications.py, and in here, I will be creating User entities, which are specified in users.py. How can I do this? I've tried import users, but I get an error: NameError: global name 'User' is not defined
Oh, I just had this problem too! After you do:
import users
to get User you have to type users.User
Alternatively you could import it like:
from users import User
then reference it as just User but if you do it this way you'll have to list every bit from users that you want in the following format:
from users import User, Somthingelse, Somthing
If you're feeling super lazy and you don't want to type in any prefixes or list all the things you want, just type
from users import *
Instead of
import users
do
from users import User
# module.py
foo = "bar"
# main.py
import module
print foo # This will cause error because foo is not located in the current namespace
print module.foo # this will print "bar"
from module import foo # But you can import contents of module "module" in the current namespace
http://docs.python.org/tutorial/modules.html

Categories