0

I would like to create a python module that would be called with python -m mymodule somefile.py some_arg some_arg.

The idea is that I would be able to set up an alias alias="python -m mymodule" and call files normally with python somefile.py some_arg some_arg.

In the file mymodule/__main__.py, what is the best way to load somefile.py and pass it the argument list?

  • I am looking for a generic solution, that would be python2 and 3 compatible.
  • It would be great to be as little intrusive as possible. If somefile.py would raise an exception, mymodule should barely be seen in the traceback.
  • What the module does is not interesting here in detail, but it sets up some python things (traceback hooks etc.), so somefile.py should be ran pythonicly in the same process. os.system or subprocess.Popen do not fit.
0

2 Answers 2

1

Ok I found something good for python 3.5, and satisfying enough for python 2.7.

mymodule/main.py

import sys

# The following block of code removes the part of 
# the traceback related to this very module, and runpy
# Negative limit support came with python 3.5, so it will not work
# with previous versions.
# https://docs.python.org/3.5/library/traceback.html#traceback.print_tb
def myexcepthook(type, value, tb):
     nb_noise_lines = 3
     traceback_size = len(traceback.extract_tb(tb))
     traceback.print_tb(tb, nb_noise_lines - traceback_size)
if sys.version_info >= (3, 5):
    sys.excepthook = myexcepthook

if len(sys.argv) > 1:
    file = sys.argv[1]
    sys.argv = sys.argv[1:]

    with open(file) as f:
         code = compile(f.read(), file, 'exec')
         exec(code)

somefile.py

import sys
print sys.argv
raise Exception()

in the terminal

$ python3 -m mymodule somefile.py some_arg some_arg
['somefile.py', 'some_arg', 'some_arg']
Traceback (most recent call last):
  File "somefile.py", line 3, in <module>
    raise Exception()

$ python2 -m mymodule somefile.py some_arg some_arg
['somefile.py', 'some_arg', 'some_arg']
Traceback (most recent call last):
  File "/usr/lib64/python3.5/runpy.py", line 184, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib64/python3.5/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/home/azmeuk/dev/testpy/mymodule/__main__.py", line 16, in <module>
    exec(code)
  File "somefile.py", line 3, in <module>
    raise Exception()

$ python somefile.py some_arg some_arg
['somefile.py', 'some_arg', 'some_arg']
Traceback (most recent call last):
  File "somefile.py", line 3, in <module>
    raise Exception()
Exception

Still, if someone has a better proposition, it would be great!

Sign up to request clarification or add additional context in comments.

1 Comment

It is a good solution. Make sure to add as little names as possible in the global dictionary of mymodule/__main__.py because somefile.py doesn't expect these objects in its namespace. You could also play with the limit argument in traceback.print_tb to display a clean traceback.
1

I think the negative value of limit does not work in traceback module before python 3.5. Here is an ugly hack that works with python 2.7

import sys
import traceback

class ExcFile(object):
    def __init__(self, file):
        self.topline = True
        self.file = file

    def write(self, s):
        if self.topline:
            u, s = s.split('\n', 1)
            self.file.write(u +'\n')
            self.topline = False
        if '#---\n' in s:
            u, s = s.split('#---\n', 1)
            self.file.write(s)
            self.write = self.file.write

ExcFile._instance = ExcFile(sys.stdout)    
# The following block of code removes the part of 
# the traceback related to this very module, and runpy
def myexcepthook(type, value, tb):
    traceback.print_exception(type, value, tb, file=ExcFile._instance)
sys.excepthook = myexcepthook

if len(sys.argv) > 1:
    file = sys.argv[1]
    sys.argv = sys.argv[1:]

    with open(file) as f:
        code = compile(f.read(), file, 'exec')
        exec(code) #---

All this should be written in a separate file, to avoid clutter __main__.py.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.