Newer
Older
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Ein Python-Modul entwicklen und veröffentlichen\n",
"\n",
"<br/>\n",
"<br/>\n",
"\n",
"Dieses Notebook finden Sie hier: https://scm.cms.hu-berlin.de/ibi/python/-/blob/master/programmierspass/Develop_and_publish_modul.ipynb\n",
"\n",
"<br/>\n",
"\n",
"\n",
"\n",
"Dieses Notebook ist als freies Werk unter der Lizenz [Creative Commons Attribution-NonCommercial 3.0 Unported](http://creativecommons.org/licenses/by-nc/3.0/) verfügbar. Sie dürfen die Inhalte kopieren, verteilen und verändern, solange Sie die Urheber nennen und sie nicht für kommerzielle Zwecke nutzen."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Was ist ein Modul?\n",
"\n",
"- Ein Modul ist eine Datei, die Python-Definitionen und -Anweisungen beinhaltet. Oder anders gesagt: Jede Python-Datei ist ein Modul und der Name des Moduls ist der Dateiname ohne die Endung `.py`\n",
"- Ein Package ist eine Sammlung von Modulen. Hierfür liegen mehrere Module in einem Ordner, welcher auch eine Datei `__init__.py` enthalten muss. Dies unterscheidet ein Package von einem Ordner, der zufällig mehrere Module enthält."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Module verwenden\n",
"\n",
"- Python wird mit einer Bibliothek von Standardmodulen ausgeliefert. Ein solches Modul kann mit dem Befehl `import` importiert und anschließend verwendet weden."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import json"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- Wenn wir nun versuchen ein Modul zu installieren, welches nicht in den Standardmodulen enthalten ist, dann kommt es zu einer Fehlermeldung."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Module installieren\n",
"\n",
"- Module, die nicht in der Bibliothek von Standardmodulen enthalten sind, müssen vor dem Import installiert werden. Hierfür wird ein Paketverwaltungsprogramm verwendet. Dies ist im Normfall `pip`. Mit dem Befehl `pip install` können Pakete installiert werden."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!pip install pandas"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Doch was passiert hier eigentlich?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Python Package Index (PyPI)\n",
"\n",
"- `pip`lädt Pakete aus dem Python Package Index (PyPI).\n",
"- PyPI ist ein Sofware-Vezeichnis der Programmiersprache Python.\n",
"- PyPI umfasst knapp 420.00 Projekte (Stand: Dezember 2022)\n",
"- Jede:r kann sich auf PyPI (https://pypi.org) registrieren und ein Projekt erstellen.\n",
"\n",
"<div class=\"alert alert-info\">\n",
"<b>Hinweis</b> Der Begriff \"Package\" kann etwas verwirrend sein. PyPI spricht auch von Packages. Ein PyPI-Package kann ein einzelnes Python-Modul sein oder aber auch mehrere Python-Packages umfassen. \n",
"</div>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### PyPI Testumgebung\n",
"\n",
"- Für Testzwecke gibt es die PyPI Testumgebgung (https://test.pypi.org)\n",
"- Diese funktioniert genau wie die reale Version und sollte verwendet werden um die eigene Konfiguration zu testen. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Mein erstes PyPI (Test)-Package"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Beispiel für ein minimales Projekt\n",
"```\n",
"pypi-example/\n",
"├── LICENSE\n",
"├── pyproject.toml\n",
"├── README.md\n",
"└── pypiex/\n",
" ├── __init__.py\n",
" └── example.py\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### pyproject.toml - Konfiguration von Metadaten\n",
"\n",
"```toml\n",
"[build-system]\n",
"requires = [\"setuptools>=61.0\"]\n",
"build-backend = \"setuptools.build_meta\"\n",
"\n",
"[project]\n",
"version = \"0.0.1\"\n",
"authors = [\n",
" { name = \"Frederik Arnold\", email = \"frederik.arnold@hu-berlin.de\" }\n",
"description = \"Eine kurze Beschreibung für PyPiex\"\n",
"readme = \"README.md\"\n",
"requires-python = \">=3.7\"\n",
"\n",
"classifiers = [\n",
" \"Programming Language :: Python :: 3\",\n",
" \"License :: OSI Approved :: Apache Software License\",\n",
" \"Operating System :: OS Independent\",\n",
"]\n",
"\n",
"[project.urls]\n",
"\"Homepage\" = \"https://...\"\n",
"\"Bug Tracker\" = \"https://...\"\n",
"```\n",
"\n",
"- Weitere Informationen zu den einzelnen Angaben: https://packaging.python.org/en/latest/specifications/declaring-project-metadata/#declaring-project-metadata"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### README.md\n",
"\n",
"```\n",
"# Mein Projekt\n",
"\n",
"Dies ist ein Beispielprojekt.\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
"\n",
"```\n",
"Apache License\n",
" Version 2.0, January 2004\n",
" http://www.apache.org/licenses/\n",
"\n",
" TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n",
"\n",
" 1. Definitions.\n",
"\n",
" \"License\" shall mean the terms and conditions for use, reproduction,\n",
" and distribution as defined by Sections 1 through 9 of this document.\n",
"\n",
" \"Licensor\" shall mean the copyright owner or entity authorized by\n",
" the copyright owner that is granting the License.\n",
"\n",
" \"Legal Entity\" shall mean the union of the acting entity and all\n",
" other entities that control, are controlled by, or are under common\n",
" control with that entity. For the purposes of this definition,\n",
" \"control\" means (i) the power, direct or indirect, to cause the\n",
" direction or management of such entity, whether by contract or\n",
" otherwise, or (ii) ownership of fifty percent (50%) or more of the\n",
" outstanding shares, or (iii) beneficial ownership of such entity.\n",
" \n",
" [...]\n",
"\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Package für den Upload erzeugen\n",
"\n",
"- Für die Erzeugung der zum Upload benötigten Dateien, muss zuerst in den Projektordner navigiert werden, und dort die folgende Befehle ausgeführt werden."
]
},
{
"cell_type": "markdown",
"id": "68e05d16",
"metadata": {},
"source": [
"- Zuerst müssen für den Build benötigten Pakete installiert werden.\n",
"\n",
"```\n",
"pip install --upgrade build\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "3cf7b4cd",
"metadata": {},
"source": [
"- Dann kann anhand der `pyproject.toml` das Package erstellt werden. Dieses wird in den Ordner `dist` im Projektordner gespeichert.\n",
"\n",
"```\n",
"python -m build\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "afd1c91e",
"metadata": {},
"source": [
"- Wenn die Befehle korrekt ausgeführt werden konnten, dann sollte die letzte Zeile so lauten:\n",
"\n",
"Successfully built xyz-version.tar.gz and xyz-version-py3-none-any.whl\n",
"- Außerdem sollten zwei Dateien im Ordner `dist` erzeugt worden sein. Es gibt verschiedene Arten von Packages.\n",
"- Die Datei mit der Endung `.tar.gz` ist eine Source Distribution.\n",
"- Die Datei mit der Endung `.whl` (Wheel-Datei) ist eine Built Distribution. Dieses Format ist neuer und hat verschiedene Vorteile. Die Details sind an dieser Stelle nicht relevant. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Package auf den Testserver hochladen\n",
"- Um Pakete hochladen zu können muss zuerst ein Account erstellt werden. \n",
"\n",
"<div class=\"alert alert-info\">\n",
"<b>Hinweis</b> Der PyPI Testserver ist unabhängig von der Produktionsumgebung und es werden jeweils eigene Accounts benötigt.\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "1444d33a",
"metadata": {},
"source": [
"- Jetzt muss ein `API token` generiert werden. Dies geschieht in den Kontoeinstellungen im Bereich `API-Token`. Folgender Link führt automatisch dort hin: https://test.pypi.org/manage/account/#api-tokens. Auf `API-Token hinzufügen` klicken. Dort einen sinnvollen Namen vergeben und den Geltungsbereich auf `Gesamtes Konto` stellen.\n",
"\n",
"<div class=\"alert alert-info\">\n",
"<b>Hinweis</b> Ein erstelltes Token muss direkt gesichert werden, da es danach nicht mehr einsehbar ist.\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "abf06326",
"metadata": {},
"source": [
"- Jetzt die für den Upload benötigten Packages laden und installieren:\n",
"\n",
"```\n",
"pip install --upgrade twine\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "325e2694",
"metadata": {},
"source": [
"- Und dann den Upload ausführen. Hierbei wird nach dem Username und Passwort gefragt. Für den Username muss `__token__` verwendet werden und für Passwort das gerade erstelle Token inklusive des `pypi` Prefix.\n",
"\n",
"\n",
"```\n",
"python -m twine upload --repository testpypi dist/*\n",
"```\n",
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
"\n",
"<div class=\"alert alert-info\">\n",
"<b>Hinweis</b> Soll eine neue Version für ein Paket hochgeladen werden, dann muss vor der Erzeugung des Pakets immer zuerst die Versionsnummer in der `pyproject.toml` angepasst werden. Eine Version kann immer nur ein Mal hochgeladen werden. \n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "a55a2eb7",
"metadata": {},
"source": [
"## Package installieren und verwenden\n",
"\n",
"- Das Paket kann jetzt mit `pip` installiert werden. Hierzu muss der Testserver angegeben werden."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "eab699f8",
"metadata": {},
"outputs": [],
"source": [
"!pip install --index-url https://test.pypi.org/simple/ --no-deps Pypiex"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2f369bf9",
"metadata": {},
"outputs": [],
"source": [
"from pypiex import example\n",
"example.say_hello(\"Frederik\")"
{
"cell_type": "markdown",
"id": "2a17f85c",
"metadata": {},
"source": [
"## Literatur\n",
"\n",
"- https://packaging.python.org/en/latest/tutorials/packaging-projects/\n",
"- https://setuptools.pypa.io/en/latest/userguide/index.html"
]
},
{
"cell_type": "code",
"execution_count": null,