Python: How to Call Module from Other Path With __name__ == '__main__' - python

As described in this answer how to import module one can import a module located in another path this way:
import sys
sys.path.append('PathToModule')
import models.user
My question is:
How can I execute this other module (and also pass parameters to it), if this other module is setup this way:
if __name__ == '__main__':
do_something()
and do_something() uses argparse.ArgumentParser to work with the parameters supplied?
I ADDED THE FOLLOWING AFTER THE FIRST QUESTIONS/COMMENTS CAME UP
I am able to pass the parameters via
sys.argv[1:] = [
"--param1", "123",
"--param2", "456",
"--param3", "111"
]
so this topic is already covered.
Why do I want to call another module with parameters?
I would like to be able to do a kind of a small regression test for another project. I would like to get this other project via a git clone and have different versions locally available, that I can debug, too, if needed.
But I do not want to be involved too much in that other project (so that forking does not make sense).
AND SO MY REMAINING QUESTION IS
How can I tweak the contents of __name__ when calling the other module?

There are multiple ways to approach this problem.
If the module you want to import is well-written, it should have separate functions for parsing the command line arguments and for actually doing work. It should look something like this:
def main(arg1, arg2):
pass # do something
def parse_args():
parser = argparse.ArgumentParser()
... # lots of code
return vars(parser.parse_args())
if __name__ == '__main__':
args = parse_args()
main(**args)
In this case, you would simply import the module and then call its main function with the correct arguments:
import yourModule
yourModule.main('foo', 'bar')
This is the optimal solution.
If the module doesn't define such a main function, you can manually set sys.argv and use runpy.run_module to execute the module:
import runpy
import sys
sys.argv[1:] = ['foo', 'bar']
runpy.run_module('yourModule', run_name='__main__', alter_sys=True)
Note that this only executes the module; it doesn't import it. (I.e. the module won't be added to sys.modules and you don't get a module object that you can interact with.)

Related

Passing arguments to python module

I want to pass arguments to the python module to help me decide whether or not to execute some part of the module initialisation code.
Suppose I have a python module named my_module
import sys
flag = sys.argv[1]
if (flag):
# Do Some thing
else:
# Do something else
def module_hello ():
print "hello"
However, I don't want a user script to interfere with the arguments. It has to be purely based on parameters passed while spawning. In the environment where this will be used, I control the spawn of the script. But the script is provided by user of the module
Say a user writes script which imports this module
sys.argv[1] = "Change to something unpleasant"
import my_module
I don't want user to have control over sys.argv. The CLI arguments passed to the script should go to the module unharmed.
Is there a way to achieve this?
If you want to set some global values for a module, you should probably consider encapsulating it in a class, or setting them by calling an intialisation function, so you can pass the parameters like that.
main.py:
import my_module
my_module.init('some values')
mymodule.py:
VALUES = None
function init(values):
global VALUES
VALUES = values
But why not simply declare some variables in the module and just set the value when you load it?
main.py:
import my_module
my_module.values = 'some values'
mymodule.py:
values = None
Or if you just want to read the arguments, it's like any other script:
main.py:
import my_module
mymodule.py:
import sys
values = sys.argv[1]
Of course you can get as fancy as you like, read https://docs.python.org/3/library/argparse.html
So, try to read arguments at your module.
my-module.py
import sys
# Assign my module properties
is_debug = arg[1]

Import a function from a module without module's dependencies

I would like to import a function foo() from module abc.py
However, abc.py contains other functions which rely on modules which are not available for Python (i.e. I cannot import them into python interpreter, because I use ImageJ to run abc.py as Jython)
One solution I found is to put the problematic imports inside the name == "main" check, such as:
# abc.py
def foo():
print("Hello, World!")
def run_main_function():
foo()
...other stuff using IJ...
if __name__ == "__main__":
from ij import IJ
run_main_function()
So when I try to import foo from into another script def.py, e.g.:
# def.py
from abc import foo
def other_func():
foo()
if __name__ == "__main__":
other_func()
This works. But when I put imports in normal fashion, at the top of the script, I get an error: No module named 'ij'. I would like to know if there is a solution to this problem? Specifically, that I put the imports at the top of the script and then within def.py I say to import just the function, without dependencies of abc.py?
I would like to know if there is a solution to this problem? Specifically, that I put the imports at the top of the script and then within def.py I say to import just the function, without dependencies of abc.py?
As far I know, it's the way that python works. You should put that import in the function that uses it if won't be aviable always.
def run_main_function():
from ij import IJ
foo()
Also, don't use abc as a module name, it's a standard library module: Abstract Base Class 2.7, Abstract Base Class 3.6
Edit: don't use trailing .py when importing as Kind Stranger stated.

What's the correct way to change a value that is imported by other modules?

I have my entry point script take a CLI option argument using argsparser and that looks something like this:
import some_module
if __name__== "__main__":
parser = argparse.ArgumentParser(description='Nemo Node.')
parser.add_argument('-t', '--test', dest='testing', action="store_true")
# handle CLI params and options
# .....
some_module.run()
I want the -t option to change a parameter TESTING in a config script settings.py, since my some_module uses things like :
from settings import TESTING
if TESTING:
# do some testing stuff
Because of this TESTING is already imported into some_module by the time I handle the args, changes to settings.py can no longer have any effect on some_module.
My answer is what's the correct practice in dealing with this:
import some_module inside the __main__ after the args have been parsed?
replace TESTING by settings.TESTING everywhere?
something else?
Since some_module.TESTING is set immediately after import, that's the value you want to update, not settings.TESTING.
import some_module
if __name__== "__main__":
parser = argparse.ArgumentParser(description='Nemo Node.')
parser.add_argument('-t', '--test', dest='testing', action="store_true")
# handle CLI params and options
# .....
some_module.TESTING = args.t
some_module.run()
If the code using TESTING runs at import time as well, then you only option is to avoid importing some_module before you can change settings.TESTING. It's a bit ugly, though. Module-level globals are best thought of as read-only constants, or private implementation details of the module.
It also sounds like you are treating settings.py as a configuration file; you may be better off using something like ConfigParser to read a configuration from settings.conf, then passing that configuration (possibly modified based on command-line arguments) to some_module.run().

Command line options and importing functions

I'm importing a function from a python module (using from python_file import function), then making use of that function in my system.
My problem right now is that I want the user to specify the python file and function via the commandline using the argparse module. But I am not sure how to do this. Please can someone explain how to do this?
The use of argparse is straightforward.
The trick is to import a module and one of its functions, both being provided as strings.
import argparse
parser = argparse.ArgumentParser(description='Import stuff')
parser.add_argument('--module')
parser.add_argument('--function')
args = parser.parse_args()
module = __import__(args.module)
function = getattr(module, args.function)

How to import a module as __main__?

I have a module that has the usual
if __name__ == '__main__':
do stuff...
idiom.
I'd like to import that from another module, and fool it into running that code. Is there any way of doing this?
I should mention, for reasons I won't go into here, I'm not in a position to change the code in the imported module. I need to somehow modify the import procedure so that it's name is main when imported, perhaps using ihooks or similar.
As pointed out in the other answers, this is a bad idea, and you should solve the issue some other way.
Regardless, the way Python does it is like this:
import runpy
result = runpy._run_module_as_main("your.module.name"))
There is, execute the script instead of importing it. But I consider this an extremely hackish solution.
However the ideal pattern would be:
def do_stuff():
... stuff happens ...
if __name__ == '__main__':
do_stuff()
that way you can do:
from mymodule import do_stuff
do_stuff()
EDIT: Answer after clarification on not being able to edit the module code.
I would never recommend this in any production code, this is a "use at own risk" solution.
import mymodule
with open(os.path.splitext(mymodule.__file__)[0] + ".py") as fh:
exec fh.read()
Put that code in a function, and call it from the module you are importing it into as well.
def stuff():
...
if __name__ == '__main__':
stuff()
And then in the module you are importing it into:
import module
module.stuff()
The correct answer has been already given however it is confined in a comments (see How to import a module as __main__?
and How to import a module as __main__?).
The same with proper formatting:
import runpy
runpy.run_module("your.module.name", {}, "__main__")
or
import runpy
runpy.run_path("path/to/my/file.py", {}, "__main__")
Code in a main stanza usually never makes sense to run directly. If you want to run it then use subprocess to run it in another Python interpreter.
Here is an example of a main module in Python:
#! /usr/bin/env python
import sys
import os
def main(arg1, arg2, arg3):
print(arg1, arg2, arg3)
if __name__ == "__main__":
main(*sys.argv)
But you can also include
def main():
#The module contains Python code specific to the library module,
#like tests, and follow the module with this:
if __name__ == "__main__":
main(*sys.argv)
in any module you would like to run as main.
For example, if you have a library module, you can always use this construct to execute something specific like tests.
Put it in a function:
def _main():
do stuff
if __name__ == '__main__':
main()

Categories