NameError: global name is not defined - python

I know there are a bunch of similar questions, but they didn't help me to understand my problem. Also I have 3 modules:
First one is model:
from datetime import datetime
from elixir import *
from run_test import create_db
from sqlalchemy.schema import UniqueConstraint
class ValueTest(Test):
value = Field(Integer)
def __init__(self, name, value):
'''
Constructor
'''
self.name = name;
self.value = value
If I run the test method from the second module named run_test, there aren't any problems
from model import *
def main():
test();
def test():
test = ValueTest("test",2)
if __name__ == "__main__":
main()
But when I try something like that, I get the the well known error NameError: global name 'ValueTest' is not defined
import run_test
def main():
run_test.test()
if __name__ == '__main__':
main()

When you import a module, you don't import the names it has imported; you only import the names it defines itself. You still need to do from model import ValueTest in the last script.
If from foo import * imported every name that foo imported into its own scope, a single import something might also import every symbol in os or sys for example. It would be a nightmare.
Actually, this is not true. The symbols imported from the module are only those defined by the __all__ list set in that module. (If not present, all symbols not starting with _ are indeed imported.)
Thanks Ethan for the correction.

The problem is you have circular imports happening. run_test is importing model, which in turn is importing run_test. Strange things happen when circular imports are used. If you can, put the common functions (create_db, in your example) into another module, then model can import it from there and not from run_test.

Related

What is the scope of an imported function

If I have a python file as follows
import time
class abc:
def func1(self):
while True:
do something
time.sleep(5)
xyz = abc()
xyz.func1()
The above works exactly as expected
If I move the class definition out to a seperate .py file and import it
import time
from test1 import abc
xyz = abc()
xyz.func1()
Works as expected until it hits the time.sleep(5) when it errors
saying time is not defined
I have tried adding the time import into the class definition but
still seem to get the same problem
What am I missing.
Moving the import to the class definition file doesn't seem to resolve the problem, either adding it at the top or in the init .
Take a look at the python documentation about the import statement.
Quote from there:
define a name or names in the local namespace for the scope where the import statement occurs.
So your toplevel import is bound on module level and the name time is accessible from wherever in that module.
If you move the import inside the method, it will be accessible in the local scope of the method and won't be visible outside of that method.
So, you don't want to move the import in your other module, as you don't use it there. To make things work, you should preserve the import of time where it is now and only move your function calls outside:
# test1.py
import time
class ABC:
def func1(self):
while True:
# do something
time.sleep(5)
print('slept 5 secs')
# main.py
from test1 import ABC
abc = ABC()
abc.func1()
[Not related to your question tip] You should also take a look at PEP8 and follow it's conventions.

How to initialize whole Python module and import all classes without needing to reference module name for every call?

Suppose I have a module named 'module1', which has many classes. I want to use all of their classes in my main controller main.py . I can import whole module with import module1, however, I assume that the class names in 'module1' are unique and I do not want to reference module1 every time I call its function such as module1.class1().
How can I import the whole contents of a module without explicitly having to state class/function names and still being able to refer to them without stating the module name.
Such as:
# module1.py.
class Class1():
pass
class Class2():
pass
# __main__.py
import module1
# The following will not work.
object1 = Class1()
object2 = Class2()
You can use the * import pattern
from module1 import *
This lets you use everything inside that module without referencing it.
from module1 import *
instance = Class1()
However, this is generally discouraged. For reasons why, some additional reading:
Why is "import *" bad?
https://www.geeksforgeeks.org/why-import-star-in-python-is-a-bad-idea/

How to trigger a late import of another module only when a certain object is needed from my module?

The Situation
I want to have a module that roughly works like the following:
# my_module.py
my_number = 17
from other_module import foo
my_object = foo(23)
However, there is a problem: Installing other_module causes problems for some users and is only required for those who want to use my_object – which in turn is only a small fraction of users. I want to spare those users who do not need my_object from installing other_module.
I therefore want the import of other_module to happen only if my_object is imported from my_module. With other words, the user should be able to run the following without having installed other_module:
from my_module import my_number
My best solution so far
I could provide my_object via a function that contains the import:
# in my_module.py
def get_my_object():
from other_module import foo
my_object = foo(23)
return my_object
The user would then have to do something like:
from my_module import get_my_object
my_object = get_my_object()
Question
Is there a better way to conditionally trigger the import of other_module? I am mostly interested in keeping things as simple as possible for the users.
I would prefer the get_my_object() approach, but as of Python 3.7, what you ask is possible by defining a module-level __getattr__ function:
# my_module.py
my_number = 17
def __getattr__(name):
if name != 'my_object':
raise AttributeError
global my_object
from other_module import foo
my_object = foo(23)
return my_object
This will attempt to import other_module and call foo only once my_object is accessed. A few caveats:
It will not trigger for attempts to access my_object by global variable lookup within my_module. It will only trigger on my_module.my_object attribute access, or a from my_module import my_object import (which performs attribute access under the hood).
If you forget to assign to the global my_object name in __getattr__, my_object will be recomputed on every access.
Module-level __getattr__ does nothing before Python 3.7, so you may want to perform a version check and do something else for Python 3.6 and below:
import sys
if sys.version_info >= (3, 7):
def __getattr__(name):
...
else:
# Do something appropriate. Maybe raise an error. Maybe unconditionally
# import other_module and compute my_object up front.
Approach A – clean solution
Create a new separate module and have the user import the object from the other module. For example
from my_module import my_number # regular use
from my_module.extras import my_object # for a small part of the user base
This means in your code you create a module folder my_module with an __init__.py where you import the usual stuff and don't import the extras submodule.
If you don't want to put extras in my_module (simpler), just create my_object in an individual extras.py module.
Approach B – signals bad architecture 99% of times
You can use importlib.import_module to dynamically import a module inside get_my_object without polluting the global space and that is cleaner than an import inside a function which creates side effects such as overriding your global variable with that import name (see this question and answers), however this is usually a sign of bad coding patterns on other part of the code.
Approach C – simple and effective
I usually tend to favour this simple pattern when there are users that might not have a library, as Python 3 discourages imports that are not at top level:
try:
import other_module
_HAS_OTHER_MODULE_ = True
except:
_HAS_OTHER_MODULE_ = False
def get_my_object():
assert _HAS_OTHER_MODULE_, "Please install other module"
return other_module.foo(23)
This is a common hack to be done:
import sys
class MockModule:
def __init__(self, module):
self.module = module
def __getattr__(self, attr):
if attr == 'dependency_required_var':
try:
import foo
return self.module.dependency_required_var
except ImportError:
raise Exception('foo library is required to use dependency_required_var')
else:
return getattr(self.module, attr)
MockModule.__name__ = __name__
sys.modules[__name__] = MockModule(sys.modules[__name__])
dependency_required_var = 0
With this PEP, we can simply do (we should be able to but I couldn't get it to work) the following in Python 3.7 and higher:
def __getattr__(attr):
if attr == 'dependency_required_var':
try:
import foo
return dependency_required_var
except ImportError:
raise Exception('foo library is required to use dependency_required_var')
else:
return globals()[attr]
The PEP seems to be accepted, but the relevant pull request from the PEP seems to be closed, I'm actually not sure if it has been implemented or not.

PyCharm "Unused import statement" when adding method to class

I want add a method to a class, without extending another class (for the import statements should be unaltered).
Conceptually, my current approach is:
add_method.py
def new_method():
pass
MyObject.new_method = new_method
main.py
from package import MyObject
import add_method
ob = MyObject()
ob.new_method()
This does the job, however, PyCharm does not recognize that the import add_method import statement is acutally used: "Unused import statement". Is there an elegant way to obtain the same effect with PyCharm recognizing the import?

Import a class variable from another module

I'm trying to import just a variable inside a class from another module:
import module.class.variable
# ImportError: No module named class.variable
from module.class import variable
# ImportError: No module named class
from module import class.variable
# SyntaxError: invalid syntax (the . is highlighted)
I can do the following, but I'd prefer to just import the one variable I need.
from module import class as tmp
new_variable_name = tmp.variable
del tmp
Is this possible?
variable = __import__('module').class.variable
You can't do that -
the import statement can only bring elements from modules or submodules - class attributes, although addressable with the same dot syntax that is used for sub-module access, can't be individually imported.
What you can do is:
from mymodule import myclass
myvar = myclass.myvar
del myclass
Either way, whenever one does use the from module import anything syntax, the whole module is read and processed.The exception is
from module.submodule import submodule
, where, if thesubmodule itself does not require the whole module, only the submodule is processed.
(So, even onmy workaround above , mymodule is read, and executed - it is not made accessible in the global namespace where the import statement is made, but it will be visible, with all its components, in the sys.modules dictionary.
Follow these two steps
Import the class from the module
from Module import Class
Assign new variables to the existing variables from the class
var1=Class.var1
var2=Class.var2

Categories