UWAGA Post archiwalny, przeniesiony ze starego bloga. Linki mogą nie działać, dane mogą być przeterminowane, autor może się wstydzić tego co napisał.
Dnia 5 marca dumnie prowadziłem prelekcje ‘Od problemu do aplikacji czyli jak automatyzować zadania’ na Wroc.py 54.
Poniżej umieszczam link do prezentacji, a już niedługo zamieszczę tekstową transkrypcję do niej, tak aby osoby które nie uczestniczyły w Wroc.py miały także okazje wyciągnąć coś dla siebie ze slajdów.
Link do prezentacji w serwisie slides.com
Transkrypcja
O mnie
Pozwolę sobie ominąć ten wstęp pozostawiając wyłącznie najistotniejszy element tej sekcji:
Wyznaję zasadę: Problem, System, Automation
Polega ona na 3 etapach:
- Problem
Odnalezienie w życiu powtarzalnego problemu lub takiego na który często napotykamy.
- System
Stworzenie systemu, flowchartu, TODO-listy tego w jaki sposób dany problem rozwiązujemy. Dzięki temu zdołamy ogarnąć chaos który występuje w danym problemie, zamknąć go w odpowiednich ramach i zaoszczędzić czas - właśnie przekształciliśmy nasz problem w zamknięty schemat postępowania, koniec z rozwiązywaniem tego problemu za każdym razem od nowa.
- Automatyzacja
Próba zautomatyzowania całego systemu bądź elementów łatwo automatyzowalnych aby zaoszczędzić nasz czas.
To, że istnieją takie trzy etapy wcale nie oznacza, że każdy z moich problemów je przechodzi. Są problemy słabo automatyzowalne ale też problemy słabo systematyzowalne i musimy sobie z tego zdawać sprawę. Jednakże w tym przypadku omówimy problem łatwy do usystematyzowania oraz automatyzacji.
Problem
Problem który potrzebowałem rozwiązać pojawił się naturalnie. Lubię muzykę, i lubię mieć ją dobrze pokatalogowaną. Z podziałem na rok i na miesiąc, tak abym mógł w dowolnym momencie cofnąć się do muzyki i wspomnień z nią związanych. Lubię też zbierać, czego dowodem jest ogromny zbiór memów na moim dysku.
Te dwie rzeczy łączą się w mój problem: problem zbierania muzyki oraz odpowiedniego katalogowania jej (wraz z odpowiednim tagowaniem).
System
Sposób w jaki rozwiązaywałem ten problem do tej pory można rozbić na listę odpowiednich kroków:
- Sprawdź każdy z linków w przeglądarce (
Google Chrome
) czy działa - Pobierz piosenki za pomocą
Free YouTube to MP3 Converter
- Użyj
Mp3tag
:- Usuń wszelkie “[Radio Edit]”, “[Official Video]” itp. z nazwy
- Wygeneruj artystę oraz tytuł piosenki na podstawie poprawionej nazwy
- Uzupełnij rok oraz gatunek muzyki ręcznie
Automatyzacja
Najprostszym rozwiązaniem mojego problemu było połączenie możliwości pobierania muzyki z YouTube za pomocą biblioteki
YouTubeDL
oraz parsowania tagów artystów z LastFM
w narzędzie które nazwałem LlameDL
Tak, jeżeli logo przypomina Ci Winamp
to wiesz skąd brałem inspiracje.
Minimum Viable Solution
Pierwszym etapem, gdy wiemy jak rozwiązujemy problem manualnie, to stworzenie MVS - Minimum Viable Solution. Będzie to najprostsze rozwiązanie naszego problemu bądź jego elementu. W tym przypadku chcemy zastąpić cały proces jednym skryptem.
Warto w tym miejscu oprzeć się na wiedzy którą posiadamy bez szukania tysiąca bibliotek i technologii które możemy wykorzystać - tworzymy minimalne działające rozwiązanie.
W moim przypadku było to użycie biblioteki YouTubeDL
do pobierania muzyki oraz parsowanie LastFM z pomocą requests
.
W tym momencie następuje natychmiastowa gratyfikacja. Udało nam się zautomatyzować nasz problem. YEY!
Readme
Działający kod to nie wszystko. Powinniśmy także zadbać o prostą dokumentacje naszego skryptu. Plik README może stać się nie tylko pomocny w sytuacji gdy zechcemy podzielić się z kimś naszym skryptem ale także w sytuacji gdy sami będziemy chcieli po czasie skorzystać ze skryptu.
Najprostszy plik który na tym etapie warto już mieć będzie zawierać informacje o tym, do czego jest dany skrypt, jak go uruchomić oraz jakie ma wymagania środowiskowe (system, biblioteki itp.).
Gratification
First issue
Chwalenie się kodem na tym etapie może być ryzykowne. W moim przypadku po pochwaleniu się skryptem na reddit dostałem pierwszy problem na githubie. Mój skrypt działał tak jak chciałem, ale tylko u mnie. Tylko na mojej maszynie.
Improvements
W tym momencie powinniśmy zadbać o możliwość rozbudowy naszego skryptu oraz jego reużywalność. Udostępnić więcej możliwości użycia danego skryptu w zależności od potrzeb i środowiska.
Existing libraries
W Pythonie jest niemalże pewne, że kod który napisałeś można zastąpić już istniejącą biblioteką.
Rule 34 of Python - 'If there is a need, there is a Python library for it'
Bardzo przydatnym narzędziem w tym miejscu staje się PyPi
gdzie możemy wyszukać spośród istniejących bibliotek
czy któraś z nich może zrobić pewną pracę za nas.
Dzięki temu dostajemy fajny interfejs, rozwijany przez innego developera który prawdopodobnie wziął pod uwagę
wiele problemów związanych z danym problemem które my byśmy odnajdywali i implementowali na nowo.
W ten sposób w moim skrypcie parsowanie lastFM
zastąpiłem biblioteką musicbrainzngs
.
Nie dość, że uprościło to moje rozwiązanie, to dało dostęp do dużo większej biblioteki informacji o artystach!
Może się tu pojawić pytanie: Jak wybierac biblioteki z gąszcza innych dostępnych na PyPi
?
Gdy chcę wykorzystaćjakiś interfejs, zasada jest prosta: wybieram biblioteke która najszybciej pozwoli mi zaimplementować rozwiązanie. To powoduje, że często wygrywają u mnie biblioteki których przykład sposobu użycia jest wystarczająco krótki i prosty do zrozumienia i wykorzystania.
Unit tests
Test Pyramid
Aby zapewnić, że zmiany które wprowadzimy w bibliotece nie zmienią logiki działania programu warto napisać unit testy do naszego kodu. Uprości to dalszy proces związany z wprowadzaniem zmian w kodzie, a także zabezpieczy nas przed możliwymi problemami związanymi ze zmianami w zależnościach czy interfejsach.
Refactoring
Na refactoring zawsze jest dobry moment. Mając napisane odpowiednio unit testy robienie refactoringu jest łatwiejsze i bezpieczniejsze. Po każdych zmianach możemy na nowo uruchomić testy i zweryfikować czy nasze zmiany nie wpłynęły na działanie aplikacji.
Więcej informacji o metodach refactoryzacji możecie poczytać na https://refactoring.guru/
Static Code Analysis
To analizatory które za nas upewnią się, że nasz kod jest miły ładny i przyjemny. Dzięki temu, możemy się skupić na implementowaniu nowych rzeczy, a weryfikacje czy wszystko implementowane jest poprawnie pozostawimy analizatorom.
Dzielimy je na:
- Logiczne - weryfikujące statyczne typowanie, wykrywają problemy bądź luki bezpieczeństwa
- MyPy, Bandit, PyFlakes, Pyre
- Stylistyczne - weryfikujące zachowanie stylu w kodzie oraz dokumentacji
- pycodestyle, pydocstyle
- Analityczne - weryfikujące złożoność kodu, ilość linii
- Radon, Mccabe
Continuous Integration
Aby to wszystko miało sens, dobrze jest aby wszystkie testy były uruchamiane przy każdorazowych zmianach w kodzie. Uruchamianie wszystkich narzędzi po kolei po każdej zmianie mija się z celem. I tutaj pojawia się hasło CI - Continuous Integration. Ciągła integracja naszego kodu
Najpopularniejsze narzędzia do CI z którymi mam doświadczenie to:
- TravisCI
- Gitlab CI
- Jenkins
W moim projekcie wykorzystałem TravisCI, choć teraz skorzystałbym zapewne z GitlabCI (który wspiera także repozytoria GitHubowe). Dzięki temu wszelkie analizatory czy unit testy będą uruchamiane na serwerach z pomocą zdefiniowanych przez nas pipeline’ów, gdzie sami ustalamy które analizatory i w jakiej konfiguracji mają być uruchamiane, a także uruchamiać nasz kod dla różnych wersji Pythona aby zapewnić kompatybilność.
Gratyfikacja
Mam aplikacje ze skonfigurowanym CI oraz z badgami!!
Interface
Aby zapewnić jak najlepszą użyteczność z naszego narzędzia powinno ono mieć przyjazne i dopasowany User Interface. Rozróżniamy kilka typów UI:
CLI
To interface oparty o terminal. W tym przypadku mamy wiele ciekawych bibliotek pythonowych które pozwolą nam na proste stworzenie CLI dla naszej aplikacji. Jednymi z nich są:
- Argparse
- Google Fire
- Click
- Docopt
Aby stworzyć jak najlepsze narzędzie CLI dobrze jest spojrzeć na inne narzędzia tego typu dostępne w chociażby Linuxie. Jest pewna lista argumentów / flag wykorzystywanych przez te narzędzia, dlatego też tworząc CLI dobrą praktyką jest wykorzystanie istniejących patternów - nasze narzędzie będzie wtedy łatwiejsze do użycia.
http://www.catb.org/esr/writings/taoup/html/ch10s05.html
entry_points
W przypadku aplikacji typowo desktopowych, dobrą praktyką jest dodanie skryptu do łatwego uruchamiania go poprzez użycie
nazwy w terminalu zamiast odnoszenia się do jego ścieżki.
entry_points
- to parametr który możemy zdefiniować w pliku setup.py, a który zapewnia nam dodanie narzędzia do
uruchamiania wszędzie na naszej maszynie.
GUI
To interfejs graficzny. Nie mam dużego doświadczenia z nimi, jednak najpopularniejszymi są:
- tkinter
- PyQT5
Ten pierwszy jest wbudowaną biblioteką Pythonową która pozwala nam tworzyć proste i lekkie GUI. Druga natomiast jest dużą biblioteką pozwalającą wyklikać nam GUI do aplikacji, jednakże minusem jest rozmiar jaki taka aplikacja będzie ostatecznie ważyć.
Web Framework
Frameworki pozwalające stworzyć witrynę możliwą do hostowania lokalnie bądź na zewnętrznym serwerze. Rzadko wykorzystywana metoda przy udostepnianiu skryptów które rozwiązują nasz problem chyba, że myślimy o udostępnianiu na szeroką skalę lub o monetyzacji. Zaliczamy do nich:
- Flask
- Django
Gratyfikacja
Po dodaniu odpowiedniego CLI nasza aplikacja otrzymała ładną komendę do wywołania w terminalu
llamedl --bookmark_name music_to_download --directory_path /home/jarek/Music
Extensibility
Aplikacja już działa, ma świetne wsparcie dla użytkownika ale czegoś jej brakuje. Mimo, że daliśmy możliwość wyboru katalogów skąd pobierane będą linki, a także gdzie utwory będą zapisane nasza aplikacja wciąż jest ograniczona. Jej implementacja zakłada tylko pobieranie tresci z jednego źródła, dla linków z jednego źródła i tagowanie z pomocą danych z jednego źródła. Ważnym jest teraz taka zmiana implementacji, aby można było wprowadzać obsługę nowych interfejsów. Na tym poziomie skupiłem się na dodaniu możliwości przekazywania linków na kilka różnych sposobów.
Design Patterns
Design Patterny zostały stworzone aby ułatwić implementacje różnych potwarzalnych w aplikacjach metod za pomocą odpowiednich szablonów. Dzięki temu programista znający design patterny widząc kod łatwiej zrozumie jak dana metoda jest implementowana w naszej aplikacji, oraz jak rozszerzyć jej możliwości.
Więcej o design patternach można przeczytać tutaj https://refactoring.guru/design-patterns, a o tym, jak używać ich z Pythonem oraz które z nich są aplikowalne w nim tutaj https://python-patterns.guide.
Composition over inheritance
Inheritance is when you design your types around what they are, and composition is when you design types around what they do.
Cleaning Up
Gdy mamy już wszystko gotowe można zrobić wielkie sprzątanie w naszej aplikacji.
Documentation
Jeżeli chcemy aby nasza aplikacja się dalej rozrastała, powinna ona posiadać odpowiednią dokumentacje. Dzięki temu użytkownikom łatwiej będzie zrozumieć jej działanie, a deweloperom rozpocząć implementacje nowych rozwiązań w niej. Najpopularniejszymi systemami do generowania takich dokumentacji są:
- Sphinx
- Read The Docs
Refactoring
Tak jak wspominałem wcześniej, zawsze jest dobry moment na refactoring. Po przejściu tylu etapów oraz pisząc dokumentacje jest szansa, że zauważysz miejsca gdzie można było coś zrobić lepiej, coś poprawić, dopisać coś. Staraj się zawsze zostawiać kod lepszym niż go zastałeś, nawet jeżeli nie zajmujesz się aktualnie tą funkcją, to jeżeli możesz coś w niej poprawić by była lepsza - zrób to. Pamiętaj, że statystycznie więcej kodu czytamy niż piszemy, więc niech ten kod będzie jak najbardziej czytelny być może.
Autoformatting
Ja tu piszę kod, nie maluje. Nie muszę wiedzieć jak coś ma dobrze wyglądać ale chcę by wyglądało dobrze zawsze.
Dlatego też aby nie spierać się o to jakiego wcięcia użyć korzystajmy z możliwości technologii. Autoformattery
pozwalają na automatyczne formatowanie kodu według konfiguracji ustalonej przez dany formatter. DEP8 ustalił
black
jako typowy formater do kodu w aplikacjach Django, i także go polecam (notatka: nie wspiera Pythona 2).
- autopep8
- yapf
- black
Co dalej?
W moim przypadku projekt to metoda nauki nowych rzeczy, ale także poznawanie różnych bibliotek i interfejsów. Dlatego też dalsze plany na aplikacje to:
- Docker
- Wsparcie dla innych przeglądarek ( Firefox, Opera )
- Wsparcie dla innych źródeł ( Soundcloud, Spotify?! )
Podsumowanie
Dużo osób gdy chce nauczyć się programowania, zastanawia się co napisać, jak napisać. Najważniejsze jest bowiem zacząć i pisać. LlameDL traktowałem od samego początku jako możliwość nauki oraz rozwijania wiedzy z programowania. Z czasem ucząc się oraz poznając nowe możliwości starałem się je wprowadzić do swojego projektu - takie połączenie nauka “just in time learning” z nauką poprzez praktykę.
Jeżeli jest coś, co chciałbym abyś zapamiętał z tej prezentacji to dwie frazy:
Problem, System, Automation
Zdefiniuj problem, stwórz system jego rozwiązywania i spróbuj go zautomatyzować
Solution, Interface, Extensibility
Twórz działające rozwiązanie, następnie ułatwiaj jego ponowne użycie a ostatecznie pozwól na obsługę większej ilości przypadków użycia
Dziękuję :)