Retrieving the full command line

Oscar Benjamin oscar.j.benjamin at gmail.com
Thu Jan 24 15:01:36 EST 2013


On 24 January 2013 17:13, Tim Golden <mail at timgolden.me.uk> wrote:
> On 24/01/2013 16:53, Oscar Benjamin wrote:
>>> Does it work if you use the -m option to run a module rather than a script?
>>
>> Sorry that was written incorrectly. I meant to say: does it work when
>> a module is directly on sys.path rather than as a submodule of a
>> package? In this case __package__ is set to the empty string if run
>> with -m or None if run with a direct path. So the check needs to be
>> "__package__ is not None" rather than "bool(__package__)".
>
> The answer is: it depends. Given the code I outlined earlier:
>
> A package-based module run via -m (python -m package.module) works
> as described (including the implicit __main__ module, my
> primary use-case).

Does it work in the "python -m package.module" case? It looks to me as
if the code below will run "python -m package" instead. I think you
need to split the module name out of sys.argv[0] and put that into the
command line as well.

> import __main__
>
> # [.. .snip ...]
>
> try:
>     is_package = bool(__main__.__package__)
> except NameError:
>     is_package = False
> if is_package:
>     args = [sys.executable, '-m', __main__.__package__] + sys.argv[1:]
> else:
>     args = [sys.executable] + sys.argv
>
> os.chdir(_startup_cwd) # avoids relative/absolute issues
> os.execv(args[0], args)

I believe "python -m package" and "python -m package.__main__" are
equivalent so it doesn't matter in that case.

>
> A module run from the filesystem (python c:\path\to\module.py) works
> by dropping through through to the not is_package logic branch.
>
> A module run via -m (python -m module) actually works by accident,
> because it too drops through to the not is_package branch and is
> rerun with its full filesystem path. This doesn't have the same
> problems as running a package from the filesystem because relative
> imports aren't an issue. I don't know if there are any other differences
> between python -mmodule and python c:\path\to\module.py.

There is a (probably pathological) case in which running the script
via a file path and running it via -m are not equivalent. "python
dir/script.py" places "dir" at the top of sys.path. "python -m script"
only works if "dir" is in sys.path but it needn't be at the top. This
means that the order of sys.path is changed and import conflicts may
be resolved differently in the two cases:

~$ cat ~/.local/lib/python2.7/site-packages/script.py
print("Running script.py")
import optparse
~$ cat ~/.local/lib/python2.7/site-packages/optparse.py
raise ImportError('Wrong optparse!')
~$ python ~/.local/lib/python2.7/site-packages/script.py
Running script.py
Traceback (most recent call last):
  File "/home/oscar/.local/lib/python2.7/site-packages/script.py",
line 2, in <module>
    import optparse
  File "/home/oscar/.local/lib/python2.7/site-packages/optparse.py",
line 1, in <module>
    raise ImportError('Wrong optparse!')
ImportError: Wrong optparse!
~$ python -m script
Running script.py
~$ python ~/.local/lib/python2.7/site-packages/script.py
Running script.py
Traceback (most recent call last):
  File "/home/oscar/.local/lib/python2.7/site-packages/script.py",
line 2, in <module>
    import optparse
  File "/home/oscar/.local/lib/python2.7/site-packages/optparse.py",
line 1, in <module>
    raise ImportError('Wrong optparse!')
ImportError: Wrong optparse!
~$ python -m script
Running script.py

>
> As you say, a more refined check could determine a blank __package__
> as opposed to a None __package__. But this code (cherrypy) must also
> cope with version of Python before 2.6 which didn't even have a
> __package__ attribute, muddying the waters that little bit further.

Then you won't be able to use this method to get the -m switch to work
on Python < 2.6. If it's ok to just never try the -m switch on those
versions then it's as simple as doing:
pkg = getattr(__main__, '__package__', None)


Oscar



More information about the Python-list mailing list