How to execute if __name__ != "__main__"? - python

I have a code which I cannot alter named temp.py which contains
if __name__ == "__main__":
*some pieces of code not a function call*
I want to import temp into an other file which I can edit but importing doesnot run the above part. I know I can use subprocess to run temp.py but that is not what I want. I want to import the module entirely but cannot alter the code.
I have heard of a module called imp but is depreceated now.
EDIT:
I am aware that code under the if statement is not meant to be excecuted when imported, but lets just assume temp.py is written in a really worse way and I cannot alter it.

You are trying to go against the import system. Pretty much any thing you end up doing to achieve this will be a hack.
Consider we have a file:
(py39) Juans-MacBook-Pro:~ juan$ cat subvert.py
def foo(x):
print("Hello, ", x)
if __name__ == "__main__":
foo("Goodbye")
Note, if I open up a REPL, I'm in __main__, so at your top-level script, you could just do:
(py39) Juans-MacBook-Pro:~ juan$ python
Python 3.9.5 (default, May 18 2021, 12:31:01)
[Clang 10.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> __name__
'__main__'
>>> exec(open("subvert.py").read())
Hello, Goodbye
Of course, just execing the source code directly in the same namespace is probably not what you want...
Note, we can pass our own namespace:
>>> namespace = {"__name__": "__main__"}
>>> exec(open("subvert.py").read(), namespace)
Hello, Goodbye
So our module's namespace won't be clobbered.
Now, if you want an actual module after it, you could hack together something like:
>>> import types
>>> module = types.ModuleType("__main__")
>>> module
<module '__main__'>
>>> exec(open("subvert.py").read(), module.__dict__)
Hello, Goodbye
>>> module.foo('bar')
Hello, bar
I wouldn't expect any of this to work well. It won't have all the attributes of a fully loaded module, look at importlib if you find you need to flesh it out more.

something like this is really bad practice but will work:
file1.py has the "main" part:
def foo1():
print("foo1")
if __name__ == '__main__':
print("main function started")
foo1()
print("main function finished")
file2.py is an intermediary file:
from file1 import *
with open('file1.py') as f:
lines = f.readlines()
a = "if __name__ == '__main__':\n"
new_main = ["def main():\n"] + lines[lines.index(a)+1:]
exec("".join(new_main))
file3.py used to import file2 as if it is the file1 module:
import file2
file2.main()
file2.foo1()
output when running file3.py:
main function started
foo1
main function finished
foo1

Python programmers use following mechanism for avoiding importing a main script on another scripts.
if __name__ == "__main__":
# TODO
Read its philosophia here.
When some code used if __name__ == "__main__": means to you shouldn't import its code and this work isn't true.
But you can use this mechanism :
def main():
# TODO
if __name__ == "__main__":
main()
In this case everyone can import your main script and use main function.

Related

Python reusing of module without new load

I have two modules, and I need to import one from another. In first module, I need to only declare variable and load (if its not loaded). In second, just read this variable and use that (if not loaded, in another module load it)
First module:
run_engine.py:
#run_engine.py
import matlab
eng = None
if(eng == None):
eng = matlab.engine.start_matlab()
start.py:
#!/usr/bin/python
import matlab.engine
import os
import run_engine
def app():
currentDir = os.path.dirname(os.path.abspath(__file__))
matlabInstance = run_engine.eng
matlabInstance.addpath(currentDir)
matlabInstance.sim('thermo_simple')
if __name__ == '__main__':
app()
matlab.engine.start_matlab takes about 30seconds to start, and i´m using start.py repeatly, so I need only one instance of eng, but correct loaded. How can I do that?
Run app() repeatedly in an interactive python shell:
$ python -i start.py
>>> app()
#run_engine.py
import matlab
import time
eng = None
while not eng:
eng = matlab.engine.start_matlab()
time.sleep(1)
BTW - if you want to use something like "if X == None" - this should be put as
if X is None
This is a preferable Python idiom ;)

run import.py with sys parameters

I am debugging code, which has this line:
run('python /home/some_user/some_repo/pyflights/usertools/import.py /home/some_user/some_repo/pyflights/config/index_import.conf flights.map --import')
run - is some analog of os.system
So, I want to run this code without using run function. I need to import my import.py file and run it with sys.args. But how can I do this?
from some_repo.pyflights.usertools import import
There is no way to import import because import is a keyword. Moreover, importing a python file is different from running a script because most scripts have a section
if __name__ == '__main__':
....
When the program is running as a script, the variable __name__ has value __main__.
If you are ready to call a subprocess, you can use
`subprocess.call(...)`
Edit: actually, you can import import like so
from importlib import import_module
mod = import_module('import')
however it won't have the same effect as calling the script. Notice that the script probably uses sys.argv, and this must be addressed too.
Edit: here is an ersatz that you can try if you really don't want a subprocess. I don't guarantee it will work
import shlex
import sys
import types
def run(args):
"""Runs a python program with arguments within the current process.
Arguments:
#args: a sequence of arguments, the first one must be the file path to the python program
This is not guaranteed to work because the current process and the
executed script could modify the python running environment in incompatible ways.
"""
old_main, sys.modules['__main__'] = sys.modules['__main__'], types.ModuleType('__main__')
old_argv, sys.argv = sys.argv, list(args)
try:
with open(sys.argv[0]) as infile:
source = infile.read()
exec(source, sys.modules['__main__'].__dict__)
except SystemExit as exc:
if exc.code:
raise RuntimeError('run() failed with code %d' % exc.code)
finally:
sys.argv, sys.modules['__main__'] = old_argv, old_main
command = '/home/some_user/some_repo/pyflights/usertools/import.py /home/some_user/some_repo/pyflights/config/index_import.conf flights.map --import'
run(shlex.split(command))

Python runtime determine which module to load

I have my Python unittest script like below. It takes an argument '-a' to determine whether the testcases should load the base module from foo_PC_A.py or foo_PC_B.py. I use shutil.move() to rename either .py file to foo.py, so all the testcase modules (e.g. tm1.py, tm2.py) can simply import foo. Though this looks like a workaround and not Pythonic. Is there any better way to do this? Or a better design to fundamentally resolve this issue.
(run_all_unittest.py)
if sys.argv[1] = '-a':
shutil.move('foo_PC_A.py', 'foo.py')
else:
shutil.move('foo_PC_B.py', 'foo.py')
test_module_list = ['tm1', 'tm2', ...]
for test_module_name in test_module_list:
test_module = __import__(test_module_name)
test_suites.append(unittest.TestLoader().loadTestsFromModule(test_module))
alltests = unittest.TestSuite(test_suites)
unittest.TextTestRunner().run(alltests)
if sys.argv[1] = '-a':
shutil.move('foo.py', 'foo_PC_A.py')
else:
shutil.move('foo.py', 'foo_PC_B.py')
(tm1.py)
from foo import MyTestCase
...
(foo_PC_A.py)
import <some module only available on PC A>
class MyTestCase(unittest.TestCase):
...
(foo_PC_B.py)
# since the PC A modules are not available on PC B,
# just call the pre-built executable via subprocess
import subprocess
class MyTestCase(unittest.TestCase):
...
def test_run(self):
subprocess.call(...)
You can fool Python into thinking the module has already been loaded. Just import the module dynamically and use sys.modules:
import sys
import importlib
if sys.argv[1] = '-a':
sys.modules['foo'] = importlib.import_module('foo_PC_A')
else:
sys.modules['foo'] = importlib.import_module('foo_PC_A')
When any module runs import foo or from foo import ..., Python will use that path.
Note that if foo is moved to a package, the full Python path must be specified, as in:
sys.modules['path.to.foo'] = ...

Making Python run a few lines before my script

I need to run a script foo.py, but I need to also insert some debugging lines to run before the code in foo.py. Currently I just put those lines in foo.py and I'm careful not to commit that to Git, but I don't like this solution.
What I want is a separate file bar.py that I don't commit to Git. Then I want to run:
python /somewhere/bar.py /somewhere_else/foo.py
What I want this to do is first run some lines of code in bar.py, and then run foo.py as __main__. It should be in the same process that the bar.py lines ran in, otherwise the debugging lines won't help.
Is there a way to make bar.py do this?
Someone suggested this:
import imp
import sys
# Debugging code here
fp, pathname, description = imp.find_module(sys.argv[1])
imp.load_module('__main__', fp, pathname, description)
The problem with that is that because it uses import machinery, I need to be on the same folder as foo.py to run that. I don't want that. I want to simply put in the full path to foo.py.
Also: The solution needs to work with .pyc files as well.
Python has a mechanism for running code at startup; the site module.
"This module is automatically imported during initialization."
The site module will attempt to import a module named sitecustomize before __main__ is imported.
It will also attempt to import a module named usercustomize if your environment instructs it to.
For example, you could put a sitecustomize.py file in your site-packages folder that contains this:
import imp
import os
if 'MY_STARTUP_FILE' in os.environ:
try:
file_path = os.environ['MY_STARTUP_FILE']
folder, file_name = os.path.split(file_path)
module_name, _ = os.path.splitext(file_name)
fp, pathname, description = imp.find_module(module_name, [folder])
except Exception as e:
# Broad exception handling since sitecustomize exceptions are ignored
print "There was a problem finding startup file", file_path
print repr(e)
exit()
try:
imp.load_module(module_name, fp, pathname, description)
except Exception as e:
print "There was a problem loading startup file: ", file_path
print repr(e)
exit()
finally:
# "the caller is responsible for closing the file argument" from imp docs
if fp:
fp.close()
Then you could run your script like this:
MY_STARTUP_FILE=/somewhere/bar.py python /somewhere_else/foo.py
You could run any script before foo.py without needing to add code to reimport __main__.
Run export MY_STARTUP_FILE=/somewhere/bar.py and not need to reference it every time
You can use execfile() if the file is .py and uncompyle2 if the file is .pyc.
Let's say you have your file structure like:
test|-- foo.py
|-- bar
|--bar.py
foo.py
import sys
a = 1
print ('debugging...')
# run the other file
if sys.argv[1].endswith('.py'): # if .py run right away
execfile(sys.argv[1], globals(), locals())
elif sys.argv[1].endswith('.pyc'): # if .pyc, first uncompyle, then run
import uncompyle2
from StringIO import StringIO
f = StringIO()
uncompyle2.uncompyle_file(sys.argv[1], f)
f.seek(0)
exec(f.read(), globals(), locals())
bar.py
print a
print 'real job'
And in test/, if you do:
$ python foo.py bar/bar.py
$ python foo.py bar/bar.pyc
Both, outputs the same:
debugging...
1
real job
Please also see this answer.
You probably have something along the lines of:
if __name__ == '__main__':
# some code
Instead, write your code in a function main() in foo and then do:
if __name__ == '__main__':
main()
Then, in bar, you can import foo and call foo.main().
Additionaly, if you need to change the working directory, you can use the os.chdir(path) method, e.g. os.chdir('path/of/bar') .
bar.py would have to behave like the Python interpreter itself and run foo.py (or foo.pyc, as you asked for that) as if it was the main script. This is surprisingly hard. My 90% solution to do that looks like so:
def run_script_as_main(cmdline):
# It is crucial to import locally what we need as we
# later remove everything from __main__
import sys, imp, traceback, os
# Patch sys.argv
sys.argv = cmdline
# Clear the __main__ namespace and set it up to run
# the secondary program
maindict = sys.modules["__main__"].__dict__
builtins = maindict['__builtins__']
maindict.clear()
maindict['__file__'] = cmdline[0]
maindict['__builtins__'] = builtins
maindict['__name__'] = "__main__"
maindict['__doc__'] = None
# Python prepends a script's location to sys.path
sys.path[0] = os.path.dirname(os.path.abspath(cmdline[0]))
# Treat everything as a Python source file, except it
# ends in '.pyc'
loader = imp.load_source
if maindict["__file__"].endswith(".pyc"):
loader = imp.load_compiled
with open(cmdline[0], 'rb') as f:
try:
loader('__main__', maindict["__file__"], f)
except Exception:
# In case of an exception, remove this script from the
# stack trace; if you don't care seeing some bar.py in
# traceback, you can leave that out.
ex_type, ex_value, ex_traceback = sys.exc_info()
tb = traceback.extract_tb(ex_traceback, None)[1:]
sys.stderr.write("Traceback (most recent call last):\n")
for line in traceback.format_list(tb):
sys.stderr.write(line)
for line in traceback.format_exception_only(ex_type, ex_value):
sys.stderr.write(line)
This mimics the default behavior of the Python interpreter
relatively closely. It sets system globals __name__, __file__,
sys.argv, inserts the script location into sys.path, clears the global namespace etc.
You would copy that code to bar.py or import it from somewhere and use it like so:
if __name__ == "__main__":
# You debugging setup goes here
...
# Run the Python program given as argv[1]
run_script_as_main(sys.argv[1:])
Then, given this:
$ find so-foo-bar
so-foo-bar
so-foo-bar/debugaid
so-foo-bar/debugaid/bar.py
so-foo-bar/foo.py
and foo.py looking like this:
import sys
if __name__ == "__main__":
print "My name is", __name__
print "My file is", __file__
print "My command line is", sys.argv
print "First 2 path items", sys.path[:2]
print "Globals", globals().keys()
raise Exception("foo")
... running foo.py via bar.py gives you that:
$ python so-foo-bar/debugaid/bar.py so-foo-bar/foo.py
My name is __main__
My file is so-foo-bar/foo.py
My command line is ['so-foo-bar/foo.py']
First 2 path items ['~/so-foo-bar', '/usr/local/...']
Globals ['__builtins__', '__name__', '__file__', 'sys', '__package__', '__doc__']
Traceback (most recent call last):
File "so-foo-bar/foo.py", line 9, in
raise Exception("foo")
Exception: foo
While I'd guess that run_script_as_main is lacking in some interesting details, it's pretty close to the way Python would run foo.py.
The foo.py need not be on the same folder as the main folder. Use
export PYTHONPATH=$HOME/dirWithFoo/:$PYTHONPATH
To add the path that foo resides to Python path. Now you can
from foo import myfunc
myfunc()
And make myfunc do whatever u want
Have you tried this?
bar.py
imp.load_source('__main__', '../../foo.py')
for me it runs whatever is in foo.py (before and inside main)
After, maybe what you do in bar.py might be more tricky that my basic test.
The good thing with load_source is that it imports the .pyc if it already exists or initiates a "compile" of the .py and then imports the .pyc.
This solution is what ended up working well for me:
#!/usr/bin/env python
import imp
import sys
import os.path
file_path = sys.argv.pop(1)
assert os.path.exists(file_path)
folder, file_name = os.path.split(file_path)
###############################################################################
# #
# Debugging cruft here
# #
###############################################################################
module_name, _ = os.path.splitext(file_name)
sys.path.append(folder)
imp.load_module('__main__', *imp.find_module(module_name))

Dynamically importing Python module

I have a trusted remote server that stores many custom Python modules. I can fetch them via HTTP (e.g. using urllib2.urlopen) as text/plain, but I cannot save the fetched module code to the local hard disk. How can I import the code as a fully operable Python module, including its global variables and imports?
I suppose I have to use some combination of exec and imp module's functions, but I've been unable to make it work yet.
It looks like this should do the trick: importing a dynamically generated module
>>> import imp
>>> foo = imp.new_module("foo")
>>> foo_code = """
... class Foo:
... pass
... """
>>> exec foo_code in foo.__dict__
>>> foo.Foo.__module__
'foo'
>>>
Also, as suggested in the ActiveState article, you might want to add your new module to sys.modules:
>>> import sys
>>> sys.modules["foo"] = foo
>>> from foo import Foo
<class 'Foo' …>
>>>
Here's something I bookmarked a while back that covers something similar:
Customizing the Python Import System
It's a bit beyond what you want, but the basic idea is there.
Python3 version
(attempted to edit other answer but the edit que is full)
import imp
my_dynamic_module = imp.new_module("my_dynamic_module")
exec("""
class Foo:
pass
""", my_dynamic_module.__dict__)
Foo = my_dynamic_module.Foo
foo_object = Foo()
# register it on sys
import sys
sys.modules[my_dynamic_module.__name__] = my_dynamic_module
I recently encountered trying to do this while trying to write unit tests for source code examples I put into a project's readme (I wanted to avoid just linking to small files or duplicating the text in a way that could get out of sync).
I came up with the following
import sys
import types
from importlib import import_module
def compile_and_install_module(module_name: str, source_code: str) -> types.ModuleType:
"""Compile source code and install it as a module.
End result is that `import <module_name>` and `from <module_name> import ...` should work.
"""
module = types.ModuleType(module_name, "Module created from source code")
# Execute source in context of empty/fake module
exec(source_code, module.__dict__)
# Insert fake module into sys.modules. It's now a real module
sys.modules[module_name] = module
# Imports should work now
return import_module(module_name)
And a quick example of how you can use it
$ cat hello.py
def foo():
print("Hello world")
bar = 42
$ python
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from compile import compile_and_install_module
>>> compile_and_install_module("hello", open("hello.py").read())
<module 'hello'>
>>> import hello
>>> hello.foo()
Hello world
>>> from hello import bar
>>> bar
42
You can remove the return value and import_lib import if you

Categories

Resources