PyCON UK 2014 – poznámky

Měl jsem možnost se podívat na PyCON UK a nelituji. Odnesl jsem si spoustu nových znalostí, tipů, na co se podívat či co aplikovat v praxi. Něco málo předám dál.

Logování

Můžete se snažit jak chcete, ale uživatelé vždycky najdou chyby ve vašich aplikacích. Kvůli tomu je potřeba si zajistit dobré logování a o tom byla jedna z prvních přednášek. Tipy jsou poměrně jasné, ale je dobré si je připomenout, stejně jako tak prozkoumat, zda neexistují nějaké nové pomůcky, které se mohou hodit.

Základ je logovat tak, aby šlo chybu jednoduše znovu vyvolat. Ještě lepší by byl formát, abych to mohl jednoduše hodit do unit testů. Což je sice hezká idea, ale přednášející ji dál nerozváděl a nedovedu si představit jak něco takového zautomatizovat. Ale rozhodně stojí na zamyšlenou. U webovek by šlo třeba generovat aspoň základ Seleniového testu…

Během přednášky zaznělo několik toolů na správu reportovaných chyb. Z nich mě zaujal například Sentry, který zvládne víc, než jen sbírat chyby, ale také umí vyhledávat duplicity. Mám chuť už dlouho udělat u naší aplikace automatické nahrávání bugů do systému, z čehož mě stále odrazují duplicity. Přednáška mi sice nedala odkaz na tool, který by se mi přesně hodil, ale minimálně mne nakopla se k tomuto problému vrátit a konečně vyřešit.

Refaktorování

Další obsahově zajímavá přednáška byla o Rope. Pythoní balík umožňující refaktoring. Občas se hodí udělat přejmenování některé proměnné, která je prolezlá všude možně. To se pak do práce moc nechce. Proto mne seznámení s Rope potěšilo. Škoda jen, že zatím není tolik propojení s editory (pro vim či emacs plugin samozřejmě existuje). Proto vznikl projekt Traad, který je nadstavba nad Rope komunikující v JSON, s čímž dnes komunikuje snad každý. Jsem zvědav, zda to podpoří vytvoření pluginů pro další editory.

Jak jsem však zmínil, jedná se o Pythoní balík s API v Pythonu. Takže to lze pouštět hacker stylem. V následujícím příkladu přejmenovávám třídu nazvanou ClassName v modulu module.py na NewClassName. Hezké na Rope je, že, co jsem zkoušel, zvládne projít codebase a nahradit opravdu všude, kde se používá. Což například mé oblíbené Komodo IDE nezvládne.

project = rope.base.project.Project('.')
module = project.get_file('module.py')
offset = module.read().index('ClassName')
changes = rope.refactor.rename.Rename(project, module, offset).get_changes('NewClassName')
project.do(changes)

Hezké také na Rope je, že při volání project.history.undo() vrátí všechny změny z několika souborů najednou. Více informací na GitHubu projektu.

Prezentace z přednášky.

Testování

Většinou, když se jde v Pythonu testovat, sáhne se po knihovně unittest. Nejsem si jist, zda jsem opravdu neslyšel o knihovně pytest, nebo zda jsem zaslechl, ale vůbec jsem se nepodíval a pustil to z hlavy. Každopádně mne její představení mile překvapilo.

Klasický unittest řeší testování a nějaký ten setUp pro přípravu testovacího prostředí. U větších projektů to nemusí stačit a je potřeba si fixtures pořešit nějak jinak. V práci jsme si udělali docela dobrý jednoduchý systém. Píšu docela, protože stále to není ono. Na pytestu se mi líbí, že právě tyto problémy má vyřešené, resp. obsahuje hezkou práci s fixtures, a to hezky.

Napíšete jednoduše test, do parametru přidáte fixture(s), napíšete tyto fixtury, které označíte dekorátorem, a pustíte. Hotovo. Může to vypadat nějak takto:

@pytest.fixture(scope='module', params=['mysql', 'postgresql'])
def db_conn(request):
    return create_db_conn(request.params)

@pytest.yield_fixture
def db_table(db_conn):
    db_table = db_conn.make_db_table()
    yield db_table
    db_table.drop

def test_one(db_table):
    ...

Na této ukázce je hned několik věcí, které se mi líbí:

  • Každý test má jen takové fixtury, které potřebuje. U unittestu je jeden setUp pro třídu. Pokud tam budete mít jeden test, který patří do této sady, ale nepotřebuje všechny fixtury, vytváří se některé zbytečně (drahocené milisekundy).
  • Fixtury mohou na sebe záviset, stejně jako závisí test na fixturu. Není potřeba si dělat vlastní systém, kdy při použití tabulky musím také vytvářet konexi do databáze.
  • U fixtury mohu říct její platnost. To je v ukázce u db_conn určeno pomocí parametru scope, čímž říkáme, že chceme konexi sdílet pro module nazvaný module. Konkrétní ukázka je obdoba setUpModule, ale nemusím v každém modulu tuto funkci definovat – napíšu jednou bokem a použiji, kde potřebuji, jako závislost.
  • Pro více parametrů se volá test vícekrát. Například v ukázce výše máme jeden test. Zavolá se však dvakrát. Jednou pro databázi MySQL a jednou pro PostgreSQL. Samozřejmě, pokud použiji u jednoho testu více fixtur a každá bude mít různé parametry, vyzkouší se daný test se všemi možnostmi.
  • Vytváření a rušení fixtur je hezky v jedné metodě vedle sebe.

Na jednoduché aplikace to nemusí mít moc velký wow efekt, ale co mi řeklo, že to rozhodně musím vyzkoušet na jakémkoliv dalším projektu, je zobrazení selhání:

    def test_add():
        a = 1
        b = 2
        expected = 4
>       assert add(a, b) + add(b, b) == expected
E       assert (3 + 4) == 4
E        +  where 3 = add(1, 2)
E        +  and   4 = add(2, 2)

test_pytest_examples.py:12: AssertionError

Normálně byste jen viděli, že 7 != 4. pytest však udělá menší analýzu kódu a rozpadne jednotlivé volání v příkazu. Tak můžete rychle najít, kde je problém. Šikovné. Mimochodem nad testem je také vidět, jaké fixtures byly do testu posílány.

Jinak vidíte správně – využívá se klasický assert. Žádné volání různých metod. Zde vidím jednu nevýhodu a to, že tímto způsobem není možné nahradit metodu assertAlmostEqual, musí se ručně psát něco jako assert abs(foo) < 0.0001. Když si ale vzpomenu, kolikrát jsem tuto metodu využil, chybět mi nebude.

Prezentace srovnání unittestu a pytestu. Dokumentace k pytestu.

Práce s daty

V poslední době mne zajímá téma machine learning a tak bylo jasné, že na workshop Practical introduction to machine learning via Kaggle problems musím zajít. Moc ohledně samotného machine learningu jsme se nedozvěděli. Celé nás to spíš provázelo problémem „máme nějaká neúplná data a potřebujeme je nějak transformovat do podoby, abychom mohli předvídat“. I to však přineslo své ovoce – seznámení s knihovnou pandas.

V práci děláme na obchodní aplikaci se spoustou statistik a vždy s těmi daty operujeme docela neohrabaně. Resp. nikdy mi nepřišlo, že ten styl je až tak neohrabaný, dokud jsem se neseznámil s pandou. Tato knihovna umí doslova psí kusy. Přesvědčte se sami:

In [55]: df['one']
Out[55]: 
a     1
b     2
c     3
d   NaN
Name: one, dtype: float64

In [56]: df['three'] = df['one'] * df['two']

In [57]: df['flag'] = df['one'] > 2

In [58]: df
Out[58]: 
   one  two  three   flag
a    1    1      1  False
b    2    2      4  False
c    3    3      9   True
d  NaN    4    NaN  False

Viz dokumentace.

Může se to zdát tak trochu jako ne-Pythonovský přístup, ale líbí se mi. Je dost praktický. Složitá transformace dat lze provést jednoduše, čitelně. Je to další dokumentace, kterou si budu muset projít, abych měl přehled, kdy si budu moct jak zjednodušit práci. Hlavně odstranit zbytečné chyby. Brzy nás čekají velké úpravy statistik, tak se těším na použití. :-)

Dokumentace k pandě.

Requests

Určitě jste už někdy v Pythonu dělali nějaké requesty a určitě jste se trápili s modulem urllib. Případně jste už sáhli po lepším balíku requests, ale použili jste pouze requests.get('url').content. Víte však, co všechno tento balík zvládne a jak je sexy? Pro příště mám jasno, co použiji. Před chvílí jsem si akorát upravil jedno použití a hned to vypadá lépe.

Mrkněte na prezentaci.



Na konferenci zaznělo samozřejmě více zajímavých přednášek. Pokud prahnete po dalších informacích, mrkněte na program a dohledejte si prezentace k tomu, co vás zajímá. Všechny přednášky v místnosti HP room byly nahrávány, takže se časem na internet dostanou i záznamy. Případně můžete mrknout na videa z jiných PyCON konferencí, která jsem postoval nedávno.