Cum să scrieți propriile pachete Python

Prezentare generală

Python este un limbaj de programare minunat și multe altele. Unul dintre punctele sale cele mai slabe este ambalarea. Acesta este un fapt bine cunoscut în comunitate. Instalarea, importarea, utilizarea și crearea pachetelor s-au îmbunătățit de-a lungul anilor, dar acestea nu sunt în paralel cu limbi mai noi, cum ar fi Go și Rust, care ar putea învăța multe din luptele lui Python și alte limbi mai mature. 

În acest tutorial, veți afla tot ce trebuie să știți pentru a construi și a împărtăși propriile pachete. Pentru informații generale despre pachetele Python, vă rugăm să citiți Cum se utilizează pachetele Python.

Ambalarea unui proiect

Ambalarea unui proiect este procesul prin care luați un set coerent de module Python și, eventual, alte fișiere și le puneți într-o structură care poate fi utilizată cu ușurință. Există diferite lucruri pe care trebuie să le iei în considerare, cum ar fi dependențele de alte pachete, structura internă (subpachete), versiunea, publicul țintă și forma pachetului (sursă și / sau binar).

Exemplu

Să începem cu un exemplu rapid. Pachetul conman este un pachet pentru gestionarea configurației. Acesta acceptă mai multe formate de fișiere, precum și configurarea distribuită utilizând etcd.

Conținutul unui pachet este de obicei stocat într-un singur director (deși este comun să se împartă sub-pachete în mai multe directoare) și, uneori, ca în acest caz, în propriul depozit git. 

Directorul rădăcină conține diferite fișiere de configurare (setup.py este obligatorie și cea mai importantă), iar codul de pachete propriu-zis este de obicei într-un subdirector al cărui nume este numele pachetului și, în mod ideal, un director de teste. Iată cum arată "conman":

> copac. ├── LICENȚĂ ├── MANIFEST.in ├── README.md ├── escrocul │ ├── __init__.py │ ├── __pycache__ │ ├── conman_base.py │ ├── conman_etcd.py │ └── conman_file.py ├── requirements.txt ├── setup.cfg ├── setup.py ├── test requirements.txt teste ├── │ ├── __pycache__ │ ├── conman_etcd_test.py │ ├── conman_file_test .py │ └───────────────┘

Să aruncăm o privire rapidă la setup.py fişier. Acesta importă două funcții din pachetul de instrumente de configurare: înființat() și find_packages (). Apoi îl cheamă înființat() funcția și utilizările find_packages () pentru unul dintre parametri.

de la setuptools import setup, find_packages setup (nume = 'conman', version = "0.3", url = "https://github.com/the-gigi/conman", license = "MIT", author = "Gigi Sayfan" , author_email = "[email protected]", descriere = "Gestionați fișierele de configurare", pachete = find_packages (exclude = [ 'teste']), long_description = deschis ( 'README.md'). citește (), zip_safe = False, setup_requires = ['nose> = 1.0'], test_suite = "nose.collector") 

Acest lucru este destul de normal. In timp ce setup.py fișierul este un fișier Python obișnuit și puteți să faceți tot ce doriți în el, sarcina sa principală de ao apela înființat() funcția cu parametrii corespunzători, deoarece va fi invocată de diverse instrumente într-un mod standard atunci când instalați pachetul. Voi trece detaliile în secțiunea următoare.

Fișierele de configurare

Pe lângă setup.py, există și alte câteva fișiere de configurare opționale care pot apărea aici și pot servi scopuri diferite.

Setup.py

înființat() funcția ia un număr mare de argumente numite pentru a controla multe aspecte ale instalării pachetelor, precum și pentru a rula diverse comenzi. Multe argumente specifică metadatele utilizate pentru căutarea și filtrarea atunci când încărcați pachetul într-un depozit.

  • nume: numele pachetului dvs. (și modul în care acesta va fi listat pe PYPI)
  • versiune: aceasta este esențială pentru menținerea unei gestionări corecte a dependenței
  • url: adresa URL a pachetului dvs., de regulă GitHub sau poate adresa readthedocs
  • pachete: lista de subpachete care trebuie incluse; find_packages () ajută aici
  • setup_requires: aici specificați dependențele
  • test_suite: ce unealtă trebuie să funcționeze la ora testului

descriere lungă este setat aici la conținutul README.md fișier, care este o bună practică pentru a avea o singură sursă de adevăr.

Setup.cfg

Fișierul setup.py servește, de asemenea, o interfață de linie de comandă pentru a rula diverse comenzi. De exemplu, pentru a rula testele unității, puteți să tastați: testul python setup.py

test de funcționare care rulează egg_info scris conman.egg-info / PKG-INFO scris nume de nivel superior pentru conman.egg-info / top_level.txt scris dependency_links la conman.egg-info / dependency_links.txt lectură fișier manifest „conman.egg-info /SOURCES.txt "citirea template-ului manifest" MANIFEST.in "scrierea fișierului manifest" conman.egg-info / SOURCES.txt "executând build_ext test_add_bad_key (conman_etcd_test.ConManEtcdTest) ... ok test_add_good_key (conman_etcd_test.ConManEtcdTest) ... ok test_dictionary_access (conman_etcd_test.ConManEtcdTest ) ... OK test_initialization (conman_etcd_test.ConManEtcdTest) ... ok test_refresh (conman_etcd_test.ConManEtcdTest) ... ok test_add_config_file_from_env_var (conman_file_test.ConmanFileTest) ... ok test_add_config_file_simple_guess_file_type (conman_file_test.ConmanFileTest) ... ok test_add_config_file_simple_unknown_wrong_file_type (conman_file_test.ConmanFileTest) ... ok test_add_config_file_simple_with_file_type (conman_file_test.ConmanFileTest) ... ok test_add_config_file_simple_w rong_file_type (conman_file_test.ConmanFileTest) ... ok test_add_config_file_with_base_dir (conman_file_test.ConmanFileTest) ... ok test_dictionary_access (conman_file_test.ConmanFileTest) ... ok test_guess_file_type (conman_file_test.ConmanFileTest) ... OK test_init_no_files (conman_file_test.ConmanFileTest) ... OK test_init_some_bad_files (conman_file_test.ConmanFileTest) ... OK test_init_some_good_files ( conman_file_test.ConmanFileTest) ... ok -------------------------------------------- -------------------------- Au trecut 16 teste în 0.160s OK 

Setup.cfg este un fișier format ini care poate conține setări implicite pentru comenzi pe care le transmiteți setup.py. Aici, setup.cfg conține câteva opțiuni pentru nosetests (alergătorul nostru):

[nosetests] verbose = 1 nocapture = 1 

MANIFEST.in

Acest fișier conține fișiere care nu fac parte din directorul intern de pachete, dar totuși doriți să includeți. Acestea sunt de obicei cele Citește-mă dosarul, fișierul de licență și altele similare. Un fișier important este requirements.txt. Acest fișier este utilizat de pip pentru a instala alte pachete necesare.

Aici este conman MANIFEST.in fişier:

includ LICENȚĂ include README.md include requirements.txt

dependenţe

Puteți specifica dependențe atât în install_requires secțiune din setup.py și în a requirements.txt fişier. Pip va instala dependențe automate de la install_requires, dar nu de la requirements.txt fişier. Pentru a instala aceste cerințe, va trebui să o specificați în mod explicit când rulează pip: pip install -r requirements.txt.

install_requires este conceput pentru a specifica cerințe minime și mai abstracte la nivelul versiunii majore. Fișierul requirements.txt este pentru mai multe cerințe concrete, adesea cu versiuni minore fixate.

Aici este fișierul cerințelor conman. Puteți observa că toate versiunile sunt fixate, ceea ce înseamnă că pot fi afectate negativ dacă unul dintre aceste pachete se actualizează și introduce o schimbare care rupe conman.

PyYAML == 3,11 python-etcd == 0,4,3 urllib3 == 1,7 pyOpenSSL == 0.15.1 psutil == 4.0.0 șase == 1.7.3

Pinning vă oferă predictibilitate și liniște. Acest lucru este important mai ales dacă mulți oameni instalează pachetul în momente diferite. Fără fixare, fiecare persoană va primi o combinație diferită de versiuni de dependență bazate pe momentul în care au instalat-o. Dezavantajul legării este că, dacă nu țineți pasul cu dezvoltarea dependențelor, s-ar putea să fiți blocați pe o versiune veche, slabă și chiar vulnerabilă a unei anumite dependențe.

Am scris inițial conman în 2014 și nu am acordat prea multă atenție. Acum, pentru acest tutorial am îmbunătățit totul și au existat unele îmbunătățiri majore în întreaga bord pentru aproape fiecare dependență.

Distributii

Puteți crea o distribuție sursă sau o distribuție binară. Voi acoperi atât amândouă.

Distribuție sursă

Creați o distribuție sursă cu comanda: python setup.py sdist. Aici este ieșirea pentru conman:

> Python setup.py sdist rulează sdist care rulează egg_info scris conman.egg-info / PKG-INFO scris nume de nivel superior pentru conman.egg-info / top_level.txt scris dependency_links la conman.egg-info / dependency_links.txt lectură fișier manifest 'conman.egg-info / SOURCES.txt' citirea unui șablon manifest 'MANIFEST.in' scrierea fișierului manifest 'conman.egg-info / SOURCES.txt' avertisment: sdist: fișierul standard nu a fost găsit: ar trebui să aibă unul dintre README, README. rst, README.txt execută verificarea creării conman-0.3 creând conman-0.3 / conman creând conman-0.3 / conman.egg-info făcând legăturile greșite în conman-0.3 ... hard linking LICENȚĂ -> conman-0.3 hard linking MANIFEST.in -> conman-0.3 hard linking README.md -> conman-0.3 hard linking requirements.txt -> conman-0.3 greu linking setup.cfg -> conman-0.3 greu linking setup.py -> conman-0.3 greu linking conman / __ init__.py -> conman-0.3 / conman greu de conectare conman / conman_base.py -> conman-0.3 / conman greu de conectare conman / conman_etcd.py -> conman-0.3 / conman hard linking conman / conman_fil e.py -> conman-0.3 / conman greu de legătură conman.egg-info / PKG-INFO -> conman-0.3 / conman.egg-info conman.egg-info / conman.egg- .cont-0.3 / conman.egg-info - conman-0.3 / conman.egg-info conman.egg-info / not-zip-safe greu care leagă conman.egg-info / top_level.txt -> conman-0,3 / conman.egg-info copiere setup.cfg -> conman-0.3 Scrierea conman-0.3 / setup.cfg crearea dist crearea arhivei tar eliminarea 'conman-0,3' (și totul sub el) 

După cum puteți vedea, am un avertisment despre faptul că am dispărut un fișier README cu unul dintre prefixele standard, pentru că îmi place Markdown, deci am un "README.md". În afară de aceasta, toate fișierele sursă de pachete au fost incluse și fișierele suplimentare. Apoi, o mulțime de metadate a fost creat în conman.egg-info director. În final, a sunat o arhivă comprimată de tar conman-0.3.tar.gz este creat și pus într - un dist subdirector.

Instalarea acestui pachet va necesita un pas de construire (chiar dacă este Python pur). Puteți să-l instalați folosind pip în mod normal, doar trecând calea către pachet. De exemplu:

pip install dist / conman-0.3.tar.gz Prelucrare ./dist/conman-0.3.tar.gz Instalarea pachetelor colectate: conman Running setup.py install for conman ... done Instalat cu succes conman-0.3

Conman a fost instalat în pachete de site-uri și poate fi importat ca orice alt pachet:

import conman conman .__ file__ '/Users/gigi/.virtualenvs/conman/lib/python2.7/site-packages/conman/__init__.pyc'

Roți

Roțile sunt o modalitate relativ nouă de a împacheta codul Python și, eventual, extensiile C. Ele înlocuiesc formatul ouălor. Există mai multe tipuri de roți: roți pure Python, roți de platformă și roți universale. Roțile pure Python sunt pachete cum ar fi conman care nu au nici un cod de extensie C. 

Roțile platformei au un cod de extensie C. Roțile universale sunt roțile Python pure care sunt compatibile atât cu Python 2 cât și cu Python 3 cu aceeași bază de cod (acestea nu necesită nici 2to3). Dacă aveți un pachet Python pur și doriți ca pachetul dvs. să suporte atât Python 2 cât și Python 3 (devine din ce în ce mai important), puteți construi o singură construcție universală în loc de o roată pentru Python 2 și o roată pentru Python 3. 

Dacă pachetul dvs. are un cod de extensie C, trebuie să construiți o roată de platformă pentru fiecare platformă. Beneficiul enorm al roților, în special pentru pachetele cu extensii C, este că nu este nevoie să fie disponibile biblioteci de compilatoare și suport pe mașina țintă. Roata conține deja un pachet construit. Deci știi că nu va reuși să construiască și este mult mai rapid să instalezi, deoarece este literalmente doar o copie. Oamenii care folosesc biblioteci științifice precum Numpy și Pandas pot aprecia cu adevărat acest lucru, deoarece instalarea unor astfel de pachete este folosită pentru o perioadă lungă de timp și s-ar putea să fi eșuat dacă o bibliotecă lipsește sau compilatorul nu a fost configurat corespunzător.

Comanda pentru a construi roți pure sau platformă este: python setup.py bdist_wheel.

Instrumente de configurare - motorul care furnizează înființat() funcție - va detecta automat dacă este necesară o roată pură sau platformă.

care rulează bdist_wheel rulează construirea rulează build_py construiește construi creează crearea / lib crearea build / lib / conman copierea conman / __ init__.py -> build / lib / conman copierea conman / conman_base.py -> build / lib / conman copierea conman / conman_etcd.py -> build / lib / conman copierea conman / conman_file.py -> construirea / lib / conman instalarea pentru a construi / bdist.macosx-10.9-x86_64 / bdist.macosx-10.9-x86_64 / router creare build / bdist.macosx-10.9-x86_64 / router / conman copiere build / lib / conman / __ init__.py -> build / bdist.macosx-10.9-x86_64 / /lib/conman/conman_base.py -> construi / bdist.macosx-10.9-x86_64 / router / conman copiere build / lib / conman / conman_etcd.py -> build / bdist.macosx-10.9-x86_64 / /lib/conman/conman_file.py -> build / bdist.macosx-10.9-x86_64 / router / conman care rulează install_egg_info rulează egg_info creare conman.egg-info scris conman.egg-info / PKG-INFO scris top-level nam es la conman.egg-info / top_level.txt scrierea dependency_links la conman.egg-info / dependency_links.txt scrierea fișierului manifestului 'conman.egg-info / SOURCES.txt' citirea fișierului manifest 'conman.egg-info / SOURCES.txt 'citește șablonul manifest' MANIFEST.in 'scriind fișierul manifest' conman.egg-info / SOURCES.txt 'Copierea conman.egg-info pentru a construi / bdist.macosx-10.9-x86_64 / wheel / conman-0.3-py2.7. informații despre ouă care rulează install_scripts creare build / bdist.macosx-10.9-x86_64 / wheel / conman-0.3.dist-info / WHEEL

Verificați dist , puteți vedea că a fost creată o roată Python pură:

ls-la dist dist / total 32-rw-r-r-- 1 gigi personal 5.5K Feb 29 07:57 conman-0.3-py2-none-any.whl -rw-r-r-- 4.4K Feb 28 23:33 conman-0.3.tar.gz

Numele "conman-0.3-py2-none-any.whl" are mai multe componente: numele pachetului, versiunea pachetului, versiunea Python, versiunea platformei și, în final, extensia "whl".

Pentru a crea pachete universale, trebuie doar să adăugați --universal, ca în python setup.py bdist_wheel --universal.

Roata rezultată este denumită "conman-0.3-py2.py3-none-any.whl".

Rețineți că este responsabilitatea dvs. să vă asigurați că codul dvs. funcționează atât în ​​Python 2, cât și în Python 3 dacă creați un pachet universal.

Concluzie

Scrierea propriilor pachete Python necesită o mulțime de instrumente, specificând o mulțime de metadate și gândindu-vă cu atenție cu privire la dependențele dvs. și la publicul țintă. Dar răsplata este minunată. 

Dacă scrieți codul util și îl ambalați în mod corespunzător, oamenii îl vor putea instala cu ușurință și vor beneficia de el.

Cod