ProHoster > Blog > administracja > Tworzenie przyjaźni Pythona i Basha: biblioteki smart-env i Python-Shell
Tworzenie przyjaźni Pythona i Basha: biblioteki smart-env i Python-Shell
Dzień dobry wszystkim.
Dziś Python jest jednym z najczęściej używanych języków w zakresie tworzenia nie tylko samych produktów programowych, ale także zapewniania ich infrastruktury. W rezultacie wielu devopów, czy to z własnej woli, czy wbrew niemu, musiało nauczyć się nowego języka do późniejszego wykorzystania jako uzupełnienie starych, dobrych skryptów Bash. Bash i Python wyznają jednak odmienne podejście do pisania kodu i posiadają pewne cechy, przez co przeniesienie skryptów Bash na „język węża” okazuje się czasem zadaniem pojemnym i wcale nie trywialnym.
Aby ułatwić życie devopsom, stworzono i nadal tworzy się wiele przydatnych bibliotek i narzędzi w Pythonie. W tym artykule opisano dwie nowe biblioteki stworzone przez autora tego wpisu - inteligentne środowisko и powłoka Pythona - i zaprojektowany, aby odciążyć devopsa od konieczności zwracania dużej uwagi na zawiłości pracy z Pythonem, pozostawiając miejsce na ciekawsze zadania. Zakresem działania bibliotek są zmienne środowiskowe i uruchamianie narzędzi zewnętrznych.
Wszystkich zainteresowanych zapraszam do kota.
Nowe „rowery”?
Wydawałoby się, po co tworzyć nowe pakiety do całkiem zwyczajnych operacji? Co uniemożliwia Ci bezpośrednie użycie os.environ i subprocess.<wybranej metody lub klasy>?
Dowody na korzyść każdej z bibliotek przedstawię osobno.
biblioteka smart-env
Przed napisaniem własnego pomysłu warto skorzystać z Internetu i poszukać gotowych rozwiązań. Oczywiście istnieje ryzyko, że nie znajdziesz tego, czego potrzebujesz, ale jest to raczej „zdarzenie ubezpieczeniowe”. Z reguły takie podejście działa i pozwala zaoszczędzić dużo czasu i wysiłku.
Zgodnie z wynikami poszukiwania wyszło na jaw, co następuje:
istnieją pakiety, które faktycznie otaczają wywołania os.environ, ale jednocześnie wymagają szeregu odwracających uwagę działań (utworzenie instancji klasy, specjalne parametry w wywołaniach itp.);
Istnieją dobre pakiety, które jednak są ściśle powiązane z konkretnym ekosystemem (głównie frameworki internetowe, takie jak Django) i dlatego nie są wcale uniwersalne bez pliku;
rzadko zdarzają się próby zrobienia czegoś nowego. Na przykład, dodaj pisanie i jawnie analizuj wartości zmiennych, wywołując metody takie jak
get_<typename>(var_name)
Lub tu jeszcze jedno rozwiązanie, który jednak nie obsługuje obecnie zhańbionego Pythona 2 (który pomimo oficjalny RIP, nadal istnieją góry napisanego kodu i całe ekosystemy);
Istnieją rzemiosła uczniów, które z nieznanego powodu trafiły do upstreamowego PyPI i stwarzają jedynie problemy z nazewnictwem nowych pakietów (w szczególności nazwa „smart-env” jest niezbędnym środkiem).
A tę listę można ciągnąć długo. Jednak powyższe punkty wystarczyły, abym podekscytował się pomysłem stworzenia czegoś wygodnego i uniwersalnego.
Wymagania jakie zostały postawione przed napisaniem smart-env:
Najprostszy schemat użycia
Łatwo konfigurowalna obsługa wpisywania danych
Kompatybilny z Pythonem 2.7
Dobre pokrycie kodu testami
Ostatecznie udało się to wszystko zrealizować. Oto przykład użycia:
from smart_env import ENV
print(ENV.HOME) # Equals print(os.environ['HOME'])
# assuming you set env variable MYVAR to "True"
ENV.enable_automatic_type_cast()
my_var = ENV.MY_VAR # Equals boolean True
ENV.NEW_VAR = 100 # Sets a new environment variable
Jak widać na przykładzie, aby pracować z nową klasą, wystarczy ją zaimportować (nie trzeba tworzyć instancji - pomijając dodatkową akcję). Dostęp do dowolnej zmiennej środowiskowej uzyskuje się poprzez odwołanie się do niej jako do zmiennej klasy ENV, co w istocie czyni tę klasę intuicyjnym opakowaniem dla natywnego środowiska systemu, jednocześnie zamieniając ją w możliwy obiekt konfiguracyjny dla niemal każdego systemu ( podobne podejście osiągnięto na przykład w Django, tylko tam obiektem konfiguracyjnym jest sam moduł/pakiet ustawień).
Włączanie/wyłączanie trybu obsługi automatycznego pisania odbywa się za pomocą dwóch metod - Enable_automatic_type_cast() i Disable_automatic_type_cast(). Może to być wygodne, jeśli zmienna środowiskowa zawiera serializowany obiekt podobny do JSON lub nawet tylko stałą logiczną (jednym z najczęstszych przypadków jest jawne ustawienie zmiennej DEBUG w Django poprzez porównanie zmiennej środowiskowej z „prawidłowymi” ciągami znaków). Ale teraz nie ma potrzeby jawnej konwersji ciągów znaków - większość niezbędnych akcji jest już osadzona w głębinach biblioteki i czeka tylko na sygnał do działania. 🙂 Ogólnie rzecz biorąc, pisanie działa w sposób przejrzysty i obsługuje prawie wszystkie dostępne wbudowane typy danych (zamrożone, złożone i bajty nie były testowane).
Wymóg obsługi Pythona 2 został wdrożony praktycznie bez wyrzeczeń (rezygnacja z pisania i niektórych „cukierków” najnowszych wersji Pythona 3), w szczególności dzięki wszechobecnej szóstce (w celu rozwiązania problemów związanych z wykorzystaniem metaklas ).
Ale są pewne ograniczenia:
Obsługa Pythona 3 oznacza wersję 3.5 i wyższą (ich obecność w Twoim projekcie wynika albo z lenistwa, albo z braku potrzeby ulepszania, bo ciężko znaleźć obiektywny powód, dla którego nadal jesteś na 3.4);
W Pythonie 2.7 biblioteka nie obsługuje deserializacji literałów zestawu. Opis tutaj. Ale jeśli ktoś ma ochotę to wdrożyć to zapraszam :);
Biblioteka posiada również mechanizm wyjątków na wypadek błędów analizy. Jeśli żaden z dostępnych analizatorów nie może rozpoznać ciągu znaków, wartość pozostaje ciągiem (raczej ze względu na wygodę i kompatybilność wsteczną ze zwykłą logiką działania zmiennych w Bash).
biblioteka powłoki Pythona
Teraz opowiem o drugiej bibliotece (pominę opis wad istniejących analogów - jest ona podobna do tej opisanej dla smart-env. Analogi - tutaj и tutaj).
Ogólnie pomysł wdrożenia i wymagania co do niego są podobne do tych opisanych dla smart-env, co widać na przykładzie:
from python_shell import Shell
Shell.ls('-l', '$HOME') # Equals "ls -l $HOME"
command = Shell.whoami() # Equals "whoami"
print(command.output) # prints your current user name
print(command.command) # prints "whoami"
print(command.return_code) # prints "0"
print(command.arguments) # prints ""
Shell.mkdir('-p', '/tmp/new_folder') # makes a new folder
Pomysł jest taki:
Pojedyncza klasa reprezentująca Basha w świecie Pythona;
Każde polecenie Bash wywoływane jest jako funkcja klasy Shell;
Parametry każdego wywołania funkcji są następnie przekazywane do odpowiedniego wywołania polecenia Bash;
Każde polecenie wykonywane jest „tu i teraz” w momencie jego wywołania, tj. podejście synchroniczne działa;
możliwy jest dostęp do wyniku polecenia na stdout, a także do jego kodu powrotu;
Jeśli polecenia nie ma w systemie, zgłaszany jest wyjątek.
Podobnie jak w przypadku smart-env, istnieje wsparcie dla Pythona 2 (chociaż wymagane było trochę więcej ofiarnej krwi) i nie ma wsparcia dla Pythona 3.0-3.4.
Plany rozwoju biblioteki
Możesz teraz korzystać z bibliotek: obie są opublikowane na oficjalnym PyPI. Źródła są dostępne na Githubie (patrz poniżej).
Obie biblioteki będą rozwijane z uwzględnieniem opinii zebranych od zainteresowanych. A jeśli wymyślenie różnych nowych funkcji w smart-env może być trudne, to w Pythonie-Shell zdecydowanie jest coś jeszcze do dodania:
obsługa połączeń nieblokujących;
możliwość interaktywnej komunikacji z zespołem (praca ze stdin);
dodanie nowych właściwości (na przykład właściwość do odbierania danych wyjściowych ze stderr);
implementacja katalogu dostępnych poleceń (do użycia z funkcją dir());
UPD 23.02.2020:
* Repozytoria zostały przeniesione, odpowiednie linki zostały zaktualizowane
* Wersja python-shell==1.0.1 jest przygotowywana do wydania 29.02.2020. Zmiany obejmują obsługę autouzupełniania poleceń i polecenia dir(Shell), uruchamianie poleceń z nieprawidłowym identyfikatorem Pythona oraz poprawki błędów.