I created a package in python. In the init file I placed a variable that must be available in all the modules of this package.
Example:
# main script
from _Framework.STB import DB
DB.environment = 'lab'
channel = DB.Channels.ChannelStatic.getChannelByNumber(416)
...
# Channels.py module in the package
from _Framework.DB.__init__ import cursor
from _Framework.DB.__init__ import environment
from time import *
...
The problem is that everdy call to
from _Framework.DB.__init__ import xy
overwrites my "global" variables
How can I solve?
Thanks
EDIT:
init.py:
all = [ 'Events', 'Channels', 'Genres', 'Subgenres','EPGSections']
try:
conn = MySQLdb.connect(host,user,passwd,db)
cursor = conn.cursor(MySQLdb.cursors.DictCursor)
except:
cursor = None
environment = 'live'
I import the DB package and I set the "environment" variable, but when the Channels module is imported, I think, it makes a new call to init.py and reexecutes its code so "environment" is overwritten. I'm looking for a clean way to share a variable between modules of the same package
Importing from module __init__ like
from package.__init__ import X
runs the module code every time. That's why your variable gets overwritten. This is because the __init__ module is getting added twice(with different names) in sys.modules. First, as your package's name when it is importing it then as package.__init__ while importing it explicitly. As the source is loaded twice, the code is executed twice.
However you don't need to do it like this, you only need to do:
from _Framework.DB import cursor
from _Framework.DB import environment
Then your code would only run once.
I'm still not sure but if I understand correctly. YOu should just use the edit on your question and post correctly formatted code:
__init__.py:
all = [ 'Events', 'Channels', 'Genres', 'Subgenres','EPGSections']
try:
conn = MySQLdb.connect(host,user,passwd,db)
cursor = conn.cursor(MySQLdb.cursors.DictCursor)
except:
cursor = None
environment = 'live
Now in Channels.py you want to change _Framework.DB.__init__.cursor and _Framework.DB.__init__.environment and this changes should be seen all around your program?
If this is what you want you should know that when you do in a import:
from _Framework.DB.__init__ import cursor
from _Framework.DB.__init__ import environment
What happens is that you get two variables cursor and environment in your Channels.py module namespace and any change you do will only be visible in that module. If you want to achieve your result try this:
import _Framework.DB
_Framework.DB.cursor = new_cursor
_Framework.DB.environment = new_environment
This way you actually change the module variables. Now in the rest of your program, when you want to see the changes took effect you could either:
Acces the varibles in the same way you changed them:
import _Framework.DB
_Framework.DB.cursor #changes made in other module will have effect
_Framework.DB.environment #changes made in other module will have effect
import like you did, BUT make sure the changes you want are done BEFORE you do any imports.
As an negative example for the second case:
#module Vars
a = 1
--------------------------
#module Do_change
import Vars
Vars.a = 2
--------------------------
#module A
from Vars import a
--------------------------
#module B
from Vars import a
--------------------------
#some random module
import A
import Do_change
import B
Now if you are in this order of imports when you import A it will create a variable a in A's namespace with the value of Vars.a. The Do_change will edit the variable accordingly, but A will have no knowledge of this. B however who is imported after will see the correct Vars.a = 2.
That's why I really reccomend the first option as it is the correct approach in my opinion.
Related
I am developping a program which runs on two different plattforms. Depending on which plattform I want to run it, the import directories and the names of the libraries change. For this reason i set a variable called RUN_ON_PC to True or False.
I want to implement a helper which sets the paths correctly and imports the libraries with the correct name depending of the platform and gives an interface with the same name of the libraries to the main program. The module myimporthelper is either in the "/mylib" or in the "/sd/mylib" directory. The other module names in these directories differ.
I try to do the following which is not working, since the imported modules from myimporthelper.py are not visible to main.py:
main.py:
RUN_ON_PC = True
import sys
if RUN_ON_PC:
sys.path.append("/mylib1")
else:
sys.path.append("/sd/mylib1")
import myimporthelper
myimporthelper.importall(RUN_ON_PC)
a = moduleA.ClassA() -> produces NameError: name not defined
myimporthelper.py:
import sys
def importall(run_on_pc):
if (run_on_pc == True):
sys.path.append("C:\\Users\\.....\\mylib")
import module1 as moduleA
else:
sys.path.append("/sd/mylib")
import module_a as moduleA
I want to keep the main.py short and want to outsource the platform dependent importing stuff to other module. I was not able to find a solution for this and would aprecciate any help.
Thanks a lot in advance.
You just have to qualify the name with the helper module name
a = myimporthelper.moduleA.ClassA()
But the moduleA name has to be accessible. If you import it inside a function in the helper it won't be, because of scope, unless you assign it to a name you previously declared as global in the helper module function.
I'm bit confused about how the global variables work. I have a large project, with around 50 files, and I need to define global variables for all those files.
What I did was define them in my projects main.py file, as following:
# ../myproject/main.py
# Define global myList
global myList
myList = []
# Imports
import subfile
# Do something
subfile.stuff()
print(myList[0])
I'm trying to use myList in subfile.py, as following
# ../myproject/subfile.py
# Save "hey" into myList
def stuff():
globals()["myList"].append("hey")
An other way I tried, but didn't work either
# ../myproject/main.py
# Import globfile
import globfile
# Save myList into globfile
globfile.myList = []
# Import subfile
import subfile
# Do something
subfile.stuff()
print(globfile.myList[0])
And inside subfile.py I had this:
# ../myproject/subfile.py
# Import globfile
import globfile
# Save "hey" into myList
def stuff():
globfile.myList.append("hey")
But again, it didn't work. How should I implement this? I understand that it cannot work like that, when the two files don't really know each other (well subfile doesn't know main), but I can't think of how to do it, without using io writing or pickle, which I don't want to do.
The problem is you defined myList from main.py, but subfile.py needs to use it. Here is a clean way to solve this problem: move all globals to a file, I call this file settings.py. This file is responsible for defining globals and initializing them:
# settings.py
def init():
global myList
myList = []
Next, your subfile can import globals:
# subfile.py
import settings
def stuff():
settings.myList.append('hey')
Note that subfile does not call init()— that task belongs to main.py:
# main.py
import settings
import subfile
settings.init() # Call only once
subfile.stuff() # Do stuff with global var
print settings.myList[0] # Check the result
This way, you achieve your objective while avoid initializing global variables more than once.
See Python's document on sharing global variables across modules:
The canonical way to share information across modules within a single program is to create a special module (often called config or cfg).
config.py:
x = 0 # Default value of the 'x' configuration setting
Import the config module in all modules of your application; the module then becomes available as a global name.
main.py:
import config
print (config.x)
In general, don’t use from modulename import *. Doing so clutters the importer’s namespace, and makes it much harder for linters to detect undefined names.
You can think of Python global variables as "module" variables - and as such they are much more useful than the traditional "global variables" from C.
A global variable is actually defined in a module's __dict__ and can be accessed from outside that module as a module attribute.
So, in your example:
# ../myproject/main.py
# Define global myList
# global myList - there is no "global" declaration at module level. Just inside
# function and methods
myList = []
# Imports
import subfile
# Do something
subfile.stuff()
print(myList[0])
And:
# ../myproject/subfile.py
# Save "hey" into myList
def stuff():
# You have to make the module main available for the
# code here.
# Placing the import inside the function body will
# usually avoid import cycles -
# unless you happen to call this function from
# either main or subfile's body (i.e. not from inside a function or method)
import main
main.mylist.append("hey")
Using from your_file import * should fix your problems. It defines everything so that it is globally available (with the exception of local variables in the imports of course).
for example:
##test.py:
from pytest import *
print hello_world
and:
##pytest.py
hello_world="hello world!"
Hai Vu answer works great, just one comment:
In case you are using the global in other module and you want to set the global dynamically, pay attention to import the other modules after you set the global variables, for example:
# settings.py
def init(arg):
global myList
myList = []
mylist.append(arg)
# subfile.py
import settings
def print():
settings.myList[0]
# main.py
import settings
settings.init("1st") # global init before used in other imported modules
# Or else they will be undefined
import subfile
subfile.print() # global usage
Your 2nd attempt will work perfectly, and is actually a really good way to handle variable names that you want to have available globally. But you have a name error in the last line. Here is how it should be:
# ../myproject/main.py
# Import globfile
import globfile
# Save myList into globfile
globfile.myList = []
# Import subfile
import subfile
# Do something
subfile.stuff()
print(globfile.myList[0])
See the last line? myList is an attr of globfile, not subfile. This will work as you want.
Mike
I just came across this post and thought of posting my solution, just in case of anyone being in the same situation as me, where there are quite some files in the developed program, and you don't have the time to think through the whole import sequence of your modules (if you didn't think of that properly right from the start, such as I did).
In such cases, in the script where you initiate your global(s), simply code a class which says like:
class My_Globals:
def __init__(self):
self.global1 = "initial_value_1"
self.global2 = "initial_value_2"
...
and then use, instead of the line in the script where you initiated your globals, instead of
global1 = "initial_value_1"
use
globals = My_Globals()
I was then able to retrieve / change the values of any of these globals via
globals.desired_global
in any script, and these changes were automatically also applied to all the other scripts using them. All worked now, by using the exact same import statements which previously failed, due to the problems mentioned in this post / discussion here. I simply thought of global object's properties being changing dynamically without the need of considering / changing any import logic, in comparison to simple importing of global variables, and that definitely was the quickest and easiest (for later access) approach to solve this kind of problem for me.
Based on above answers and links within I created a new module called global_variables.py:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ==============================================================================
#
# global_variables.py - Global variables shared by all modules.
#
# ==============================================================================
USER = None # User ID, Name, GUID varies by platform
def init():
""" This should only be called once by the main module
Child modules will inherit values. For example if they contain
import global_variables as g
Later on they can reference 'g.USER' to get the user ID.
"""
global USER
import getpass
USER = getpass.getuser()
# End of global_variables.py
Then in my main module I use this:
import global_variables as g
g.init()
In another child imported module I can use:
import global_variables as g
# hundreds of lines later....
print(g.USER)
I've only spent a few minutes testing in two different python multiple-module programs but so far it's working perfectly.
Namespace nightmares arise when you do from config import mySharedThing. That can't be stressed enough.
It's OK to use from in other places.
You can even have a config module that's totally empty.
# my_config.py
pass
# my_other_module.py
import my_config
def doSomething():
print(my_config.mySharedThing.message)
# main.py
from dataclasses import dataclass
from my_other_module import doSomething
import my_config
#dataclass
class Thing:
message: str
my_config.mySharedThing = Thing('Hey everybody!')
doSomething()
result:
$ python3 main.py
Hey everybody!
But using objects you pulled in with from will take you down a path of frustration.
# my_other_module.py
from my_config import mySharedThing
def doSomething():
print(mySharedThing.message)
result:
$ python3 main.py
ImportError: cannot import name 'mySharedThing' from 'my_config' (my_config.py)
And maybe you'll try to fix it like this:
# my_config.py
mySharedThing = None
result:
$ python3 main.py
AttributeError: 'NoneType' object has no attribute 'message'
And then maybe you'll find this page and try to solve it by adding an init() method.
But the whole problem is the from.
I create the following package in eclipse via PyDev:
class Repository(object):
'''
classdocs
'''
def __init__(self):
'''
Constructor
'''
print("salaam")
class Materials(Repository):
'''
'''
def __init__(self):
'''
constructor
'''
My main file is:
if __name__ == '__main__':
pass
import repository;
x = Repository();
When i run my application, i get the following error:
x = Repository();
NameError: name 'Repository' is not defined
Of course, i got a warning on importing my module.
I know my import and relation of my main file and my package or eclipse configuration have problem.
first of all, when you import like this, you can only refer to your class as either repository.Repository or repository.repository.Repository, depending on the whether you import the module or the package.
second, what you import depends on where eclipse thinks you are. You can check that with
import os
print(os.pwd)
at the top of your main script.
third, if you want to import your package like this, you should put it in your search path. You can do that by placing it in site-packages, or for instance by adding
import sys
import os
sys.path.append(os.path.abspath(__file__))
at the top of your main script
additionally, you might want to avoid confusion by giving your module a different name than the package (or the other way round)
(and a little nitpick: __init__ is not the constructor, merely an initializing routine).
The import is wrong.
Instead of
import repository
you want to write for your case:
from repository.repository import Repository
As for PyDev giving the error, it's correct at this point and when you fix your code it should stop complaining.
I'm trying to temporarily remove a python module from sys.modules so that I can import it as part of a test case (with various system functions mocked out) and then put it back again. (Yes, that's a bit crazy and I'm probably going to end up restructuring the code instead but now I'm curious...)
I can remove the module and reimport it just fine but I can't seem to put it back to the original module once I'm finished. (Maybe that's just not posible?) Here's a test case that I wrote to test out the idea:
class Test(unittest.TestCase):
def test_assumptions(self):
import meta.common.fileutils as fu1
del(sys.modules["meta.common.fileutils"])
import meta.common.fileutils
del(sys.modules["meta.common.fileutils"])
sys.modules["meta.common.fileutils"] = fu1 # I hoped this would set the module back
import meta.common.fileutils as fu2
self.assertEqual(fu1, fu2) # assert fails, fu2 is a new copy of module :-(
Can anyone suggest why it might be failing?
Edit, using pop() as suggested by one of the answers also fails:
class Test(unittest.TestCase):
def test_assumptions(self):
import meta.common.fileutils as fu1
orig = sys.modules.pop("meta.common.fileutils")
import meta.common.fileutils
del(sys.modules["meta.common.fileutils"])
sys.modules["meta.common.fileutils"] = orig
import meta.common.fileutils as fu2
self.assertEqual(fu1, orig) # passes
self.assertEqual(fu2, orig) # fails
self.assertEqual(fu1, fu2) # fails
It looks to me like the issue here has to do with packages. In particular, for a module that lives in a package (eg meta.common), there are two ways to access it: via sys.modules, and via the parent package's dictionary (i.e., meta.common.__dict__). It looks to me like the import meta.common.fileutils as fu2 line is getting fu2's value from meta.common.__dict__, and not from sys.modules.
So the solution: in addition to monkey-patching sys.modules, you should also monkey-patch the parent package. I.e., add something like this:
>>> import meta.common
>>> meta.common.fileutils = fu1
right before the sys.modules["meta.common.fileutils"] = fu1 line.
The sys.modules structure is really just a Python dict. You can remove modules from it, and you can also put them back in.
Store the original module object in a local variable, using dict.pop() to both remove the module and return it:
orig = sys.modules.pop('meta.common.fileutils')
then, when it comes to restoring it, just put that object back into sys.modules:
sys.modules['meta.common.fileutils'] = orig
Summary: when a certain python module is imported, I want to be able to intercept this action, and instead of loading the required class, I want to load another class of my choice.
Reason: I am working on some legacy code. I need to write some unit test code before I start some enhancement/refactoring. The code imports a certain module which will fail in a unit test setting, however. (Because of database server dependency)
Pseduo Code:
from LegacyDataLoader import load_me_data
...
def do_something():
data = load_me_data()
So, ideally, when python excutes the import line above in a unit test, an alternative class, says MockDataLoader, is loaded instead.
I am still using 2.4.3. I suppose there is an import hook I can manipulate
Edit
Thanks a lot for the answers so far. They are all very helpful.
One particular type of suggestion is about manipulation of PYTHONPATH. It does not work in my case. So I will elaborate my particular situation here.
The original codebase is organised in this way
./dir1/myapp/database/LegacyDataLoader.py
./dir1/myapp/database/Other.py
./dir1/myapp/database/__init__.py
./dir1/myapp/__init__.py
My goal is to enhance the Other class in the Other module. But since it is legacy code, I do not feel comfortable working on it without strapping a test suite around it first.
Now I introduce this unit test code
./unit_test/test.py
The content is simply:
from myapp.database.Other import Other
def test1():
o = Other()
o.do_something()
if __name__ == "__main__":
test1()
When the CI server runs the above test, the test fails. It is because class Other uses LegacyDataLoader, and LegacydataLoader cannot establish database connection to the db server from the CI box.
Now let's add a fake class as suggested:
./unit_test_fake/myapp/database/LegacyDataLoader.py
./unit_test_fake/myapp/database/__init__.py
./unit_test_fake/myapp/__init__.py
Modify the PYTHONPATH to
export PYTHONPATH=unit_test_fake:dir1:unit_test
Now the test fails for another reason
File "unit_test/test.py", line 1, in <module>
from myapp.database.Other import Other
ImportError: No module named Other
It has something to do with the way python resolves classes/attributes in a module
You can intercept import and from ... import statements by defining your own __import__ function and assigning it to __builtin__.__import__ (make sure to save the previous value, since your override will no doubt want to delegate to it; and you'll need to import __builtin__ to get the builtin-objects module).
For example (Py2.4 specific, since that's what you're asking about), save in aim.py the following:
import __builtin__
realimp = __builtin__.__import__
def my_import(name, globals={}, locals={}, fromlist=[]):
print 'importing', name, fromlist
return realimp(name, globals, locals, fromlist)
__builtin__.__import__ = my_import
from os import path
and now:
$ python2.4 aim.py
importing os ('path',)
So this lets you intercept any specific import request you want, and alter the imported module[s] as you wish before you return them -- see the specs here. This is the kind of "hook" you're looking for, right?
There are cleaner ways to do this, but I'll assume that you can't modify the file containing from LegacyDataLoader import load_me_data.
The simplest thing to do is probably to create a new directory called testing_shims, and create LegacyDataLoader.py file in it. In that file, define whatever fake load_me_data you like. When running the unit tests, put testing_shims into your PYTHONPATH environment variable as the first directory. Alternately, you can modify your test runner to insert testing_shims as the first value in sys.path.
This way, your file will be found when importing LegacyDataLoader, and your code will be loaded instead of the real code.
The import statement just grabs stuff from sys.modules if a matching name is found there, so the simplest thing is to make sure you insert your own module into sys.modules under the target name before anything else tries to import the real thing.
# in test code
import sys
import MockDataLoader
sys.modules['LegacyDataLoader'] = MockDataLoader
import module_under_test
There are a handful of variations on the theme, but that basic approach should work fine to do what you describe in the question. A slightly simpler approach would be this, using just a mock function to replace the one in question:
# in test code
import module_under_test
def mock_load_me_data():
# do mock stuff here
module_under_test.load_me_data = mock_load_me_data
That simply replaces the appropriate name right in the module itself, so when you invoke the code under test, presumably do_something() in your question, it calls your mock routine.
Well, if the import fails by raising an exception, you could put it in a try...except loop:
try:
from LegacyDataLoader import load_me_data
except: # put error that occurs here, so as not to mask actual problems
from MockDataLoader import load_me_data
Is that what you're looking for? If it fails, but doesn't raise an exception, you could have it run the unit test with a special command line tag, like --unittest, like this:
import sys
if "--unittest" in sys.argv:
from MockDataLoader import load_me_data
else:
from LegacyDataLoader import load_me_data