How to import and get variables from another python file? - python

The main file is aaa.py, code as below:
import os,time
from get_newest_folder import *
def main_test():
print(newest_folder)
print(path)
if __name__ == '__main__':
get_n_folder()
main_test()
The another file is get_newest_folder.py, code as below:
import os
def get_n_folder():
path = 'D:\\Test'
os.chdir(path)
files = sorted(os.listdir(os.getcwd()), key=os.path.getmtime)
newest_folder = files[-1]
return (path, newest_folder)
if __name__ == '__main__':
get_n_folder()
I'm trying to use variable "path" and "newest_folder" from get_newest_folder.py in aaa.py, so I import "get_newest_folder". But I can't get(print) the 2 variables in aaa.py. It displays the error message as below when I run aaa.py:
NameError: name 'newest_folder' is not defined
Does anyone can correct my code or give me some suggestion?
Many thanks.

modify your aaa.py
import os,time
from get_newest_folder import *
def main_test():
path,newest_folder = get_n_folder()
print(newest_folder)
print(path)
if __name__ == '__main__':
main_test()

Inorder to access newest_folder and path, you have to declare them as global in get_newest_folder.py and import these variables in aaa.py. They are local variables currently.
But this is not an ideal way to get about things. May be you need something like this
import os,time
from get_newest_folder import *
def main_test():
_path, _newest_folder = get_n_folder()
print(_path)
print(_newest_folder)
if __name__ == '__main__':
main_test()

First of all, to answer your question, you need to modify your module:
import os
newest_folder = '' # <<<<
path = '' # <<<<
def get_n_folder():
global newest_folder # <<<<
global path # <<<<
....
# The rest is unchanged
The variables are attributes of the module now, previously they were only attributes of the function.
So that got rid of the error messages, but it just prints two blank lines!!! Why? Because newest_folder and path have not been setup, you don't call the function to populate the values!
That's one problem, the other is from get_newest_folder import *. Don't do that - it pollutes your own namespace. It gives the opportunity for a module to overwrite your own variables (namespace collision). Fine, you know this module, but will you always? Do you know very variable in every module that you import?
So:
import get_newest_folder
def main_test():
print(get_newest_folder.newest_folder)
print(get_newest_folder.path)
if __name__ == '__main__':
get_newest_folder.get_n_folder()
main_test()
But, as others have said, it is better not to share variables, if you do it means that the module implementation cannot change - it breaks encapsulation. There are a few occasions where it might be justified, but not here.
Instead, using your original get_newest_folder.py:
import os,time
import get_newest_folder
def main_test():
print(newest_folder)
print(path)
if __name__ == '__main__':
print(dir())
newest_folder, path = get_newest_folder.get_n_folder()
main_test()

Related

Calling Python class method which is in another file

From a main fill (or another class file), I would instantiate a class and call a method.
Knowing many languages and learning Python: this must be very easy, but could not find a good example. So, if you show me another post or answer, please feel free.
My config is:
All files are in the same folder (main.py and MathsOperations.py)
Python 3.7
IDE: Pycharm 2020.2 (just upgraded)
Starting (Running) the main.py in the folder.
The Class file:
class MathsOperations:
def __init__ (self, x, y):
self.a = x
self.b = y
def testAddition (self):
return (self.a + self.b)
def testMultiplication (self):
return (self.a * self.b)
Try-1: The main file:
from . import MathsOperations
if __name__ == '__main__':
xyz = MathsOperations(2, 3)
print( xyz.testAddition())
I get the error: ImportError: cannot import name 'MathsOperations' from '__main__'
Try-2: import in
import MathsOperations;
if __name__ == '__main__':
xyz = MathsOperations(2, 3)
print( xyz.testAddition())
Gives error: No module named MathOperations
Try-3: using dot notation with file
from .MathsOperations import MathsOperations
if __name__ == '__main__':
xyz = MathsOperations(2, 3)
print( xyz.testAddition())
Gives error: ModuleNotFoundError: No module named '__main__.MathsOperations'; '__main__' is not a package
SOLUTION: using from and import of python file
This works thanks to #Surya R and #Chad S.
from MathsOperations import MathsOperations
if __name__ == '__main__':
xyz = MathsOperations(2, 3)
print( xyz.testAddition())
This gives a WARNING in PyCharm but that one can be ignored!
to solve this you have to understand the dot in the line 1 which is important
The dot in the module name is used for relative module import
You can use more than one dot, referring not to the current package but its parent(s). This should only be used within packages, in the main module one should always use absolute module names.
let me know your folder's structure so that we could find the right import statement
incase to import a file
import file
you can use
from yourfilenmae import yourclassname
if you wanna use dot things refer this one dot syntax to import files

What are the rules for __all__ and relative imports?

Inside a package I am using a few checks inside each module directory's __init__.py to see whether the environment is sane and then use from . import mod1 for nested modules (mod1 here). Each of the module directories in turn contains an __init__.py (obviously) which will import the next lower level (if any).
Some of the __init__.py contain __all__ arrays to only expose certain names upon import.
Let's say I have the following "main" script (spam.py):
import os, sys
if (sys.version_info[0] != 2) or (sys.version_info < (2,7)):
sys.exit("...")
else:
import pkgname
def main():
pass
if __name__ == '__main__':
main()
and the following pkgname/__init__.py:
import sys
if __name__ == '__main__':
sys.exit(0)
else:
from . import db
from os.path import dirname, realpath
sys.modules[__name__].__all__ = ['inipath']
sys.modules[__name__].__dict__['inipath'] = dirname(realpath(__file__)) + '.ini'
and the following pkgname/db/__init__.py:
import sys
if __name__ == '__main__':
sys.exit(0)
else:
import sqlite3
# ...
foobar = { 'spam' : 'egg' }
__all__ = ["foobar"]
will the symbol pkgname.db.foobar be visible despite the __all__ array in pkgname/__init__.py? I.e. does the __all__ array only affect the immediate module or also any lower level?
Which rules govern Python's behavior here? I tried some searches but came up empty-handed. Possibly I used the wrong searches (__all__, python, relative import)?
__all__ has no effect in the case you're describing. It only affects what happens when you do from package import *. See the documentation as well as previous questions (which I found by googling python __all__).

How to append functions to current module from unrelated script in Python

I have two python scripts in different locations. I try to import the functions of the second one in such a way that they are integrated with the globals of the first one. That works fine. However when I call functions in the first script from the second one they cant be found.
foo.py
def run():
glob = {}
loc = {}
execfile("/path/to/bar.py", glob, loc)
currmodule = globals()
currmodule["func_in_bar"] = glob["func_in_bar"]
func_in_bar()
def func_in_foo_A():
print("fooA")
def func_in_foo_B():
print("fooB")
if __name__ == "__main__":
run()
bar.py
def func_in_bar():
func_in_foo_A()
func_in_foo_B()
When foo is run it fails with: NameError: global name 'func_in_foo_A' is not defined
In bar.py you need to add an import foo and then reference foo.func_in_foo(), etc.
Alternatively, use the form: from foo import func_in_foo

How to test or mock "if __name__ == '__main__'" contents

Say I have a module with the following:
def main():
pass
if __name__ == "__main__":
main()
I want to write a unit test for the bottom half (I'd like to achieve 100% coverage). I discovered the runpy builtin module that performs the import/__name__-setting mechanism, but I can't figure out how to mock or otherwise check that the main() function is called.
This is what I've tried so far:
import runpy
import mock
#mock.patch('foobar.main')
def test_main(self, main):
runpy.run_module('foobar', run_name='__main__')
main.assert_called_once_with()
I will choose another alternative which is to exclude the if __name__ == '__main__' from the coverage report , of course you can do that only if you already have a test case for your main() function in your tests.
As for why I choose to exclude rather than writing a new test case for the whole script is because if as I stated you already have a test case for your main() function the fact that you add an other test case for the script (just for having a 100 % coverage) will be just a duplicated one.
For how to exclude the if __name__ == '__main__' you can write a coverage configuration file and add in the section report:
[report]
exclude_lines =
if __name__ == .__main__.:
More info about the coverage configuration file can be found here.
Hope this can help.
You can do this using the imp module rather than the import statement. The problem with the import statement is that the test for '__main__' runs as part of the import statement before you get a chance to assign to runpy.__name__.
For example, you could use imp.load_source() like so:
import imp
runpy = imp.load_source('__main__', '/path/to/runpy.py')
The first parameter is assigned to __name__ of the imported module.
Whoa, I'm a little late to the party, but I recently ran into this issue and I think I came up with a better solution, so here it is...
I was working on a module that contained a dozen or so scripts all ending with this exact copypasta:
if __name__ == '__main__':
if '--help' in sys.argv or '-h' in sys.argv:
print(__doc__)
else:
sys.exit(main())
Not horrible, sure, but not testable either. My solution was to write a new function in one of my modules:
def run_script(name, doc, main):
"""Act like a script if we were invoked like a script."""
if name == '__main__':
if '--help' in sys.argv or '-h' in sys.argv:
sys.stdout.write(doc)
else:
sys.exit(main())
and then place this gem at the end of each script file:
run_script(__name__, __doc__, main)
Technically, this function will be run unconditionally whether your script was imported as a module or ran as a script. This is ok however because the function doesn't actually do anything unless the script is being ran as a script. So code coverage sees the function runs and says "yes, 100% code coverage!" Meanwhile, I wrote three tests to cover the function itself:
#patch('mymodule.utils.sys')
def test_run_script_as_import(self, sysMock):
"""The run_script() func is a NOP when name != __main__."""
mainMock = Mock()
sysMock.argv = []
run_script('some_module', 'docdocdoc', mainMock)
self.assertEqual(mainMock.mock_calls, [])
self.assertEqual(sysMock.exit.mock_calls, [])
self.assertEqual(sysMock.stdout.write.mock_calls, [])
#patch('mymodule.utils.sys')
def test_run_script_as_script(self, sysMock):
"""Invoke main() when run as a script."""
mainMock = Mock()
sysMock.argv = []
run_script('__main__', 'docdocdoc', mainMock)
mainMock.assert_called_once_with()
sysMock.exit.assert_called_once_with(mainMock())
self.assertEqual(sysMock.stdout.write.mock_calls, [])
#patch('mymodule.utils.sys')
def test_run_script_with_help(self, sysMock):
"""Print help when the user asks for help."""
mainMock = Mock()
for h in ('-h', '--help'):
sysMock.argv = [h]
run_script('__main__', h*5, mainMock)
self.assertEqual(mainMock.mock_calls, [])
self.assertEqual(sysMock.exit.mock_calls, [])
sysMock.stdout.write.assert_called_with(h*5)
Blam! Now you can write a testable main(), invoke it as a script, have 100% test coverage, and not need to ignore any code in your coverage report.
Python 3 solution:
import os
from importlib.machinery import SourceFileLoader
from importlib.util import spec_from_loader, module_from_spec
from importlib import reload
from unittest import TestCase
from unittest.mock import MagicMock, patch
class TestIfNameEqMain(TestCase):
def test_name_eq_main(self):
loader = SourceFileLoader('__main__',
os.path.join(os.path.dirname(os.path.dirname(__file__)),
'__main__.py'))
with self.assertRaises(SystemExit) as e:
loader.exec_module(module_from_spec(spec_from_loader(loader.name, loader)))
Using the alternative solution of defining your own little function:
# module.py
def main():
if __name__ == '__main__':
return 'sweet'
return 'child of mine'
You can test with:
# Override the `__name__` value in your module to '__main__'
with patch('module_name.__name__', '__main__'):
import module_name
self.assertEqual(module_name.main(), 'sweet')
with patch('module_name.__name__', 'anything else'):
reload(module_name)
del module_name
import module_name
self.assertEqual(module_name.main(), 'child of mine')
I did not want to exclude the lines in question, so based on this explanation of a solution, I implemented a simplified version of the alternate answer given here...
I wrapped if __name__ == "__main__": in a function to make it easily testable, and then called that function to retain logic:
# myapp.module.py
def main():
pass
def init():
if __name__ == "__main__":
main()
init()
I mocked the __name__ using unittest.mock to get at the lines in question:
from unittest.mock import patch, MagicMock
from myapp import module
def test_name_equals_main():
# Arrange
with patch.object(module, "main", MagicMock()) as mock_main:
with patch.object(module, "__name__", "__main__"):
# Act
module.init()
# Assert
mock_main.assert_called_once()
If you are sending arguments into the mocked function, like so,
if __name__ == "__main__":
main(main_args)
then you can use assert_called_once_with() for an even better test:
expected_args = ["expected_arg_1", "expected_arg_2"]
mock_main.assert_called_once_with(expected_args)
If desired, you can also add a return_value to the MagicMock() like so:
with patch.object(module, "main", MagicMock(return_value='foo')) as mock_main:
One approach is to run the modules as scripts (e.g. os.system(...)) and compare their stdout and stderr output to expected values.
I found this solution helpful. Works well if you use a function to keep all your script code.
The code will be handled as one code line. It doesn't matter if the entire line was executed for coverage counter (though this is not what you would actually actually expect by 100% coverage)
The trick is also accepted pylint. ;-)
if __name__ == '__main__': \
main()
If it's just to get the 100% and there is nothing "real" to test there, it is easier to ignore that line.
If you are using the regular coverage lib, you can just add a simple comment, and the line will be ignored in the coverage report.
if __name__ == '__main__':
main() # pragma: no cover
https://coverage.readthedocs.io/en/coverage-4.3.3/excluding.html
Another comment by # Taylor Edmiston also mentions it
My solution is to use imp.load_source() and force an exception to be raised early in main() by not providing a required CLI argument, providing a malformed argument, setting paths in such a way that a required file is not found, etc.
import imp
import os
import sys
def mainCond(testObj, srcFilePath, expectedExcType=SystemExit, cliArgsStr=''):
sys.argv = [os.path.basename(srcFilePath)] + (
[] if len(cliArgsStr) == 0 else cliArgsStr.split(' '))
testObj.assertRaises(expectedExcType, imp.load_source, '__main__', srcFilePath)
Then in your test class you can use this function like this:
def testMain(self):
mainCond(self, 'path/to/main.py', cliArgsStr='-d FailingArg')
To import your "main" code in pytest in order to test it you can import main module like other functions thanks to native importlib package :
def test_main():
import importlib
loader = importlib.machinery.SourceFileLoader("__main__", "src/glue_jobs/move_data_with_resource_partitionning.py")
runpy_main = loader.load_module()
assert runpy_main()

Problems trying to access to a global var

I know how to use the global variables when they are defined in a class, but I have a global variable in a main.
If I want to use it inside a class, which would be the import to access it?
My main is something like this
Main.py:
from EvolutionaryAlgorithm import EvolutionaryAlgorithm
initialTimeMain = 0
if __name__ == '__main__':
evolutionaryAlgorithm= EvolutionaryAlgorithm()
.
.
and my EvolutionaryAlgorithm class has a method which uses the initialTimeMain variable.
the problem is when I add this import in the EvolutionaryAlgorithm:
EvolutionaryAlgorithm.py
import Main
because when I run the script, an error appears
from EvolutionaryAlgorithm import
EvolutionaryAlgorithm ImportError:
cannot import name
EvolutionaryAlgorithm
the import isn't recognized anymore
You have a case of circular imports, short-term solution is to move import statement inside the if clause:
initialTimeMain = 0
if __name__ == '__main__':
from EvolutionaryAlgorithm import EvolutionaryAlgorithm
evolutionaryAlgorithm= EvolutionaryAlgorithm()
A better, long-term solution would be to refactor your code so that you don't have circular imports or initialTimeMain is defined in the EvolutionaryAlgorithm.py, which of course would be available in Main.py with your existing import strategy.
Old answer:
a.py:
globalvar = 1
print(globalvar) # prints 1
if __name__ == '__main__':
print(globalvar) # prints 1
b.py:
import a
print(a.globalvar) # prints 1

Categories