python -m a podobné legrácky

... Petr Blahoš, 27. 7. 2017 Python

V příspěvku o setup.py jsme používali volání python -m jméno.modulu. Jak jste asi pochopili, tenhle parametr zavolá modul. Tím bych mohl skončit.

Ale přece, pár drobností.

Moduly

Python hledá moduly na cestě. Cesta je někde definovaná. Zkuste:

Python 3.6.1 (default, Apr 23 2017, 15:03:36) 
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/usr/local/lib/python36.zip', '/usr/local/lib/python3.6', '/usr/local/lib/python3.6/lib-dynload',
'/home/petr/.local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/site-packages']

Teď si to zkuste s jiným pythonem a pak ještě ve virtualenv. Všimněte si, že ty cesty jsou pokaždé jiné. Když nainstalujete balíček, nainstaluje se do site-packages. Když pak spustíte python -m jméno.modulu, hledá se jméno.modulu na cestě, a pokud se najde, tak se modul spustí.

Všimněte si toho '' na první pozici. To znamená, že moduly se hledají i v aktuálním adresáři. Takže jestli v něm máte nějaký modul, můžete ho zkusit spustit: python -m modul

Tohle využívá i mnoho standardních modulů. A protože máme dobrou konvenci, že pokud má modul samostatně fungovat, obsahuje na konci něco jako:

if __name__ == "__main__":
   # udělej nějakou práci
   main()

tak můžeme snadno najít moduly, které se dají přes python -m spustit:

cd /usr/local/lib/python3.6
grep __main__ *.py

Namátkou:

python -m calendar
python -m http.server
python -m pydoc -b
python -m smtplib
python -m unittest
python -m webbrowser  -n http://petr.blahos.com/
python -m zipfile -c zipfile.zip src_directory

__module__.py

Vrátíme se k balíčku semprini, který jsme si vytvořili v díle o setup.py. Jak si možná vzpomínáte, spouštěli jsme tam python -m semprini.main. Lepší by bylo spouštět jen python -m semprini. Co to udělá:

$ python -m semprini
/home/petr/work/SEMP/bin/python: No module named semprini.__main__; 'semprini' is a package and cannot be directly executed

Tak tady to máte. Musíme si vytvořit modul semprini/__main__.py, a je to:

if "__main__" == __name__:
    from semprini import main
    print (main.main_func())

Proč if __name__ == "__main__"?

Python nerozlišuje mezi moduly, které slouží k naimportování a moduly, které slouží ke spuštění. Tak si představte, že máte modul, který definuje funkce na rekurzivní mazání souborů. Když ho naimportujete, prostě Vám nadefinuje nějaké ty mazací funkce. Když ho spustíte, smaže disk. Takové moduly každý píše naprosto běžně, že ano?

Kdybyste prostě na konci modulu napsali:

rm_rf("/")

tak by se při spuštění modulu zavolala funkce rm_rf z toho modulu, a pokusila se smazat disk. To při spuštění modulu chceme. Jenže totéž by se stalo i při importu. Proto upravíme:

if __name__ == "__main__":
    rm_rf("/")

V běžícím programu v pythonu jsou vždy nějaké globální proměnné. Jedna z nich je __name__ - jméno aktuálního modulu. No a ten modul, který byl spuštěn v ní bude mít __main__. Importovaný modul v ní bude mít své jméno, např. semprini.main. Takže pokud modul spustíte, bude se jmenovat __main__, pokud jej importujete, nebude se jmenovat __main__.

Schválně, přečtěte si můj příspěvek o rozdílu mezi interpretovaným a kompilovaným jazykem.

Závěr

Když už jsme si dali tu práci a vytvořili balíček, který něco dělá, měli bychom dát možnost ho nějak smysluplně spustit. python -m ... je rozhodně krok, který někam vede, ale ještě to není úplně ono. Proto se někdy příště podíváme na skripty.