The Python -m switch

March 14, 2021

This post will be very short, and aside from explaining what is the -m switch and how it works, it will also serve 2 secondary purposes:

  • putting me back in the habit of writing (I haven’t been writing for more than one year)
  • tightening my own grasp of the -m switch : Python ‘s import system has always been a bit confusing to me.

What is it ?

The -m (for mod) switch, is a switch you can give to the python interpreter; if you type python --help, you ‘ll see the following:

-m mod : run library module as a script (terminates option list)

It means that if you have a Python project with the following file hierarchy:

├── foo
│   └── foo.py

You can execute the module contained in the foo.py as a script with the following command: python -m foo.foo. Notice the syntax of the argument for the -m switch: it’s a dotted syntax, akin to the syntax for importing modules in Python files. In particular, this syntax python -m foo/foo.py wouldn’t be valid.

The above example was rather contrived, let’s see a more interesting example, which will involve the notion of module search path.

Example

Let’s consider the following project structure:

├── bar
│   ├── bar.py
├── foo
│   ├── foo.py

And let’s assume the module bar.bar needs to import something from the foo.foo module:

# bar/bar.py
from foo.foo import foo


def bar():
    foo()


if __name__ == 'main':
    print('bar module called as script')

Now, let’s say we want to execute the bar.bar module as a script, from the project directory. One could try to do:

python bar/bar.py`

But then they would have the following error:

ModuleNotFoundError: No module named 'foo'

To understand this, we need to introduce the notion of module search path. Basically, everytime Python tries to perform an import statement, it is looking for the corresponding module / package in a list of directories, which is the module search path.

As per the Python documentation, the module search path is initialized from these locations:

  • The directory containing the input script (or the current directory when no file is specified).
  • PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
  • The installation-dependent default.

Back to our example: the directory containing the input script is bar, from which the foo folder is not reachable, hence the ModuleNotFoundError. So how do we manage to execute the bar.bar module as a script ? We can use the -m switch!

python -m bar.bar

This way i’ll work as expected and display the following output:

bar module called as script

It works because in this case Python considered the folder from which we ran the command to be part of the module search path; indeed, let’s look back at the first clause of the module search path initialization: “The directory containing the input script (or the current directory when no file is specified)” - we didn’t specify any file (what we did specify is a module), hence Python took the current directory as part of the module search path, and hence was able to locate the foo/foo.py module.

That’s the end of my explanation - I’ll tackle Python’s relative imports (another nemesis of mine) some day if I feel brave.


Profile picture

Written by Vincent Perez, a polymorphic software developer.