Is there a more elegant/systematic/future proof way to invoke all code in if __name__ == '__main__' that is better than copy/past?
I'm converting my bash scripts into python. Two commands we use heavily are python -m SimpleHTTPServer YOUR_PORT and python -m http.server YOUR_PORT.
Translating this in 2.x is fairly clean.
if __name__ == '__main__':
test()
My code to simulate main:
import SimpleHTTPServer
SimpleHTTPServer.test()
Translating this in 3.x is not clean.
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--cgi', action='store_true',
help='Run as CGI Server')
parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
help='Specify alternate bind address '
'[default: all interfaces]')
parser.add_argument('port', action='store',
default=8000, type=int,
nargs='?',
help='Specify alternate port [default: 8000]')
args = parser.parse_args()
if args.cgi:
handler_class = CGIHTTPRequestHandler
else:
handler_class = SimpleHTTPRequestHandler
test(HandlerClass=handler_class, port=args.port, bind=args.bind)
My code to simulate main:
import argparse
import http.server
from http.server import CGIHTTPRequestHandler, SimpleHTTPRequestHandler
parser = argparse.ArgumentParser()
parser.add_argument('--cgi', action='store_true',
help='Run as CGI Server')
parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
help='Specify alternate bind address '
'[default: all interfaces]')
parser.add_argument('port', action='store',
default=8000, type=int,
nargs='?',
help='Specify alternate port [default: 8000]')
args = parser.parse_args()
if args.cgi:
handler_class = CGIHTTPRequestHandler
else:
handler_class = SimpleHTTPRequestHandler
http.server.test(HandlerClass=handler_class, port=args.port, bind=args.bind)
1 Answer 1
This looks horrible, and I am not sure if this is the best way to do this, but the following code appears to start the server without too much difficulty involved:
import importlib
exec(compile(importlib.util.find_spec('http.server').loader.get_source(
'http.server'), 'server.py', 'exec'), dict(__name__='__main__'))
Update 1: The next idea for starting the http.server module probably is not much better, but it utilizes the http.server module directly instead of having to deal with the importlib module.
import http.server
exec(compile(http.server.__loader__.get_source(http.server.__name__),
http.server.__file__, 'exec'), dict(__name__='__main__'))
Of course, that probably just means that it would be better to create a utility function that handles executing modules in whatever context we want to run them in.
import sys
nvl = lambda value, other: other if value is None else value
def exec_module(module, globals=None, locals=None):
frame = sys._getframe(1)
globals = nvl(globals, {})
globals.update(frame.f_globals)
locals = nvl(locals, {})
locals.update(frame.f_locals)
exec(compile(module.__loader__.get_source(module.__name__),
module.__file__, 'exec'), globals, locals)
# this is how you would use the code up above
import http.server
exec_module(http.server, dict(__name__='__main__'))
Update 2: The exec_module function should probably be more like the following, but for some reason that fails to come to my attention, it does not seem to be working as expected:
def exec_module(module, globals=None, locals=None):
frame = sys._getframe(1)
exec_globals = nvl(globals, {})
copy_globals = exec_globals.copy()
exec_globals.update(frame.f_globals)
exec_globals.update(copy_globals)
exec_locals = nvl(locals, {})
copy_locals = exec_locals.copy()
exec_locals.update(frame.f_locals)
exec_locals.update(copy_locals)
exec(compile(module.__loader__.get_source(module.__name__),
module.__file__, 'exec'), exec_globals, exec_locals)
The changes take into account two things. First, references to the passed in globals and locals are retained in case the caller wants to examine their state after the function runs. Second, the globals and locals from the caller cannot overwrite any values already set in the globals and locals passed in.
3 Comments
importlib module altogether. Furthermore, the second design was refined into a utility function that might be useful for any module written in Python.exec_module!Explore related questions
See similar questions with these tags.