Struktura pro Váš projekt

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

S pythonem je hrozně snadné začít. Podle internetového návodu dáte dohromady, nejprve Hello world!, potom Fibonacciho posloupnost, a ani se nenadějete a už píšete Hada v pygame. Ale protože to šlo tak rychle, tak máte pořád všechny soubory v jednom adresáři, a vůbec žádnou představu, jak si v projektu udělat pořádek:

  • udržovat seznam závislostí
  • vytvořit balíček
  • psát testy

Dnes si ukážeme to nejzákladnější, co musíme udělat, aby to přestala být hromada souborů, a stal se z toho projekt. Upozorňuji, že článek obsahuje jistá zjednodušení. Cílem není dokonale všechno popsat, ale umožnit Vám rychle začít.

virtualenv

Předpokládám, že všechno, co budete dělat budete dělat ve virtualenv. Nechci virtualenv popisovat zde, na síti najdete spoustu návodů a vysvětlení.

Jméno

Vybrat jméno pro projekt je pro mě vždycky nejtěžší. Mělo by být krátké a výstižné, pokud možno trošku vtipné. Rozhodně bez mezer a diakritiky. Pro naši demonstraci použijeme název semprini. Proč? Proto.

Adresářová struktura

V hlavním adresáři projektu budou soubory pro vlastní organizování projektu, a teprve v podadresářích budou vlastní programové nebo pomocné soubory projektu. Např. takto:

Struktura projektu

K setup.py se brzo dostaneme. Soubory Vašeho projektu jsou v tom vnitřním semprini. semprini/__init__.py bude pro začátek prázdný. No a do main.py něco dáme:

def main_func():
    x = 0
    y = 1
    out = []
    for i in range(0, 10):
        out.append(x)
        (x, y) = (y, x + y)
    return out

if "__main__" == __name__:
    print (main_func())

Tak teď máme modul, který něco dělá, když spustíme python main.py, tak něco vypíše.

setup.py

Do tohoto souboru napíšeme informace o projektu. Vůbec se nemusí jmenovat setup.py, ale je to dobrá konvence. Naprosté minimum:

from setuptools import setup

setup(
    name='semprini',
    version='0.1',
    packages=['semprini'],
)

Takový soubor mají celkem všechny projekty, které si stáhnete, třeba z githubu. Je v něm toho víc, někdy i mnohem víc, ale v principu to vždycky končí voláním funkce setup.

Vyvíjíme

Na začátek uděláme v adresáři, ve kterém je setup.py

pip install -e .

nebo

python setup.py develop

Tohle udělá odkaz z adresáře site-packages do našeho projektu. Je to odkaz, takže když vyvíjíte ve Vašem pracovním adresáři, a balík je zároveň nainstalován. Nainstalovaná je ta pracovní verze v pracovním adresáři. K čemu nám to je? Dosud jsme mohli náš modul spouštět jen s uvedením celé cesty, např.

cd semprini/semprini
python main.py

nebo

cd semprini
python semprini/main.py

Poté, co jsme nainstalovali, můžeme kdekoliv (při aktivním virtualenv) udělat

python -m semprini.main

Případně, pokud vyvíjíme knihovnu, dá se takto nainstalovaná knihovna importovat/používat.

My zatím nemáme žádné závislosti, ale až je mít budeme, tak setup.py develop je nainstaluje.

Balíme

# nainstalujeme wheel, který umožní vytvářet balíčky ve formátu .whl
pip install wheel
# vytvoření balíčku se zdrojovými kódy:
python setup.py sdist
# vytvoření instalačního wheel balíčku
python setup.py bdist_wheel

setup.py sdist vytvoří buď zip nebo tar.gz. Když se do něj podíváte, zjistíte, že k vrcholový adresář pojmenoval podle jména projektu, a přidal verzi. V našem případě je to semprini-0.1. Podobně se můžete podívat i do wheel balíčku, což je vlastně jinak pojmenovaný zip. Schválně, zkuste změnit v setup.py jméno projektu a verzi, a vytvořit balíčky. Co se stane?

Závislosti

V setup.py lze definovat závislosti projektu. Náš žádný nemá, tak si je vymyslíme a doplníme do setup.py:

from setuptools import setup

setup(
    name='semprini',
    version='0.1',
    packages=['semprini'],
    install_requires=["markdown", ],
)

Jak už jsem naznačoval, když uděláte python setup.py develop nebo pip install -e ., nainstalují se Vám i závislosti. Stejně tak, když sestavíte balíček, tak při instalaci pipem se pip pokusí nainstalovat závislosti.

Testy

Existuje několik názorů, kam dát testy. Dvě nejčastější varianty jsou adresáře:

  • semprini/tests
  • semprini/semprini/tests Já preferuju první možnost, protože pak nemám testy v instalačním balíčku. Tak si přidáme test (používám pytest, protože s ním je to všechno tak nějak jednodušší):

Struktura projektu s testy

__init__.py bude prázdný, do test_main.py si přidáme nějakou blbůstku:

import unittest

from semprini.main import main_func


class TestCase(unittest.TestCase):
    def test_main_default(self):
        assert len(main_func()) == 10

Testy se spustí takto:

python setup.py test

U testů by šlo strávit hodně času. Pro teď se spokojíme s doplněním setup.py:

from setuptools import setup

setup(
    name='semprini',
    version='0.1',
    packages=['semprini'],
    install_requires=["markdown", ],
    test_suite="tests",
)

Pokud nepřidáme parametr test_suite, setup nalezne testy dvakrát. Zmíním snad ještě to, že standardní modul unittest je poněkud těžkopádný, a odkážu čtenáře na module py.test.

Závěr

Cílem tohoto textu bylo ukázat, že projekt by měl mít nějakou strukturu. Pokud ji má, tak python má nějaké zaběhnuté mechanizmy, jak s tou strukturou pracovat, a vždy je lepší držet se těchto zaběhnutých mechanizmů, než vymýšlet vlastní. V budoucnu se ke struktuře projektu možná vrátíme, a probereme témata jako:

  • Číslování verzí
  • Lokalizace
  • Testování pro různé platformy a různé verze pythonu