So erstellen Sie Ihr erstes Python-Paket und laden es auf PyPI hoch


Vor ein paar Wochen wollte ich lernen, wie man mein erstes Python-Paket erstellt, und überlegte, wo ich anfangen sollte.

Nun, ich war verwirrt und etwas gestresst, als ich versuchte, ein einfaches und unkompliziertes Tutorial zu finden, mit dem ich beginnen konnte. Aus diesem Grund habe ich beschlossen, dieses Tutorial zu schreiben, das dokumentiert, wie ich mein erstes Python-Paket erstellt habe.

Was ist ein Paket in Python?

Bevor wir beginnen, sollten wir die Bedeutung eines Pakets in Python kennen.

Ein Python-Paket ist ein Verzeichnis, das eine Reihe von Modulen mit einer Abhängigkeitsdatei namens __init__.py enthält. Diese Datei kann vollständig leer sein und Sie verwenden sie, um das Verzeichnis auf einer Festplatte als Python-Paket zu markieren.

Im Folgenden sehen Sie ein Beispiel für ein Paketverzeichnis:

package
	__init__.py
	module_a.py
	module_b.py
	module_c.py

Die __init__.py ist eine Abhängigkeitsdatei, die Python dabei hilft, in unserem Paketverzeichnis nach den verfügbaren Modulen zu suchen. Wenn wir diese Datei entfernen, kann Python unsere Module nicht importieren.

Pakete vs. Module in Python

Sie sollten jetzt verstehen, dass Python-Pakete ein strukturiertes Verzeichnis mit mehreren Modulen erstellen, aber was ist mit Modulen? Stellen wir sicher, dass wir den Unterschied zwischen einem Paket und einem Modul verstehen:

Ein Modul entspricht immer einer einzelnen Python-Datei turtle.py. Es enthält Logik wie Klassen, Funktionen und Konstanten.

Ein Paket ist im Grunde ein Modul, das viele Module oder Unterpakete enthalten kann.

Paketstruktur

Pakete enthalten jedoch nicht nur Module. Sie bestehen ebenfalls aus Skripten der obersten Ebene, Dokumentation und Tests. Das folgende Beispiel zeigt, wie ein einfaches Python-Paket aufgebaut sein kann:

package_name/
	docs/
	scripts/
	src/
		package_a
			__init__.py
			module_a.py
		package_b
			__init__.py
			module_b.py
	tests/
    	__init__.py
		test_module_a.py
		test_module_b.py
	LICENSE.txt
	CHANGES.txt
	MANIFEST.in
	README.txt
	pyproject.toml
	setup.py
	setup.cfg

Lassen Sie uns verstehen, wofür jede Datei in der Baumstruktur oben verwendet wird:

  • Paketname: stellt das Hauptpaket dar.
  • docs: Enthält Dokumentationsdateien zur Verwendung des Pakets.
  • scripts/: Ihre Skripte der obersten Ebene.
  • src: wohin Ihr Code geht. Es enthält Pakete, Module, Unterpakete usw.
  • Tests: Hier können Sie Unit-Tests durchführen.
  • LICENSE.txt: enthält den Text der Lizenz (z. B. MIT).
  • CHANGES.txt: meldet die Änderungen jeder Version.
  • MANIFEST.in: Hier geben Sie Anweisungen dazu an, welche zusätzlichen Dateien Sie einschließen möchten (Nicht-Code-Dateien).
  • README.txt: enthält die Paketbeschreibung (Markdown-Format).
  • pyproject.toml: zum Registrieren Ihrer Build-Tools.
  • setup.py: enthält das Build-Skript für Ihre Build-Tools.
  • setup.cfg: die Konfigurationsdatei Ihrer Build-Tools.

Beachten Sie, dass es zwei Möglichkeiten gibt, unsere Testdateien in unser Hauptpaket einzubinden. Wir können es wie oben beschrieben auf der obersten Ebene belassen oder es wie folgt in das Paket einfügen:

package_name/
      __init__.py
      module_a.py
      module_b.py
      test/
          __init__.py
          test_module_a.py
          test_module_b.py

Meiner Meinung nach kann es sehr hilfreich sein, die Tests auf der obersten Ebene zu halten, insbesondere wenn unsere Tests das Lesen und Schreiben anderer externer Dateien erfordern.

Sollten Sie setup.cfg oder setup.py verwenden?

setup.py und setup.cfg sind die Standardpaketierungstools in PyPI, setuptools, pip und der Standard-Python-Bibliothek.

Hier stellen sie die Konfigurations- und Build-Skripte für Setuptools dar. Beide teilen den Setuptools mit, wie das Paket erstellt und installiert werden kann.

Die genannte Datei enthält Informationen wie die Version, Pakete und einzubindende Dateien sowie etwaige Anforderungen.

Das Folgende zeigt ein Beispiel von setup.py, das einige setup()-Argumente verwendet. Weitere Argumente finden Sie hier:

import setuptools

with open("README.md", "r", encoding = "utf-8") as fh:
    long_description = fh.read()

setuptools.setup(
    name = "package-name",
    version = "0.0.1",
    author = "author",
    author_email = "author@example.com",
    description = "short package description",
    long_description = long_description,
    long_description_content_type = "text/markdown",
    url = "package URL",
    project_urls = {
        "Bug Tracker": "package issues URL",
    },
    classifiers = [
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    package_dir = {"": "src"},
    packages = setuptools.find_packages(where="src"),
    python_requires = ">=3.6"
)

Die setup.cfg ist in einem anderen Format geschrieben als setup.py und enthält grundsätzlich zwei wesentliche Schlüssel, command und options .

Die command-Taste stellt einen der distutils-Befehle dar, während die options-Taste die Optionen definiert, die der Befehl unterstützen kann.

[command]
option = value

Das Folgende zeigt ein Beispiel von setup.cfg, das einige Metadaten und Optionen verwendet. Eine Vielzahl an Metadaten und Optionen finden Sie hier:

[metadata]
name = package-name
version = 0.0.1
author = name of the author
author_email = author@example.com
description = short package description
long_description = file: README.md
long_description_content_type = text/markdown
url = package url
project_urls =
    Bug Tracker = package issues url
classifiers =
    Programming Language :: Python :: 3
    License :: OSI Approved :: MIT License
    Operating System :: OS Independent

[options]
package_dir =
    = src
packages = find:
python_requires = >=3.6

[options.packages.find]
where = src

setup.py und setup.cfg sind beide spezifisch für Setuptools. Außerdem kann die setup.cfg sicher nach pyproject.toml verschoben werden.

Hier besteht die Idee darin, dass wir vielleicht eines Tages auf andere Build-Systeme wie flit oder poetry umsteigen wollen. In diesem Fall müssen wir lediglich den Build-System-Eintrag (z. B. setuptools) in pyproject.toml in etwas wie „flit“ oder „poetry“ ändern, anstatt noch einmal von vorne zu beginnen.

Hier finden Sie Informationen zu anderen Tools, die Python-Pakete erstellen und verteilen.

Unabhängig davon, welche Konfigurationsdatei wir ausgewählt haben, sind wir darauf festgelegt, diese bestimmte Konfigurationsdatei beizubehalten, entweder pyproject.toml, setup.cfg oder setup.py.

Laut Python Packaging User Guide wird setup.cfg bevorzugt, da es statisch, sauber und einfacher zu lesen ist und Codierungsfehler vermeidet.

So erstellen Sie Ihr erstes Python-Paket

Jetzt ist es an der Zeit, mit der Erstellung eines einfachen Python-Pakets zu beginnen. Wir werden setuptools als Build-System verwenden und unser Projekt mit setup.cfg und pyproject.toml konfigurieren.

Richten Sie die Paketdateien ein

Für dieses einfache Paket müssen wir unser Verzeichnis strukturieren, indem wir die Abhängigkeitsdateien hinzufügen, die erforderlich sind, um das Paket für die Verteilung vorzubereiten. So strukturieren Sie unser Paket:

basicpkg/
	src/
		divide
			__init__.py
			divide_by_three.py
		multiply
			__init__.py
			multiply_by_three.py
	tests/
		__init__.py
		test_divide_by_three.py
		test_multiply_by_three.py
	LICENSE.txt
	README.txt
	pyproject.toml
	setup.cfg

Unser Hauptpaket besteht aus zwei Paketen: dem ersten zum Teilen von Zahlen durch drei und dem anderen zum Multiplizieren von Zahlen mit drei.

Außerdem ignorieren wir einige Dateien wie CONTEXT.txt, MANIFEST.in und das Verzeichnis docs/, um die Dinge im Moment einfach zu halten. Wir benötigen jedoch das Verzeichnis test/, um unsere Komponententests aufzunehmen, um das Verhalten des Pakets zu testen.

Fügen Sie unseren Modulen etwas Logik hinzu

Wie immer lassen wir die __init__.py leer. Dann müssen wir etwas Logik in unsere Module einbauen, um unsere Operationen auszuführen.

Für das Divide-Paket fügen wir den folgenden Inhalt in divide_by_ three.py ein, um eine beliebige Zahl durch drei zu dividieren:

def divide_by_three(num):
	return num / 3

Die gleiche Logik gilt für multiply_by_ three.py innerhalb des Multiplikationspakets. Diesmal geht es jedoch darum, eine beliebige Zahl mit drei zu multiplizieren:

def multiply_by_three(num):
	return num * 3

Sie können jederzeit weitere Pakete und Module hinzufügen, um andere Arten von Vorgängen auszuführen. Sie können beispielsweise Pakete hinzufügen, um Additions- und Subtraktionsaufgaben auszuführen.

Testen Sie unsere Module

Es ist gut, das Hinzufügen automatisierter Tests zu jedem von uns erstellten Programm zu üben. Wir werden unittest verwenden, um unsere Module und das Verhalten des Pakets zu testen.

Fügen wir im Verzeichnis test/ den folgenden Code zu test_divide_by_ three.py hinzu:

import unittest
from divide.by_three import divide_by_three 

class TestDivideByThree(unittest.TestCase):

	def test_divide_by_three(self):
		self.assertEqual(divide_by_three(12), 4)

unittest.main()

Wir haben TestCase aus unittest importiert, um unsere automatisierten Tests durchzuführen. Dann haben wir unsere Divisionsmethode divide_by_ three() aus dem Modul by_ three importiert, das sich im Divide-Paket befindet.

Wenn wir hier __init__.py entfernen, kann Python unsere Module nicht mehr finden.

Mit .assertEqual() wird die Gleichheit der beiden oben genannten Werte (divide_by_ three(12) und 4) überprüft. Der unittest.main() wird instanziiert, um alle unsere Tests auszuführen.

Die gleiche Logik gilt für test_multiply_by_ three.py:

import unittest
from multiply.by_three import multiply_by_three

class TestMultiplyByThree(unittest.TestCase):

	def test_multiply_by_three(self):
		self.assertEqual(multiply_by_three(3), 9)

unittest.main()

Um die Tests auszuführen, geben Sie Folgendes in Ihr Terminal/Ihren Befehl ein:

Für Linux:

python3 tests/test_divide_by_three.py

Für Windows:

py tests/test_divide_by_three.py

Machen Sie dasselbe, um das Multiplikationsmodul zu testen. Wenn unsere Tests erfolgreich verlaufen, sollten Sie Folgendes erhalten:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Wenn Sie zusätzliche Pakete und Module hinzufügen, versuchen Sie, ihnen einige unittest-Methoden hinzuzufügen. Das wird eine gute Herausforderung für Sie sein.

Konfigurieren Sie Metadaten mit setup.cfg

Als nächstes müssen wir eine Konfigurationsdatei für setuptools hinzufügen. Wie bereits erwähnt, teilt diese Konfigurationsdatei den Setuptools mit, wie unser Paket erstellt und installiert werden kann.

Daher müssen wir die folgenden Metadaten und Optionen zu unserer setup.cfg hinzufügen. Vergessen Sie dann nicht, einen anderen Namen zu wählen, da ich dieses Paket mit dem untenstehenden Namen bereits auf TestPyPi hochgeladen habe. Ändern Sie außerdem andere Informationen wie Autor, E-Mail und Projekt-URLs, um das Paket mit Ihren Informationen zu verteilen.

[metadata]
name = basicpkg
version = 1.0.0
author = your name
author_email = your email
description = A simple Python package
long_description = file: README.md, LICENSE.txt
long_description_content_type = text/markdown
url = https://gitlab.com/codasteroid/basicpkg
project_urls =
    Bug Tracker = https://gitlab.com/codasteroid/basicpkg/-/issues
    repository = https://gitlab.com/codasteroid/basicpkg
classifiers =
    Programming Language :: Python :: 3
    License :: OSI Approved :: MIT License
    Operating System :: OS Independent

[options]
package_dir =
    = src
packages = find:
python_requires = >=3.6

[options.packages.find]
where = src

Sie sollten einfach alles als Standard in der Optionskategorie belassen. Das Paketverzeichnis lokalisiert das Stammpaket, in dem sich Ihre Pakete, Module und alle Ihre Python-Quelldateien befinden.

Im Paketschlüssel können wir unsere Pakete manuell wie folgt auflisten: [divide, multiply]. Wenn wir jedoch alle Pakete erhalten möchten, können wir find: verwenden und angeben, wo wir diese Pakete finden müssen, indem wir [options.packages.find] mit dem < verwendenwhere Schlüssel, der dem Namen des Root-Pakets zugewiesen ist.

Stellen Sie immer sicher, dass Sie den Schlüssel classifiers in Ihre Konfigurationsdatei aufnehmen, um einige wichtige Informationen hinzuzufügen, wie die Version von Python und das Betriebssystem, für das unser Paket geeignet ist. Die vollständige Liste der Klassifikatoren finden Sie hier.

Erstellen Sie pyproject.toml

Wir werden Setuptools als Build-System verwenden. Um pip oder andere Build-Tools über unser Build-System zu informieren, benötigen wir zwei Variablen, wie unten gezeigt.

Wir verwenden build-system.require, um einzuschließen, was wir zum Erstellen unseres Pakets benötigen, während build-system.build-back-end das Objekt definiert, das den Build ausführt.

Geben wir also den folgenden Inhalt in pyproject.toml ein:

[build-system]
requires = ['setuptools>=42']
build-backend = 'setuptools.build_meta'

Beachten Sie, dass Sie alle Konfigurationseinstellungen in setup.cfg sicher nach pyproject.toml verschieben können, wenn Sie sich entscheiden, das Build-System beispielsweise auf Flit oder Poetry zu ändern. Dadurch sparen Sie Zeit.

Erstellen Sie die README.md

Es ist wichtig, eine gute README.md zu erstellen. Fügen wir unserem Paket eine Beschreibung und einige Anweisungen zur Installation hinzu:

# `basicpkg`

The `basicpkg` is a simple testing example to understand the basics of developing your first Python package. 

Wir können auch hinzufügen, wie unser Paket wie folgt verwendet wird:

from multiply.by_three import multiply_by_three
from divide.by_three import divide_by_three

multiply_by_three(9)
divide_by_three(21)

Fühlen Sie sich frei, alle Informationen hinzuzufügen, die anderen Entwicklern helfen können, zu verstehen, wofür Ihr Paket verwendet wird, sowie einige Anweisungen, wie man es installiert und richtig damit arbeitet.

Beachten Sie, dass unsere Konfigurationsdatei die Datei README.md lädt und bei der Verteilung unseres Pakets enthalten ist.

Fügen Sie eine Lizenz hinzu

Es ist sehr wichtig, Ihrem Projekt eine LIZENZ hinzuzufügen, damit Benutzer wissen, wie sie Ihren Code verwenden können. Wählen wir eine MIT-Lizenz für unser Python-Paket und fügen Sie LICENSE.txt den folgenden Inhalt hinzu:

MIT License

Copyright (c) [year] [fullname]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Vergessen Sie nicht, [Jahr] durch das aktuelle Jahr und [vollständiger Name] durch Ihren Namen oder die Namen der Urheberrechtsinhaber zu ersetzen.

Generieren Sie die Verteilungsarchive

Es bleibt noch ein weiterer Schritt, um unser Paket für die Verteilung vorzubereiten: die Generierung der Verteilungsarchive für unser Python-Paket. Dazu müssen wir zunächst den Build unseres PyPA aktualisieren und dann die Quell- und Build-Archive generieren.

Führen Sie im Terminal/cmd die folgenden Befehle aus demselben Verzeichnis aus, in dem sich die Datei pyproject.toml befindet:

Für Linux:

python3 -m pip install --upgrade build
python3 -m build

Für Windows:

py -m pip install --upgrade build
py -m build

Sobald der obige Vorgang abgeschlossen ist, wird ein neues Verzeichnis namens dist/ mit zwei Dateien darin generiert. Die Datei .tag.tz ist das Quellarchiv und die Datei .whl* ist das erstellte Archiv. Diese Dateien stellen die Verteilungsarchive unseres Python-Pakets dar, die in den Python-Paketindex hochgeladen und in den folgenden Abschnitten von pip installiert werden.

So laden Sie ein Paket in Python hoch

Im Python-Paketindex sollten wir unser Projekt hochladen. Da sich unser Python-Paket im Test befindet und wir möglicherweise weitere Funktionalitäten hinzufügen, um damit zu experimentieren, sollten wir eine separate Instanz von PyPI namens TestPyPI verwenden. Diese Instanz ist speziell zum Testen und Experimentieren implementiert. Sobald Ihr Paket dann fertig ist und Ihren Erwartungen entspricht, können Sie es als echtes Paket auf PyPI hochladen.

Befolgen wir die nachstehenden Anweisungen, um unser TestPyPI zum Hochladen unseres Pakets vorzubereiten:

  1. Gehen Sie zu TestPyPI und erstellen Sie ein Konto.
  2. Bestätigen Sie Ihre E-Mail-Adresse, damit Sie Pakete hochladen können.
  3. Aktualisieren Sie Ihre Profileinstellungen (fügen Sie Ihr Bild hinzu usw.).
  4. Gehen Sie zu API-Tokens und erstellen Sie Ihr API-Token, um Ihre Pakete sicher hochzuladen.
  5. Stellen Sie auf derselben Seite den Bereich auf „Gesamtes Konto“ ein.
  6. Kopieren Sie Ihren Token und speichern Sie ihn an einem sicheren Ort auf Ihrer Festplatte.

Als nächstes müssen wir unsere Distributionsarchive hochladen. Dazu müssen wir ein Upload-Tool verwenden, um unser Paket hochzuladen. Das offizielle PyPI-Upload-Tool ist twine, also installieren wir Twine und laden unsere Distributionsarchive in das Verzeichnis dist/ hoch.

Führen Sie im Terminal/cmd die folgenden Befehle aus demselben Verzeichnis aus, in dem sich die Datei pyproject.toml befindet:

Für Linux:

python3 -m pip install --upgrade twine
python3 -m twine upload --repository testpypi dist/*

Für Windows:

py -m pip install --upgrade twine
py -m twine upload --repository testpypi dist/*

Geben Sie dann __token__ ein. als Benutzernamen und das von Ihnen gespeicherte Token (einschließlich Pypi-Präfix) als Passwort. Drücken Sie die Eingabetaste, um die Verteilungen hochzuladen.

So installieren Sie das hochgeladene Python-Paket

Jetzt ist es an der Zeit, unser Paket zu installieren. Sie können eine neue virtuelle Umgebung erstellen und sie mit pip von TestPyPI aus installieren:

Für Linux:

python3 -m venv env
source env/bin/activate
(env) python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps basicpkg

Für Windows:

py -m venv env
.\env\Scripts\activate
(env) py -m pip install --index-url https://test.pypi.org/simple/ --no-deps basicpkg

Stellen Sie sicher, dass Ihre virtuelle Umgebung aktiviert ist, bevor Sie überprüfen, ob unser Paket ordnungsgemäß funktioniert.

Führen Sie im Terminal/Befehl python3 für Linux-Benutzer oder py für Windows-Benutzer aus. Geben Sie dann den folgenden Code ein, um sicherzustellen, dass die Multiplikations- und Divisionspakete wie erwartet funktionieren:

from multiply.by_three import multiply_by_three
from divide.by_three import divide_by_three

multiply_by_three(9)
divide_by_three(21)

# Output: 27
# Output: 7

Denken Sie daran, dass wir die Methoden aus unseren Modulen importieren müssen, die wir benötigen, um die gewünschten Operationen auszuführen, wie wir es oben getan haben.

Hurra! Unser Paket funktioniert wie erwartet.

Nachdem Sie Ihr Paket getestet und damit experimentiert haben, befolgen Sie die folgenden Anweisungen, um Ihr Paket auf das echte PyPI hochzuladen:

  1. Gehen Sie zu PyPI und erstellen Sie ein Konto.
  2. Führen Sie twine upload dist/* im Terminal/in der Befehlszeile aus.
  3. Geben Sie die Kontoanmeldeinformationen ein, für die Sie sich auf dem eigentlichen PyPI registriert haben.
  4. Führen Sie dann pip install [package_name] aus, um Ihr Paket zu installieren.

Glückwunsch! Ihr Paket wurde vom echten PyPI installiert.

Was kommt als nächstes?

Es wäre großartig, wenn Sie eine einfache Idee hätten und daraus Ihr erstes echtes Python-Paket erstellen würden. In diesem Blogbeitrag habe ich mich nur auf die Grundlagen konzentriert, die Sie für den Einstieg benötigen, aber in der Welt der Verpackung gibt es viel zu lernen.

Hoffentlich hilft Ihnen meine erste Erfahrung mit der Entwicklung von Python-Paketen dabei, zu lernen, was Sie zum Erstellen benötigen. Wenn Sie Fragen haben, können Sie mich jederzeit auf LinkedIn kontaktieren. Außerdem können Sie meinen Kanal auf YouTube abonnieren, wo ich Videos darüber teile, was ich lerne und mit Code baue.

Wir sehen uns im nächsten Beitrag und machen weiter so!

Verweise

Hier sind einige Referenzen, die mir bei der Entwicklung meines ersten Python-Pakets geholfen haben:

  • Verpacken von Python-Projekten
  • Metadaten konfigurieren
  • PEP 621 – Speichern von Projektmetadaten in pyproject.toml
  • Glossar – Python Packaging-Benutzerhandbuch