Przyjaźnimy Pythona z Bashem: biblioteki smart-env i python-shell

Dzień dobry wszystkim.

Obecnie Python jest jednym z najczęściej używanych języków w dziedzinie tworzenia nie tylko produktów programistycznych, ale także zapewniania ich infrastruktury. W związku z tym wielu devopsów, z ich woli lub przeciwko niej, musiało nauczyć się nowego języka do dalszego wykorzystania jako uzupełnienie do starych, dobrych skryptów Bash. Jednak Bash i Python reprezentują różne podejścia do pisania kodu i mają swoje specyficzne cechy, przez co portowanie skryptów Bash na „język węży” czasami okazuje się być czasochłonnym i dalekim od trywialności zadaniem.

Aby uprościć życie devopsom, stworzono i nadal tworzy się wiele przydatnych bibliotek i narzędzi w Pythonie. Artykuł ten opisuje od razu dwie nowe biblioteki stworzone przez autora tego wpisu — smart-env i python-shell — mające na celu uwolnienie devopsa od konieczności poświęcania dużo uwagi szczegółom pracy z Pythonem, pozostawiając przestrzeń na bardziej interesujące zadania. Zakres działalności bibliotek obejmuje zmienne środowiskowe oraz uruchamianie zewnętrznych narzędzi.

Kto zainteresowany, zapraszam pod kat.

Nowe „rowery”?

Można by się zastanawiać, po co tworzyć nowe pakiety dla dość powszechnych operacji? Co przeszkadza w bezpośrednim wykorzystaniu os.environ i subprocess.?

Dowody na poparcie każdej z bibliotek przedstawię osobno.

Biblioteka smart-env

Zanim zacznie się pisać własne dzieło, warto poszukać w Internecie gotowych rozwiązań. Oczywiście jest ryzyko, że nie znajdzie się to, czego się potrzebuje, ale to raczej „przypadek ubezpieczeniowy”. Zazwyczaj podejście to działa i oszczędza mnóstwo czasu i wysiłku.

Z wynikami szukania zostały zidentyfikowane następujące:

  • są pakiety, które rzeczywiście owijają wywołania do os.environ, ale wymagają przy tym mnóstwa rozpraszających działań (tworzenie instancji klasy, specjalne parametry w wywołaniach itp.);
  • są niezłe pakiety, które jednak mocno wiążą się z określoną ekosystemem (głównie z frameworkami webowymi, takimi jak Django) i dlatego bez modyfikacji są zupełnie nieuniwersalne;
  • są rzadkie próby zrobienia czegoś nowego. Na przykład, dodanie typizacji i jawne analizowanie wartości zmiennych poprzez wywołanie metod typu
    get_(var_name)

    Albo jeszcze jedno rozwiązanie , które jednak nie wspiera obecnie zapomnianego Pythona 2 (na którym, pomimo że, które jednak nie wspiera obecnie niepopularnego Pythona 2 (na którym, pomimo oficjalny R.I.P., wciąż pozostają góry napisanego kodu i całe ekosystemy);

  • są prace studenckie, które w ogóle nie wiadomo po co znalazły się w upstreamowym PyPI i tylko tworzą problemy z nazewnictwem nowych pakietów (w szczególności nazwa „smart-env” — to działanie wymuszone).

I tę listę można by kontynuować długo. Jednak już powyższe punkty wystarczyły, aby zapalić pomysł zrobienia czegoś wygodnego i uniwersalnego.

Wymagania, które zostały postawione przed napisaniem smart-env:

  • Maksymalnie prosta schemat użycia
  • Łatwo konfigurowalna obsługa typów danych
  • Zgodność 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)  # Równa się print(os.environ['HOME'])

# zakładając, że ustawiłeś zmienną środowiskową MYVAR na "True"

ENV.enable_automatic_type_cast()

my_var = ENV.MY_VAR  # Równa się boolean True

ENV.NEW_VAR = 100  # Ustawia nową zmienną środowiskową

Jak widać z przykładu, do pracy z nową klasą wystarczy ją zaimportować (nie trzeba tworzyć instancji — to dodatkowy krok do wyeliminowania). Dostęp do każdej zmiennej środowiskowej uzyskuje się poprzez odniesienie do niej jako do zmiennej klasy ENV, co praktycznie czyni tę klasę intuicyjnie zrozumiałym opakowaniem natywnego systemowego środowiska, jednocześnie przekształcając je w możliwą opcję obiektu konfiguracyjnego praktycznie dowolnego systemu (podobne podejście, na przykład, osiąga się w Django, gdzie obiektem konfiguracyjnym jest bezpośrednio moduł/pakiet settings).

Włączanie/wyłączanie trybu wsparcia automatycznego typowania osiąga się za pomocą dwóch metod — enable_automatic_type_cast() i disable_automatic_type_cast(). Może to być wygodne, jeśli w zmiennej środowiskowej znajduje się zserializowany obiekt podobny do JSON lub nawet po prostu stała boolean (ewidencja zmiennej DEBUG w Django przez porównanie zmiennej środowiskowej z „dozwolonymi” łańcuchami — jeden z często spotykanych przypadków). Ale teraz nie ma potrzeby jawnie konwertować łańcuchów — większość potrzebnych działań już została zawarta w głębi biblioteki i czeka tylko na sygnał do działania. 🙂 W ogólnym rozrachunku typowanie działa przezroczysto i wspiera niemal wszystkie wbudowane typy danych (nie testowano frozenset, complex i bytes).

Wsparcie dla Pythona 2 zostało wdrożone praktycznie bez ofiar (rezygnacja z typing i niektórych "słodkich dodatków" ostatnich wersji Pythona 3), w szczególności dzięki powszechnemu six (w celu rozwiązania problemów z użyciem metaklas).

Jednak są też pewne ograniczenia:

  • Wsparcie dla Pythona 3 obejmuje wersję 3.5 i wyższe (ich obecność w Twoim projekcie jest wynikiem albo lenistwa, albo braku potrzeby na ulepszenia, ponieważ trudno wymyślić obiektywną przyczynę, dla której nadal używasz 3.4);
  • W Pythonie 2.7 biblioteka nie wspiera deserializacji literali zbiorów. Opis tutaj. Ale jeśli ktoś chciałby zaimplementować — zapraszam:);

Biblioteka również stosuje mechanizm wyjątków na wypadek błędów podczas analizy. Jeśli ciąg nie uda się rozpoznać przez żaden z dostępnych analizatorów, wartość pozostaje łańcuchowa (raczej ze względów wygody i zgodności wstecznej z tradycyjną logiką działania zmiennych w Bash).

Biblioteka python-shell

Teraz opowiem o drugiej bibliotece (opis niedociągnięć istniejących analogów pomijam — jest podobny do opisanego dla smart-env. Analogi — tutaj i tutaj).

Ogólnie rzecz biorąc, idea wdrożenia i wymagania wobec niej są analogiczne do opisanych dla smart-env, co widać z przykładu:

from python_shell import Shell

Shell.ls('-l', '$HOME')  # Równoważne "ls -l $HOME"

command = Shell.whoami()  # Równoważne "whoami"
print(command.output)  # wypisuje nazwę aktualnego użytkownika

print(command.command)  # wypisuje "whoami"
print(command.return_code)  # wypisuje "0"
print(command.arguments)  # wypisuje ""

Shell.mkdir('-p', 'tmp/new_folder')  # tworzy nowy folder

Idea jest taka:

  1. Jedna klasa, która personifikuje Bash w świecie Pythona;
  2. Każda komenda Bash jest wywoływana jako funkcja klasy Shell;
  3. Parametry wywołania każdej funkcji są następnie przekazywane do wywołania odpowiedniej komendy Bash;
  4. Każda komenda jest wykonywana "tu i teraz" w momencie jej wywołania, tzn. działa synchroniczne podejście;
  5. istnieje możliwość uzyskania dostępu do wyjścia komendy w stdout, a także do kodu jej zakończenia;
  6. Jeśli komenda nie istnieje w systemie — rzucany jest wyjątek.

Jak w przypadku smart-env, zapewnione jest wsparcie dla Pythona 2 (prawda, ofiary krwi było nieco więcej) i brak wsparcia dla Pythona 3.0-3.4.

Plany rozwoju bibliotek

Biblioteki można już teraz używać: obie są dostępne na oficjalnym PyPI. Źródła dostępne są na Githubie (patrz poniżej).

Obie biblioteki będą się rozwijać z uwzględnieniem opinii zebranej od zainteresowanych. I chociaż w smart-env może być trudno wymyślić różnorodność nowych funkcji, to w python-shell z pewnością jest jeszcze wiele do dodania:

  • wsparcie dla nieblokujących wywołań;
  • możliwość interaktywnej komunikacji z zespołem (praca z stdin);
  • dodawanie nowych właściwości (na przykład, property do uzyskania wyjścia z stderr);
  • implementacja katalogu dostępnych komend (do wykorzystania z funkcją dir());
  • itd.

Linki

  1. Biblioteka smart-env: Podróż, którą przeszedłem, okazała się fascynującą wyprawą w przeszłość i cieszę się, że w końcu znalazłem rozwiązanie. Poza tym: i PyPI
  2. Biblioteka python-shell: Podróż, którą przeszedłem, okazała się fascynującą wyprawą w przeszłość i cieszę się, że w końcu znalazłem rozwiązanie. Poza tym: i PyPI
  3. Kanał Telegram aktualizacje bibliotek

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. Wśród zmian — wsparcie dla autouzupełniania komend i komendy dir(Shell), uruchamianie komend z identyfikatorem niezgodnym z Pythonem, poprawki błędów.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster