Hoe een bibliotheek in Python te schrijven, verpakken en distribueren

Python is een geweldige programmeertaal, maar verpakking is een van de zwakste punten. Het is een bekend feit in de gemeenschap. Het installeren, importeren, gebruiken en maken van pakketten is in de loop der jaren veel verbeterd, maar het is nog steeds niet op een lijn met nieuwere talen als Go en Rust die veel hebben geleerd van de strijd van Python en andere volwassen talen. 

In deze tutorial leert u alles wat u moet weten over het schrijven, verpakken en distribueren van uw eigen pakketten. 

Hoe een Python-bibliotheek te schrijven

Een Python-bibliotheek is een samenhangende verzameling Python-modules die is georganiseerd als een Python-pakket. In het algemeen betekent dit dat alle modules onder dezelfde directory leven en dat deze directory zich op het Python-zoekpad bevindt. 

Laten we snel een klein Python 3-pakket schrijven en al deze concepten illustreren.

Het Pathologie Pakket

Python 3 heeft een uitstekend Path-object, wat een enorme verbetering is ten opzichte van de lastige os.path-module van Python 2. Maar het mist één cruciaal vermogen - het vinden van het pad van het huidige script. Dit is erg belangrijk wanneer u toegangsbestanden wilt zoeken die relatief zijn ten opzichte van het huidige script. 

In veel gevallen kan het script op elke locatie worden geïnstalleerd, dus u kunt geen absolute paden gebruiken en de werkdirectory kan op elke waarde worden ingesteld, dus u kunt geen relatief pad gebruiken. Als u een bestand in een subdirectory of bovenliggende map wilt openen, moet u de huidige scriptdirectory kunnen vinden. 

Hier is hoe je het doet in Python:

import pathlib script_dir = pathlib.Path (__ file __). parent.resolve ()

Om toegang te krijgen tot een bestand met de naam 'file.txt' in de submap 'data' van de map van het huidige script, kunt u de volgende code gebruiken: afdruk (open (str (script_dir / 'data / file.txt'). read ())

Met het pathologiepakket heb je een ingebouwde script_dir methode, en je gebruikt het als volgt:

van pathology.Path import script_dir print (open (str (script_dir () / 'data / file.txt'). lees ()) 

Ja, het is een mondvol. Het pathologiepakket is heel eenvoudig. Het ontleent zijn eigen padklasse uit pathlib's pad en voegt een statische toe script_dir () dat retourneert altijd het pad van het aanroepende script. 

Hier is de implementatie:

import pathlib import inspecteer class Path (type (pathlib.Path ())): @staticmethod def script_dir (): print (inspect.stack () [1] .bestandsnaam) p = pathlib.Path (inspect.stack () [1 ] .bestandsnaam) retourneert p.parent.resolve () 

Vanwege de platformonafhankelijke implementatie van pathlib.Path, je kunt er rechtstreeks van afleiden en moet afgeleid zijn van een specifieke subklasse (PosixPath of WindowsPath). De script-dir-resolutie gebruikt de module inspecteren om de beller en vervolgens het bestandsnaamattribuut te vinden.

Het pathologiepakket testen

Wanneer u iets schrijft dat meer is dan een wegwerpscript, moet u het testen. De pathologiemodule is geen uitzondering. Dit zijn de tests die het standaard testraamwerk voor eenheden gebruiken: 

import os import shutil from unittest import TestCase from pathology.path import Path path (TestCase): def test_script_dir (self): expected = os.path.abspath (os.path.dirname (__ file__)) actual = str (Path.script_dir ()) self.assertEqual (expected, actual) def test_file_access (self): script_dir = os.path.abspath (os.path.dirname (__ file__)) subdir = os.path.join (script_dir, 'test_data') if Path (subdir) .is_dir (): shutil.rmtree (subdir) os.makedirs (subdir) file_path = str (Path (subdir) / 'file.txt') content = '123' open (file_path, 'w'). write (inhoud) test_path = Path.script_dir () / subdir / 'file.txt' actual = open (str (testpad)). read () self.assertEqual (inhoud, actueel) 

Het Python-pad

Python-pakketten moeten ergens op het Python-zoekpad worden geïnstalleerd om door Python-modules te worden geïmporteerd. Het Python-zoekpad is een lijst met directory's en is altijd beschikbaar in sys.path. Dit is mijn huidige sys.path:

>>> print ('\ n'.join (sys.path)) /Users/gigi.sayfan/miniconda3/envs/py3/lib/python36.zip /Users/gigi.sayfan/miniconda3/envs/py3/lib/ python3.6 /Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/lib-dynload /Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/site-packages / Users / gigi.sayfan / miniconda3 / envs / PY3 / lib / python3.6 / site-packages / setuptools-27.2.0-py3.6.egg 

Merk op dat de eerste lege regel van de uitvoer de huidige map vertegenwoordigt, dus u kunt modules uit de huidige werkdirectory importeren, wat het ook is. U kunt direct mappen toevoegen aan of verwijderen uit sys.path. 

U kunt ook een definiëren PYTHONPATH omgevingsvariabele, en er zijn een paar andere manieren om het te besturen. De standaard website-pakketten is standaard inbegrepen, en dit is waar pakketten die u installeert met gebruik van pip gaan. 

Hoe een Python-bibliotheek te verpakken

Nu we onze code en tests hebben, kunnen we het allemaal in een juiste bibliotheek verpakken. Python biedt een eenvoudige manier via de setup-module. U maakt een bestand met de naam setup.py in de hoofdmap van uw pakket. Vervolgens voert u voor het maken van een brondistributie het volgende uit: python setup.py sdist

Als u een binaire distributie met de naam wiel wilt maken, voert u het volgende uit: python setup.py bdist_wheel

Hier is het setup.py-bestand van het pathologiepakket:

van setuptools import setup, find_packages setup (name = 'pathology', version = "0.1", url = "https://github.com/the-gigi/pathology", license = "MIT", author = "Gigi Sayfan" , author_email = "[email protected]", description = "Voeg static script_dir () toe aan Path", packages = find_packages (exclude = ['tests']), long_description = open ('README.md'). read (), zip_safe = False)

Het bevat veel metadata naast het item 'packages' dat de find_packages () functie geïmporteerd uit setuptools om subpakketten te vinden.

Laten we een brondistributie bouwen:

$ python setup.py sdist draait sdist draait egg_info maakt pathologie.egg-info schrijft pathology.egg-info / PKG-INFO schrijft afhankelijkheidslinks naar pathology.egg-info / dependency_links.txt schrijft namen op het hoogste niveau naar pathology.egg-info / top_level.txt schrijven manifestbestand 'pathology.egg-info / SOURCES.txt' lezen manifestbestand 'pathology.egg-info / SOURCES.txt' schrijven manifest bestand 'pathology.egg-info / SOURCES.txt' waarschuwing: sdist: standaard bestand niet gevonden: moet een van README, README.rst, README.txt hebben lopende controle maken van pathologie-0.1 aanmaken van pathologie-0.1 / pathologie maken van pathologie-0.1 / pathology.egg-info bestanden kopiëren naar pathologie-0.1 ... kopiëren van setup.py -> pathologie - 0.1 kopiëren pathologie / __ init__.py -> pathologie - 0.1 / pathologie kopiëren pathologie / path.py -> pathologie - 0.1 / pathologie kopiëren pathologie.egg-info / PKG-INFO -> pathologie-0.1 / pathology.egg -info kopiëren van pathologie.egg-info / SOURCES.txt -> pathologie-0.1 / pathology.egg-info copying pathology.egg-info / dependency_links.txt -> pathologie -0.1 / pathology.egg-info copying pathology.egg-info / not-zip-safe -> pathology-0.1 / pathology.egg-info copying pathology.egg-info / top_level.txt -> pathology-0.1 / pathology.egg -info Schrijven van pathologie-0.1 / setup.cfg maken van dist Maken van tar-archief verwijderen van 'pathologie-0.1' (en alles daaronder)

De waarschuwing is omdat ik een niet-standaard README.md-bestand heb gebruikt. Het is veilig om te negeren. Het resultaat is een tar-gzipped bestand onder de dist map:

$ ls -la dist in totaal 8 drwxr-xr-x 3 gigi.sayfan gigi.sayfan 102 apr 18 21:20. drwxr-xr-x 12 gigi.sayfan gigi.sayfan 408 18 apr. 21: 20 ... -rw-r - r - 1 gigi.sayfan gigi.sayfan 1223 18 apr. 21:20 pathology-0.1.tar.gz

En hier is een binaire distributie:

$ python setup.py bdist_wheel running bdist_wheel running build running build_py create build creating build / lib create build / lib / pathology copying pathology / __ init__.py -> build / lib / pathology copying pathology / path.py -> build / lib / pathology installeren om te bouwen / bdist.macosx-10.7-x86_64 / wiel draait installeren wordt uitgevoerd install_lib maakt build / bdist.macosx-10.7-x86_64 maakt build / bdist.macosx-10.7-x86_64 / wiel maakt build / bdist.macosx-10.7-x86_64 / wiel / pathologie kopiëren bouwen / lib / pathologie / __ init__.py -> build / bdist.macosx-10.7-x86_64 / wheel / pathology copy build / lib / pathology / path.py -> build / bdist.macosx-10.7-x86_64 / wheel / pathology running install_egg_info running egg_info writing pathology.egg-info / PKG-INFO writing dependency_links naar pathology.egg-info / dependency_links.txt schrijven van topniveaulamen naar pathology.egg-info / top_level.txt lezen van manifestbestand 'pathologie. egg-info / SOURCES.txt 'writing manifest file' pathology.egg-info / SOURCES.txt 'Kopiëren van pathology.egg-info naar bui ld / bdist.macosx-10.7-x86_64 / wheel / pathology-0.1-py3.6.egg-info running install_scripts create build / bdist.macosx-10.7-x86_64 / wheel / pathology-0.1.dist-info / WHEEL

Het pathologiepakket bevat alleen pure Python-modules, dus er kan een universeel pakket worden gebouwd. Als uw pakket C-extensies bevat, moet u voor elk platform een ​​afzonderlijk wiel bouwen:

$ ls -la dist in totaal 16 drwxr-xr-x 4 gigi.sayfan gigi.sayfan 136 apr 18 21:24. drwxr-xr-x 13 gigi.sayfan gigi.sayfan 442 18 apr 21: 24 ... -rw-r - r - 1 gigi.sayfan gigi.sayfan 2695 apr 18 21:24 pathology-0.1-py3-none-any .whl -rw-r - r-- 1 gigi.sayfan gigi.sayfan 1223 18 apr 21:20 pathology-0.1.tar.gz 

Voor een diepere duik in het onderwerp van het verpakken van Python-bibliotheken, ga je naar Hoe je eigen Python-pakketten schrijft.

Hoe een Python-pakket te distribueren

Python heeft een centrale pakketrepository met de naam PyPI (Python Packages Index). Wanneer u een Python-pakket installeert met pip, wordt het pakket gedownload van PyPI (tenzij u een andere repository opgeeft). Om ons pathologiepakket te distribueren, moeten we het uploaden naar PyPI en wat extra metagegevens leveren die PyPI vereist. De stappen zijn:

  • Maak een account aan op PyPI (slechts één keer).
  • Registreer uw pakket.
  • Upload uw pakket.

Account aanmaken

U kunt een account maken op de PyPI-website. Maak vervolgens een .pypirc bestand in uw homedirectory:

[distutils] index-servers = pypi [pypi] repository = https://pypi.python.org/pypi gebruikersnaam = the_gigi 

Voor testdoeleinden kunt u een "pypitest" -indexserver toevoegen aan uw .pypirc het dossier:

[distutils] index-servers = pypi pypitest [pypitest] repository = https://testpypi.python.org/pypi gebruikersnaam = the_gigi [pypi] repository = https://pypi.python.org/pypi gebruikersnaam = the_gigi

Registreer uw pakket

Als dit de eerste versie van uw pakket is, moet u het met PyPI registreren. Gebruik de registeropdracht van setup.py. Het zal u om uw wachtwoord vragen. Merk op dat ik het hier naar de testrepository verwijs:

$ python setup.py register -r pypitest running register running egg_info writing pathology.egg-info / PKG-INFO writing dependency_links naar pathology.egg-info / dependency_links.txt schrijven van topniveaus naar pathology.egg-info / top_level.txt leesmanifestbestand 'pathology.egg-info / SOURCES.txt' schrijven manifestbestand 'pathology.egg-info / SOURCES.txt' running check Wachtwoord: Registratie van pathologie naar https://testpypi.python.org/pypi Serverreactie (200 ): OK

Upload uw pakket

Nu het pakket is geregistreerd, kunnen we het uploaden. Ik raad aan om touw te gebruiken, wat veiliger is. Installeer het zoals gebruikelijk met pip install twine. Upload vervolgens uw pakket met touw en verstrek uw wachtwoord (hieronder geredigeerd):

$ twine upload -r pypitest -p  dist / * Uploaddistributies naar https://testpypi.python.org/pypi Uploaden van pathologie-0.1-py3-none-any.whl [==================== ============] 5679/5679 - 00:00:02 Uploaden van pathologie-0.1.tar.gz [=================== =============] 4185/4185 - 00:00:01 

Voor meer informatie over het distribueren van uw pakketten, bekijkt u Hoe u uw Python-pakketten deelt.

Conclusie

In deze zelfstudie hebben we het volwaardige proces doorlopen van het schrijven van een Python-bibliotheek, het verpakken en distribueren via PyPI. Op dit punt zou je alle tools moeten hebben om je bibliotheken met de wereld te schrijven en te delen.

Aarzel ook niet om te zien wat we beschikbaar hebben voor de verkoop en om te studeren op de markt, en stel vragen en geef waardevolle feedback via de onderstaande feed.