Od jakiegoś czasu chciałem zrobić wpis bardziej w stylu programistycznych blogów, jakie chętnie czytam. Ale bez nurkowania w głębiny. Skupiający się zamiast tego na czymś podstawowym, co często bierze się za pewnik.
Taki wpis, żeby była eksploracja. Radosne zaspokajanie ciekawości poznawczej. W stylu piosenkarki Björk rozbierającej telewizor na części.

Na początek pomyślałem, że zajrzę w bebechy programiku yt-dlp. Napisanego w języku Python, który najbardziej mi leży.

Ale ostatecznie wyszedłem poza ten konkretny program. Odciągnął mnie od niego ogólniejszy temat „skryptów wejściowych”. Tego, co pozwala wzywać programy konsolowe z dowolnej części systemu.

Odkryłem, jak wygląda ich droga od twórców do użytkowników. Przy okazji mogłem wprawić się w eksploracji z użyciem programów konsolowych.

Zapraszam! Znajomość Pythona nie jest konieczna, bo cała sprawa ma w sobie więcej z łamigłówki logicznej. Poza tym będę wszystko wyjaśniał.

Uwaga

Pokazuję tu przykłady z aplikacji Termux na Androida oraz z domyślnej konsoli systemu Linux Mint. Na systemach z Windowsem (i być może MacOS, nie wiem) niektóre przykłady wpisywane w konsolę by nie zadziałały.

Spis treści

Początek: yt-dlp

Wszystko zaczęło się od yt-dlp. To bardzo przydatny programik do pobierania multimediów z różnych stron (nie tylko z YouTube’a). Już nieraz się nad nim rozpływałem na tym blogu.

W pierwotnej postaci jest to program konsolowy. Używa się go przez wpisywanie konkretnych poleceń tekstowych w konsolę. Jakaś jest zwykle domyślnie zainstalowana na komputerach osobistych.

Analogia godna współczesności? Programy konsolowe są jak ChatGPT bez lukru.
Zamiast pytań przyjmują polecenia. Zamiast kwieciście odpowiadać, robią dobrą robotę. Ewentualnie wprost piszą, że nas nie zrozumiały, zamiast zmyślać bzdurki.

Używając yt-dlp, można na przykład pobrać piosenkę z którejś ze wspieranych stron. Wystarczy skopiować link do niej i wpisać w konsolę:

yt-dlp -f bestaudio LINK

…A piosenka po chwili trafi na dysk. Albo na jego smartfonowy odpowiednik.

Smartfonowy, bo tego konkretnego programu można używać również na smartfonach z systemem Android. Najpierw pobiera się apkę Termux, przez nią język programowania Python, zaś przez narzędzia Pythona – yt-dlp. Wszystko szybkie, zwięzłe i proste.

„Wołaj, a przybędę”

W konsoli po lewej stronie zazwyczaj wyświetla się folder, w jakim obecnie jesteśmy (i tak, również tylda, ~, to skrót odnoszący się do folderu. Domowego).

Po tych folderach można się poruszać. Wpisać na przykład polecenie cd ŚCIEŻKA, żeby przejść do folderu o podanej ścieżce (jeśli istnieje). Mikrociekawostka: cd to skrót od change directory.

W tym miejscu ukazuje się ciekawa właściwość yt-dlp wewnątrz Termuksa. Mianowicie: w jakim folderze by się nie było, można przywołać ten program taką samą komendą.

Dwa zrzuty ekranu pokazujące aplikację Termux i polecenie wywołujące yt-dlp przywołane z dwóch różnych folderów.

Konsola apki Termux. Na zielono ścieżki do folderów, w których akurat byłem.

W przypadku yt-dlp w Termuksie jest to o tyle istotne, że pobierane pliki będą zapisywane zawsze do tego folderu, w którym obecnie jesteśmy.
Gdyby je pobierać do domyślnego folderu (który jest w prywatnej części Termuksa), to inne aplikacje, jak odtwarzacze multimediów, nie miałyby do nich dostępu.

Ale wracając do kwestii dostępności z każdego folderu – ten ułatwiacz życia jest bardzo powszechny w świecie programów konsolowych. Jak to działa?

Kulisy dostępności

Ogólnodostępność wynika z tego, że wiele systemów ma swoje foldery specjalne, w których domyślnie wypatruje programów.

Tak jak przeciętny obywatel wie, że najłatwiej znaleźć policjantów na komendzie, a lekarzy w szpitalu (choć oczywiście trafiają się też poza nim) – tak samo konsola/Python wie, że swoich programów ma szukać w określonych folderach.

Żeby zobaczyć listę takich specjalnych lokalizacji, można wpisać w konsolę:

echo $PATH

Wyświetli się kilka ścieżek (na świeżym Termuksie – jedna). Jeśli jest więcej, to będą rozdzielane dwukropkami.

Za każdym razem, kiedy wpisuje się proste konsolowe polecenie, dajmy na to zmyślone abcd coś, konsola rozbija je na spacjach. Następnie szuka we wspomnianych folderach specjalnych pliku o takiej samej nazwie jak pierwszy człon. Czyli w tym przypadku abcd.

Idąc tym tokiem rozumowania: wpisując yt-dlp, odnoszę się do jakiegoś pliku o takiej nazwie. I powinien być w tym jedynym folderze specjalnym, jaki mi pokazuje na Termuksie powyższe polecenie.

Ale to domysły. Jak sprawdzić to dokładniej: „jaki program odpowiada poleceniu yt-dlp?”. Albo, uogólniając: „jak poznać ścieżkę wołanego programu?”.

Znalezienie pliku

Można zapytać internetów. Podczas wyszukiwania warto podać nazwę systemu, bo różnią się między sobą. Ja używałem Termuksa, ale wiem, że to konsola w stylu systemu Linux.
Ostatecznie wpisałem w wyszukiwarkę DuckDuckGo: linux checking program path. I znalazłem odpowiedź z niezawodnego forum StackExchange.

Ludzie na forum napisali, że ścieżkę ujawni program konsolowy which albo type.
Przy tym pierwszym Termux mówił, że nie jest zainstalowany (ale podpowiadał gotowe polecenie instalujące). Drugi od razu śmigał.

Po wpisaniu:

type yt-dlp

Wyświetliło mi pełną ścieżkę do pliku:

Pojedyncza ścieżka do pliku, która pojawia się po wpisaniy komendy type yt-dlp w Termuksa

Dla przypomnienia: tak to wygląda wewnątrz Termuksa, na komputerze byłaby inna ścieżka.
Co ciekawe, com.termux to folder – jeśli ktoś się przyzwyczaił, że kropki są tylko od rozszerzeń plików, to się może zaskoczyć.

Wnętrze pliku

Znalazłem plik, pozostało do niego zajrzeć. Termux czy nie – mogłem łatwo zaznaczyć ścieżkę i ją skopiować. Pozostało ją wrzucić do innego programu konsolowego, który odczyta plik. Wybrałem do tego less:

less ŚCIEŻKA_DO_PLIKU

Ogromna zaleta tego programu: wyświetla większe pliki „na raty”, kawałek po kawałku.
W tym przypadku akurat mi to nie pomogło, bo plik był zwięzły.

Porada

Aby wyjść z programu less, wystarczy nacisnąć przycisk Q na klawiaturze. Dopóki tego nie odkryłem, pierwsze próby wyjścia były chaotyczne.

Od tego miejsca będzie odrobina kodu. Ale właściwie nie trzeba nawet rozumieć, o co w nim chodzi, można potraktować całość jak logiczną łamigłówkę.

Okazuje się, że plik o nazwie yt-dlp (bez żadnego rozszerzenia) to skrypt Pythona. To on się uruchamia, gdy wpisze się w konsolę yt-dlp. A oto cała jego zawartość:

#!/data/data/com.termux/files/usr/bin/python3.11
# -*- coding: utf-8 -*-
import re
import sys
from yt_dlp import main
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(main())

Streszczając: ten skrypt to „uruchamiacz”. Bierze (importuje) funkcję z jakiegoś innego skryptu, który nazywa się yt_dlp. I ją aktywuje.

A skąd Python wie, gdzie szukać tych pozostałych skryptów? No cóż, też ma swoje specjalne foldery. Analogiczne do tych systemowych, o których wcześniej wspomniałem.

Nazewnictwo

Gdy pojawiają się skrypty wołające inne skrypty, to łatwo się pogubić. Dlatego na czas reszty wpisu przyjmę pewną konwencję.
Skrypty takie jak ten wyżej – umieszczone w specjalnym folderze i uruchamiające coś innego – będę nazywał startowymi albo wejściowymi. Te, które są przez nie wołane, będę z kolei nazywał importowanymi.
Inne przydatne pojęcie to moduł. Tu: zestaw powiązanych ze sobą skryptów Pythona, często publikowanych jako całość.

Trendy wśród skryptów wejściowych

W tym momencie na pewien czas kończy się wątek konkretnego yt-dlp, a zaczyna ogólniejszy, związany ze skryptami Pythona.

Po poznaniu skryptu-uruchamiacza od yt-dlp, postanowiłem sprawdzić z ciekawości, jak wyglądają jego odpowiedniki dla innych modułów. W moim folderze szybkiego dostępu było bowiem więcej poinstalowanych skryptów.

Dla wygody użyłem oprócz Termuksa laptopa z lekko wiekowym systemem Linux Mint. Zerknąłem do folderu .local/bin w folderze domowym, który również jest folderem specjalnym na różne programy, przypisanym do konkretnego użytkownika.

Ciekawostka

Słowo bin (które ciągle uparcie kojarzy mi się ze śmietnikiem) to skrót od binary.
Czasem określa się tak pliki nieczytelne dla człowieka, w odróżnieniu od tekstowych. A tymczasem większość plików, jakie znalazłem w tym folderze, zawierała wyłącznie czytelny tekst. Tak jakoś wyszło, że częściej pobieram skrypty.
Ale w sumie za kulisami i tak każdy plik to zera i jedynki, więc nie będę się wykłócał :smile:

Treść plików ze specjalnego folderu odczytałem krótkim, skleconym na kolanie skryptem.

Spodziewałem się pewnej różnorodności. Myślałem, że każda osoba publikująca swoje moduły Pythona tworzy również jakiś własny skrypt startowy. A tymczasem po sprawdzeniu innych skryptów odkryłem, że wiele z nich było niemal identycznych.

Rzuciło to nowe światło na całą sprawę. Przyszło mi na myśl, że skrypty są tworzone przez komputer, z jakiegoś szablonu.
Żeby go poznać, postanowiłem porównać skrypty i spróbować w nich wyróżnić części zmienne oraz niezmienne (szablonowe).

Różnice między skryptami

Oto zawartość kilku wybranych skryptów startowych (można otworzyć w nowej karcie, żeby powiększyć):

Zestawienie czterech skryptów Pythona, pochodzących z pakietów: yt-dlp, pip, mss oraz chardetect. Części powtarzalne w kolorze szarym, ważne różnice wyróżnione kolorem

Najpierw odhaczę różnice, które uznałem za drobniejsze i zignorowałem, zostając przy istotniejszych:

  • Różnice w liczbie pustych linijek.
    Ta różnica jest ściśle skorelowana z inną, o której za moment.

  • Różnice w linijce numer jeden.

    To ona mówi, jakiego Pythona użyć (a ściślej – jakiego interpretera, programu odczytującego kod Pythona).
    Inną miałem na Termuksie, inną na laptopie. Ale ogólnie: linijka zawsze wskazywała tego samego Pythona, którym zainstalowałem cały skrypt.

  • Różnice w ostatniej linijce.

    Jej treść to sys.exit(FUNKCJA()), a ta FUNKCJA różniła się między skryptami.
    Ale zawsze odnosiła się do funkcji importowanej parę linijek wyżej. Zatem nawet jeśli nie ma powtarzalności tekstu, jest pełna powtarzalność zasady działania.

Po odsianiu drobnicy zostały dwie ciekawe rzeczy do wyjaśnienia.

Zagadki linijki importującej

Głównym źródłem różnic wydaje się być linijka, w której dochodzi do wzięcia jakiejś funkcji z innego skryptu stworzonego przez autorów. Każdy skrypt robi to inaczej:

from chardet.cli.chardetect import main
from pip._internal.cli.main import main
from yt_dlp import main
from mss.__main__ import main

Kolorami wyróżniłem części zmienne, reszta to niezmienniki.

Gdybym miał tylko te moduły, to mógłbym pomyśleć, że również nazwa wyciąganej funkcji (main) zawsze jest taka sama. Ale nie! Parę nielicznych modułów, jak tld, wyłamuje się z tej konwencji:

from tld.utils import update_tld_names_cli

Zagadka znaku zapytania

Mniejsza, ale tym ciekawsza różnica pojawia się w linijce znajdującej (po czym usuwającej) końcówki nazw plików funkcją re.sub.
W kilku modułach, jak pip3, chardetect i kilku innych, pojawiał się znak zapytania nieobecny w pozostałych:

r'(-script\.pyw?|\.exe)?$'
r'(-script\.pyw|\.exe)?$'

Ograniczyłem się tutaj jedynie do fragmentu linijki, reszta była bez zmian.

Znak zapytania to część elastycznych regułek szukających. Sprawia, że rzecz stojąca przed nim staje się opcjonalna. Może być, może jej nie być. Ten wyróżniony odnosi się do literki w.
W praktyce jedna wersja skryptu będzie usuwała z nazwy aktywnego programu (Pythona) jedynie końcówki -script.pyw.exe, druga ponadto -script.py.

Ten znak w oglądanych przeze mnie skryptach zawsze występował razem z kilkoma pustymi linijkami (ilustracja wyżej). Dlatego w myślach wydzieliłem sobie dwie kategorie skryptów: „linijki i znak zapytania” oraz „zwięzłe, bez znaku”.

Znalezienie części zmiennej

Co wspólnego miały ze sobą różne skrypty, które sprawdzałem? To, że powstały po zainstalowaniu modułu programem pip. Wziąłem swego czasu i wpisałem w konsolę:

pip install NAZWA_MODUŁU

Dokładniej rzecz biorąc, na komputerze musiałem wpisać pip3, bo miałem domyślnie dwie wersje Pythona. Ale to detal.

W ten sposób zdobyłem yt-dlp. Innym razem mss od robienia screenshotów, też pokazany powyżej. W każdym z tych przypadków do folderu specjalnego bin trafił szablonowy skrypt uruchamiający.

Pomyślałem, że rozwiązania zagadki trzeba poszukać u źródła. Wewnątrz PIP-a, czyli tego pythonowego instalatora.

Co się dzieje, kiedy używam powyższego polecenia?
Ano to, że pip łączy się z PyPI – centralną bazą, do której ludzie mogą wrzucać swoje własne moduły Pythona. PIP sprawdza, czy ma w tej bazie moduł o wpisanej przeze mnie nazwie. Jeśli tak, to go pobiera.

Do plików, które pobiera PIP, można również dobrać się ręcznie. Wejść na stronę konkretnego modułu (tu: yt-dlp), kliknąć zakładkę Download Files. Pod nagłówkiem Built Distribution (sugerującym, że to wersja dla użytkowników) znalazłem plik z rozszerzeniem .whl.

Pobrałem pliki dla modułów yt-dlp oraz mss. Jeśli to na ich podstawie instalator tworzył skrypty wejściowe, to gdzieś powinny w nich być odpowiednie informacje.

Wnętrze plików WHL

Pobrany plik .whl (skrót od wheel) to tak naprawdę archiwum ZIP. Jak wiele innych plików, po których nie każdy by się tego spodziewał (dokumenty pakietu Office, aplikacje na Androida…).
W każdym razie mogłem go rozpakować. Na Linuksie – bez zmian rozszerzenia, po prostu prawe kliknięcie i wybranie odpowiedniej opcji.

Pierwsza myśl – poszukam charakterystycznych słów ze skryptu startowego i zobaczę, czy gdzieś je znajdę w rozpakowanym folderze. Być może wszyscy twórcy osobiście załączają szablonowe skrypty startowe, a instalator jedynie je kopiuje w odpowiednie miejsce?

Do przeszukiwania zawartości folderów idealnie się nadaje programik grep, który w odpowiednim trybie odwiedza każdy plik po kolei:

grep -r -F "SZUKANY_TEKST" ROZPAKOWANY_FOLDER

Argument -r po to, żeby przeszukać wszystkie pliki oraz podfoldery we wskazanym folderze;
-F po to, żeby nie traktowało żadnych znaków jak znaków specjalnych.

Najpierw poszukałem tekstu sys.exit. Części niby szablonowej, ale z drugiej strony na tym etapie dopuszczałem myśl, że każdy moduł trzyma gdzieś własny szablon.
Ale grep niczego nie znalazł. Ani w yt-dlp, ani w paru innych.

W związku z tym, zamiast szukać części szablonowej, postanowiłem poszukać części zmiennych, różniących się między skryptami. Jak nazwy importowanych modułów i funkcji.

W przypadku MSS-a linijka ze skryptu startowego brzmiała:

from mss.__main__ import main

Drugi zmienny człon, main, brzmiał dla mnie ciut zbyt ogólnie. Dlatego w rozpakowanym pliku WHL poszukałem pierwszego:

grep -r -F "mss.__main__" mss-9.0.1-py3-none-any.whl_FILES
Porada

Nie musiałem wpisywać ani kopiować pełnej nazwy rozpakowanego folderu. Wystarczyło wpisać pierwsze literki i nacisnąć klawisz Tab.
Czasem to wystarczy, ale u mnie były dwie rzeczy o zbliżonych nazwach: WHL oraz wypakowany z niego folder. Dlatego dopisałem literkę F, żeby pasował już tylko folder (mający w nazwie FILES). I znów Tab, żeby dopełnić resztę nazwy.

I eureka! Grep znalazł szukany tekst w podfolderze zakończonym na dist-info, w pliku o nazwie entry_points.txt. Zresztą cały plik zawierał tylko to:

[console_scripts]
mss = mss.__main__:main

Zgadzały się: nazwa skryptu startowego, nazwa importowanego, nazwa konkretnej funkcji (po dwukropku), cel tego wszystkiego („skrypty konsolowe” w nawiasach kwadratowych). Miałem mocne podejrzenie, że to na podstawie tego pliku instalator uzupełnia szablon skryptów startowych.

…Ale skąd brał sam szablon?

Znalezienie części szablonowej

Na logikę: jeśli instalowane pliki zawierają tylko informacje do umieszczenia w szablonie, to sam szablon powinien tkwić na moim komputerze, wewnątrz instalatora. Może samego PIP-a. Może innego modułu, z którego korzysta.

Tyle że nie wiedziałem, gdzie szukać, zaś mój własny system zawierał więcej poinstalowanych modułów Pythona, które mogłyby wchodzić w drogę.
Dlatego postanowiłem stworzyć jakieś czyste środowisko do eksperymentów, w którym wyizoluję sobie instalatora i to jego przeszukam.

W Pythonie taką możliwość zapewniają środowiska wirtualne.
To coś w rodzaju odgrodzonej części systemu. Pozwalają eksperymentować z różnymi modułami Pythona, instalować je i usuwać, ale bez wpływu na główne pliki na komputerze.

W jakimś folderze X stworzyłem folder skrypty-testy, po czym uruchomiłem w tym samym folderze X konsolę i wpisałem:

python3.7 -m venv skrypty-testy

Użyłem tu nieco starszej wersji Pythona (3.7), jaką u siebie miałem. Jak się potem okaże, ma to istotne znaczenie.

To polecenie wypełniło folder kilkoma podstawowymi modułami Pythona. Również tymi odpowiedzialnymi za instalację, jak PIP.

Środowisko wirtualne, choć ma nietypowe możliwości, jest dla systemu najzwyklejszym folderem skrypty-testy, całkiem publicznym. Wiedząc, że w środku mam instalatory, mogłem poszukać tekstu typowego dla szablonów. Postawiłem na fragment reguły ucinającej końcówki:

grep -rF "re.sub(r'(-script\.py" skrypty-testy

Stare szablony

Wynik wyszukania? Wzmianka o dwóch plikach binarnych, których nie odczytało, oraz kilka tekstowych:

skrypty-testy/lib/python3.7/site-packages/pip/wheel.py
skrypty-testy/lib/python3.7/site-packages/setuptools/command/easy_install.py
skrypty-testy/bin/pip3.7
skrypty-testy/bin/pip
skrypty-testy/bin/pip3
skrypty-testy/bin/easy_install
skrypty-testy/bin/easy_install-3.7

Gdybym chciał celowo pominąć pliki binarne, nie dostając o nich nawet wzmianki, to mógłbym dopisać -I po słowie grep. Albo zwięźlej: zmienić -rF na -rFI.

Pliki, które zawierały w swojej ścieżce bin, same były skryptami startowymi. Stworzonymi z szablonu, więc nie mogły być szablonem. Olałem je.

Skrypt easy_install.py zawierał klasę ScriptWriter, która miała swój szablon… Tyle że nieco zbyt długi. Zawierał szereg rzeczy nieobecnych w moich skryptach startowych.

Za to plik wheel.py był strzałem w dziesiątkę! Już sama nazwa sugeruje, że odpowiada za pliki WHL, do tego jego zawartość idealnie pasowała do niektórych skryptów startowych (nie zawierał jedynie pierwszej linijki, dodanej w innym miejscu):

Fragment skryptu wheel.py, zawierający tekst szablonu

…Ale ten szablon nie wyjaśniał wszystkiego. Pasował do skryptów rozstrzelonych na więcej linijek, z dodatkowym znakiem zapytania w regułce.
Nie powstał z niego natomiast ten drugi, zwięzły typ skryptów startowych. Co więcej: w żadnym ze znalezionych wyżej plików nie było pasującego szablonu.

Nowsze szablony

Korzystałem w środowisku wirtualnym ze starszej wersji Pythona, która siłą rzeczy korzystała ze starszych wersji instalatorów. Uznałem, że je zaktualizuję do najnowszych wersji i zobaczę, czy to coś zmienia.

Żeby zaktualizować coś wewnątrz środowiska wirtualnego, musiałem w nie „wejść”. Będąc w tym samym folderze, w którym tkwił folder skrypty-testy, wpisałem:

source skrypty-testy/bin/activate

Programik konsolowy source służy do uruchamiania innych programów. Odnoszę się tu do programu activate, zakopanego w folderze skrypty-testy, podfolderze bin.

Mogłem łatwo poznać, że środowisko się włączyło, bo na początku linijki, między nawiasami, pojawiło się skrypty-testy. Nazwa aktualnego środowiska. Póki jestem w tym trybie, moduły Pythona są instalowane wyłącznie w jego obrębie.

Żeby zaktualizować rzeczy związane z instalacją, wpisałem:

pip3 install -U pip
pip3 install -U setuptools

Potem powtórzyłem polecenie do Grepa, jakim wcześniej znalazłem starszy szablon.
Ponownie znalazło mi dwa potencjalnie ciekawe skrypty. Jeden był tym samym easy-install.py co poprzednio, który miał zbyt długi szablon.
Drugi znaleziony skrypt różnił się natomiast od tego ze starszego instalatora:

skrypty-testy/lib/python3.7/site-packages/pip/_vendor/distlib/scripts.py

Zajrzałem do niego… I bingo! Był tam szablon pasujący do drugiego rodzaju skryptów startowych. Tych zwięzłych i bez znaku zapytania.

Fragment skryptu Pythona, pokazujący szablon skryptu z miejscami do uzupełnienia

Podsumowanie wątku

Na tym etapie byłem już przekonany, że rozgryzłem instalację skryptów startowych przez Pythona:

  • instalator zdobywa plik WHL z centralnej bazy Pythona,
  • znajduje w pakiecie plik entry_points.txt,
  • odczytuje z niego, że powinien stworzyć skrypt startowy w folderze ogólnodostępnym,
  • uzupełnia szablon ze swoich bebechów szczegółami z pliku,
  • tworzy skrypt.

A różnice między wyglądem finalnych skryptów? Wynikały z tego, że sama ekipa tworząca Pythona na przestrzeni wersji zmieniała swoje szablony.

A ja aktualizowałem instalatory, nie zmieniając przy tym niektórych rzeczy, które już wcześniej zainstalowałem. W folderze bin wymieszały mi się przez to skrypty startowe w wariantach starszych i nowszych.

Ciekawostka

Pojawia się tu również ciekawy wątek poboczny, może nawet związany z prywatnością (a na pewno ze swoistym fingerprintingiem) – na podstawie budowy skryptów startowych można ustalić, jaką wersją narzędzi Pythona były instalowane.
W ten sposób dałoby się np. oszacować, z jakiego okresu pochodzi folder skopiowany z czyjegoś komputera, jeśli nigdzie nie ma daty. Brzmi jak bzdet… Ale zdarzało się kiedyś, że rozpoznano podróbki po użytych czcionkach. Oszuści twierdzili, że jakiś dokument jest stary, ale jego czcionka była współczesna.

Od strony twórców

Główna zagadka wyjaśniona, ale czułem niedosyt. Sama instalacja przez użytkowników to dopiero druga połowa drogi. A jak to wygląda między niezależnymi twórcami a bazą PyPI? Czy tworzą plik entry_points.txt oraz kilka innych ręcznie, przed dodaniem do pliku WHL?

Wyszukałem w sieci entry_points.txt oraz console scripts.
Wyskoczyła mi stronka Pythona opisująca konwencje związane ze skryptami startowymi. Poznałem też ich oficjalną nazwę – entry points (dosł. punkty wejściowe).

Strona wspomniała również, że twórcy nie dodają plików ręcznie. Zostawiają wytyczne dla programu łączącego ich skrypty w plik WHL, po czym to on tworzy pliki tekstowe.

Te wytyczne mogą umieszczać w kilku miejscach. Programik yt-dlp postawił na plik pyproject.toml. W przypadku ich projektu dość obszerny.
Tam (na dzień dzisiejszy – w linijkach 78 i 79 – znajdują się instrukcje dotyczące skryptów startowych.

[project.scripts]
yt-dlp = "yt_dlp:main"

Podsumowując całą drogę skryptów startowych – od twórców po użytkowników – w formie schematu:

Schemat pokazujący trzy kolejne etapy połączone strzałkami. Na początku jest kod źródłowy, potem baza PyPI, a na koniec komputer użytkownika.

Czego się nauczyłem

Podczas eksploracji trochę się dowiedziałem na temat tego, jak udostępniać moduły Pythona. Tak, żeby dało się ich używać jako programów konsolowych, reagujących na wezwanie z każdego folderu. To mi się przyda!

Okazuje się, że to nie takie trudne. Wiele sprowadza się do wpisania paru linijek w plik tekstowy.
Resztę bierze na siebie najpierw program pakujący wszystko do jednego pliku WHL. A potem – po stronie użytkowników – wszystko załatwia instalator, czyli PIP. Oraz jego skrypty towarzyszące.

Zapewne twórcy każdego języka programowania stają przed podobnymi kwestiami – „jak ułatwić tworzenie programów konsolowych w naszym języku?”, „czy mieć centralną bazę skryptów od różnych ludzi?”.

Nie zdziwiłbym się, gdyby wiele projektów dryfowało ku temu samemu rozwiązaniu, zwięzłym plikom konfiguracyjnym. Choć skupiłem się na Pythonie, zapewne wchłonąłem w głowę też parę ogólnych konwencji.

A czy nie mogłem po prostu zacząć od dokumentacji, zamiast odkrywać koło (WHL) na nowo? Pewnie mogłem. Ale nie miałbym z tego takiej frajdy :smile: