Skip to content
Snippets Groups Projects
Develop_and_publish_modul.ipynb 11.9 KiB
Newer Older
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1fe95632",
   "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",
    "![CC-BY-NC](https://scm.cms.hu-berlin.de/ibi/python/-/raw/master/img/cc-by-nc.png)\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",
   "id": "c7dc7204",
   "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",
   "id": "21e7cd83",
   "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,
   "id": "94cad5e7",
   "metadata": {},
   "outputs": [],
   "source": [
    "import json"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1129c831",
   "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,
   "id": "3aa3db5d",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bdc84dcf",
   "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,
   "id": "2148e19b",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install pandas"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2dce18c9",
   "metadata": {},
   "source": [
    "## Doch was passiert hier eigentlich?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f07907eb",
   "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",
   "id": "d45b9900",
   "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",
   "id": "cbf98fba",
   "metadata": {},
   "source": [
    "## Mein erstes PyPI (Test)-Package"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0cbf8602",
   "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",
    "```"
   "id": "8b3f6ddf",
   "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",
    "name = \"Pypiex\"\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"
   "id": "927e9459",
   "metadata": {},
   "source": [
    "### README.md\n",
    "\n",
    "```\n",
    "# Mein Projekt\n",
    "\n",
    "Dies ist ein Beispielprojekt.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c8acd0d",
   "metadata": {},
   "source": [
    "### LICENSE\n",
    "\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",
   "id": "d4b197a7",
   "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",
    "```\n",
    "\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. "
   "id": "a87cd03e",
   "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",
    "\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,
   "id": "ee4663eb",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}