4

What is the difference between

python -m path.to.file 

and

python path/to/file.py

when executing python files ?

  • 1
    `python -m ` runs a python package using `/__main__.py` as the entrypoint, where `python ` runs a single script. – joshmeranda Sep 10 '21 at 19:55
  • @joshmeranda can you elaborate on this? i don't get the implications of it. –  Sep 10 '21 at 19:56
  • 1
    Heres the doumentaiton for modules vs packages, you can read a full description there: https://docs.python.org/3/tutorial/modules.html#packages – joshmeranda Sep 10 '21 at 19:58
  • @joshmeranda That's not right. `__main__.py` may not even exist. – wim Sep 10 '21 at 19:59
  • @wim You're right I haven't used it to run a module before only packages, so I made an correct assumption. Typically unless you provide a value for `__spec__` python will exit with an error if no `__main__.py` is found – joshmeranda Sep 10 '21 at 20:06
  • @joshmeranda: If it's a module (file typically with `.py` extension) instead of a package (directory), it's just run with its `__name__` set to `'__main__'` like a script is, no need for `__main__.py`. Only difference is how it's looked up, and that it's run with the knowledge that it's in a package hierarchy, so it can do relative imports. – ShadowRanger Sep 10 '21 at 20:08
  • 1
    Useful information on why the difference matters at [How to fix "Attempted relative import in non-package" even with __init__.py](https://stackoverflow.com/q/11536764/364696). – ShadowRanger Sep 10 '21 at 20:09
  • Another useful one: [Relative imports for the billionth time](https://stackoverflow.com/q/14132789/674039) – wim Sep 10 '21 at 20:33

3 Answers3

2

The difference is where the script is looked up. Operating systems tend to have a set of paths where executable programs are found (e.g., PATH) and a different set of directories where dynamic libraries are found (e.g., LIB). The idea is that many programs can use the same libraries regardless of where the executable is.

Its the same thing with python. You execute a top level script always called "__main__" and python will find the packages and modules you import in various places such its site-packages directory. The general idea is that you install packages/modules into these special directories and top level scripts into a directory on the PATH. The "shebang" (#!/usr/bin/env python3) at the top of a file tells unix-like systems how to execute scripts and the file association tells Windows how to do it.

But there is a third way to do things. You can use python's module lookup logic to find a module but then execute it as a top level script. Now all of your programs are in python's special directories and you don't have to muck about with getting scripts into the system PATH.

  • python path/to/file.py will look up a script in the local file system using the relative or absolute path and run it as a script. Notice that you need to know the path.
  • python -m path.to.file will look up the module in the python namespace and run it as a script. You don't need to know the path. This works with any installed module and you don't have to know where it happens to be installed at the moment. The module could be in a zip file for all you know.
tdelaney
  • 73,364
  • 6
  • 83
  • 116
1

The most important difference is that python -m path.to.file uses the import system to locate the module path.to.file, whereas python path/to/file.py does not need to resolve the location at all because it was provided directly as a filename (script).

So, the best way to understand the difference is to concoct a situation where they are significantly different. In the case of python -m usage, wherever the module "file" is eventually found depends on what's in sys.path, and we can make it resolve here or there by modifying the order of traversal.

$ mkdir -p /tmp/path/to
$ mkdir -p /tmp/other/path/to
$ echo "print('hello')" > /tmp/path/to/file.py
$ echo "print('world')" > /tmp/other/path/to/file.py
$ PYTHONPATH=/tmp:/tmp/other python3 -m path.to.file
hello
$ PYTHONPATH=/tmp/other:/tmp python3 -m path.to.file
world

On the other hand, where python path/to/file.py finds the code is unconditional: path/to/file.py is executed directly and it doesn't matter what's on sys.path.

There is another thing which is important to know about and beware of: the first element of sys.path may be different depending on whether the interpreter was invoked with a script python path/to/file.py or with python -m.

  • When python was invoked as a script, the first element of sys.path will be the directory which contains the script (e.g. /tmp/path/to).
  • When python was invoked with -m, the first element of sys.path will be the current working directory.

Therefore, where import statements resolve, or if they can be resolved at all, may be different depending on whether -m was provided or not. This is particularly important in running a test suite (e.g. "pytest" vs "python -m pytest"), where you generally want to be sure you are testing against an installed version of the code, rather than whatever state the current working directory is in.

Other minor differences:

  • python -m path.to.file may use cached bytecode file.pyc if one exists already, and it will create one if it's non-existing or stale. python path/to/file.py can not use a cache, and neither will it create a file.pyc.
  • python -m path.to.file will have the global variable __package__ set to the string "path.to", since it is loading file as a submodule of a package. python path/to/file.py will have __package__ set to None in the global namespace, since it's executed directly as a script and Python is unaware of any package structure in this case.
wim
  • 338,267
  • 99
  • 616
  • 750
0

Just quoting from documentation:

It will allow modules to be located using the Python module namespace for execution as scripts.

So basically you can get away with specifying the full path (provided a corresponding python file exists for you to be referenced) and it would be executed directly. In case a module name is provided, then it will be searched as per the sys.path (provided they can be imported with import <module> because otherwise you won't find it even using module namespace).

-m module expects a module name. So in certain cases like built in modules or precompiled modules you can't use it but in other cases it will be searched in the sys.path locations and will be executed as if we are running it with __main__ module.

Note also that package names are also allowed. (check the documentation for more details).

Here is the link and check the timeit example.

user2736738
  • 30,591
  • 5
  • 42
  • 56
  • @CharlesDuffy The search is one of the thing here, that is of importance. Then it is run as if I am running `python pdb.by` (the `__main__` method is executed then). I am repeating this to you in the hope that if I said anything wrong/unclear in my answer, you would correct me. – user2736738 Sep 10 '21 at 20:16