Hinweis: GitHub-gehostete Runner werden auf GitHub Enterprise Server derzeit nicht unterstützt. Weitere Informationen zur geplanten zukünftigen Unterstützung findest Du in der GitHub public roadmap.
Einführung
Diese Anleitung zeigt Dir, wie du ein Python-Paket baust, testest und veröffentlichst.
GitHub-gehostete Runner haben einen Toolcache mit vorinstallierter Software, die Python und PyPy einschließt. Du brauchst nichts zu installieren! Eine vollständige Liste der aktuellen Software und der vorinstallierten Versionen von Python und PyPy finden Sie unter Verwenden von auf GitHub gehosteten Runnern.
Voraussetzungen
Du solltest mit YAML und der Syntax für GitHub Actions vertraut sein. Weitere Informationen findest du unter Schreiben von Workflows.
Du solltest ein grundlegendes Verständnis von Python und pip haben. Weitere Informationen finden Sie unter:
Verwendung von selbstgehosteten Runnern auf GitHub Enterprise Server
Wenn du Setup-Aktionen (wie z.B.actions/setup-LANGUAGE
) auf GitHub Enterprise Server mit selbstgehosteten Runnern verwendest, musst du möglicherweise den Tools-Cache auf Runnern einrichten, die keinen Internetzugang haben. Weitere Informationen finden Sie unter Einrichten des Toolcaches auf selbstgehosteten Runnern ohne Internetzugriff.
Verwenden einer Python-Workflowvorlage
Fügen Sie für einen schnellen Einstieg dem Verzeichnis .github/workflows
Ihres Repositorys eine Workflowvorlage hinzu.
GitHub bietet eine Workflowvorlage für Python, die funktionieren sollte, wenn das Repository bereits mindestens eine .py
-Datei enthält. In den nachfolgenden Abschnitten dieser Anleitung finden Sie Beispiele dafür, wie diese Workflowvorlage angepasst werden kann.
-
Navigieren Sie auf GitHub zur Hauptseite des Repositorys.
-
Klicke unter dem Namen deines Repositorys auf Aktionen.
-
Wenn du bereits über einen Workflow im Repository verfügst, klicke auf Neuer Workflow.
-
Auf der Seite „Workflow auswählen“ wird eine Auswahl empfohlener Workflowvorlagen angezeigt. Suchen Sie nach „Python-Anwendung“.
-
Klicken Sie im Workflow „Python-Anwendung“ auf Konfigurieren.
Wenn Sie die Workflowvorlage „Python-Anwendung“ nicht finden, kopieren Sie den folgenden Workflowcode in eine neue Datei namens
python-app.yml
im Verzeichnis.github/workflows
Ihres Repositorys.YAML name: Python application on: push: branches: [ "main" ] pull_request: branches: [ "main" ] permissions: contents: read jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python 3.13 uses: actions/setup-python@v5 with: python-version: "3.13" - name: Install dependencies run: | python -m pip install --upgrade pip pip install ruff pytest if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Lint and format Python code with ruff run: | # Lint with the default set of ruff rules with GitHub Annotations ruff check --format=github --target-version=py39 # Verify the code is properly formatted ruff format --diff --target-version=py39 - name: Test with pytest run: | pytest
name: Python application on: push: branches: [ "main" ] pull_request: branches: [ "main" ] permissions: contents: read jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python 3.13 uses: actions/setup-python@v5 with: python-version: "3.13" - name: Install dependencies run: | python -m pip install --upgrade pip pip install ruff pytest if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Lint and format Python code with ruff run: | # Lint with the default set of ruff rules with GitHub Annotations ruff check --format=github --target-version=py39 # Verify the code is properly formatted ruff format --diff --target-version=py39 - name: Test with pytest run: | pytest
-
Bearbeiten Sie den Workflow nach Bedarf. Ändern Sie beispielsweise die Python-Version.
-
Klicke auf Änderungen committen.
Eine Python-Version angeben
Verwende die Aktion setup-python
, um eine vorinstallierte Version von Python oder PyPy für einen von GitHub gehosteten Runner zu verwenden. Mit dieser Aktion wird im Toolcache der jeweiligen Runner nach einer bestimmten Version von Python oder PyPy gesucht, und die erforderlichen Binärdateien werden zu PATH
hinzugefügt, das für den Rest des Auftrags bestehen bleibt. Wenn eine bestimmte Version von Python im Toolcache nicht vorinstalliert ist, wird die entsprechende Version mit der Aktion setup-python
aus dem Repository python-versions
heruntergeladen und eingerichtet.
Die Aktion setup-python
wird als Methode zur Verwendung von Python mit GitHub Actions empfohlen, da damit ein konsistentes Verhalten bei verschiedenen Runnern und verschiedenen Version von Python gewährleistet wird. Wenn du einen selbst gehosteten Runner verwendest, musst du Python installieren und zu PATH
hinzufügen. Weitere Informationen findest du im Artikel über die Aktion setup-python
.
Die folgende Tabelle zeigt für jeden GitHub-gehosteten Runner, wo der Tools-Cache liegt.
Ubuntu | Mac | Windows | |
---|---|---|---|
Tool Cache Directory | /opt/hostedtoolcache/* | /Users/runner/hostedtoolcache/* | C:\hostedtoolcache\windows\* |
Python Tool Cache | /opt/hostedtoolcache/Python/* | /Users/runner/hostedtoolcache/Python/* | C:\hostedtoolcache\windows\Python\* |
PyPy Tool Cache | /opt/hostedtoolcache/PyPy/* | /Users/runner/hostedtoolcache/PyPy/* | C:\hostedtoolcache\windows\PyPy\* |
Wenn du einen selbst gehosteten Runner verwendest, kannst du ihn so konfigurieren, dass die Aktion setup-python
zum Verwalten von Abhängigkeiten verwendet wird. Weitere Informationen finden Sie unter Verwenden von „setup-python“ mit einem selbst gehosteten Runner in der Infodatei zu setup-python
.
GitHub unterstützt dir Syntax für semantische Versionierung. Weitere Informationen finden Sie unter Using semantic versioning (Verwenden der semantischen Versionsverwaltung) und unter Semantic versioning specification (Spezifikation zur semantischen Versionsverwaltung).
Mehrere Python-Versionen verwenden
Im folgenden Beispiel wird eine Matrix für den Auftrag verwendet, um mehrere Python-Versionen einzurichten. Weitere Informationen findest du unter Ausführen von Variationen von Aufträgen in einem Workflow.
name: Python package on: [push] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: ["pypy3.10", "3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} # You can test your matrix by printing the current Python version - name: Display Python version run: python -c "import sys; print(sys.version)"
name: Python package
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["pypy3.10", "3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
# You can test your matrix by printing the current Python version
- name: Display Python version
run: python -c "import sys; print(sys.version)"
Eine bestimmten Python-Version verwenden
Du kannst eine bestimmte Version von Python konfigurieren. Beispiel: 3.12. Alternativ kannst du auch Syntax für semantische Versionierung verwenden, um die neuste Nebenversion abzurufen. Dieses Beispiel verwendet das neueste Minor Release von Python 3.
name: Python package on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python # This is the version of the action for setting up Python, not the Python version. uses: actions/setup-python@v5 with: # Semantic version range syntax or exact version of a Python version python-version: '3.x' # Optional - x64 or x86 architecture, defaults to x64 architecture: 'x64' # You can test your matrix by printing the current Python version - name: Display Python version run: python -c "import sys; print(sys.version)"
name: Python package
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
# This is the version of the action for setting up Python, not the Python version.
uses: actions/setup-python@v5
with:
# Semantic version range syntax or exact version of a Python version
python-version: '3.x'
# Optional - x64 or x86 architecture, defaults to x64
architecture: 'x64'
# You can test your matrix by printing the current Python version
- name: Display Python version
run: python -c "import sys; print(sys.version)"
Eine Version ausschließen
Wenn du eine nicht verfügbare Python-Version angibst, schlägt setup-python
mit einer Fehlermeldung wie der folgenden fehl: ##[error]Version 3.7 with arch x64 not found
. Die Fehlermeldung enthält die verfügbaren Versionen.
Du kannst im Workflow auch das Schlüsselwort exclude
verwenden, wenn es eine Konfiguration von Python gibt, die du nicht verwenden möchtest. Weitere Informationen findest du unter Workflowsyntax für GitHub Actions.
name: Python package on: [push] jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] python-version: ["3.9", "3.11", "3.13", "pypy3.10"] exclude: - os: macos-latest python-version: "3.11" - os: windows-latest python-version: "3.11"
name: Python package
on: [push]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.9", "3.11", "3.13", "pypy3.10"]
exclude:
- os: macos-latest
python-version: "3.11"
- os: windows-latest
python-version: "3.11"
Die Standard-Version von Python verwenden
Es wird empfohlen, setup-python
zu verwenden, um die in den eigenen Workflows verwendete Version von Python zu konfigurieren, da du so Abhängigkeiten explizit machen kannst. Wenn du setup-python
nicht verwendest, wird die in PATH
festgelegte Standardversion von Python in allen Shells beim Aufrufen von python
verwendet. Die Standardversion von Python variiert zwischen den GitHub-gehosteten Runnern. Dies kann zu unerwarteten Abweichungen führen oder es kann unerwartet eine ältere Version verwendet werden.
GitHub-gehostete Runner | BESCHREIBUNG |
---|---|
Ubuntu | Auf Ubuntu-Runnern sind unter /usr/bin/python und /usr/bin/python3 mehrere Systemversionen von Python installiert. Die Python-Versionen, die mit Ubuntu mitgeliefert werden, sind zusätzlich zu den Versionen, die GitHub im Tools-Cache installiert. |
Windows | Neben den Python-Versionen, die sich im Tools-Cache befinden, kommt Windows nicht mit einer entsprechenden Version von System-Python. Zur Gewährleistung eines konsistenten Verhaltens bei anderen Runnern und um die Verwendung von Python ohne Änderungen und ohne die Aktion setup-python zu ermöglichen, werden mit GitHub einige Versionen aus PATH zum Toolcache hinzugefügt. |
macOS | Auf macOS-Runnern sind zusätzlich zu den Versionen im Tool-Cache noch mehrere Versionen von System-Python installiert. Die Systemversionen von Python befinden sich im Verzeichnis /usr/local/Cellar/python/* . |
Installieren von Abhängigkeiten
Auf GitHub-gehosteten Runnern ist der Paketmanager pip installiert. Du kannst pip verwenden, um Abhängigkeiten von der PyPI-Paket-Registry zu installieren, bevor du deinen Code baust und testest. Mit dem folgenden YAML-Code werden beispielsweise das pip
-Paketinstallationsprogramm und die Pakete setuptools
und wheel
installiert oder aktualisiert.
Sie können auch Abhängigkeiten zwischenspeichern, um Ihren Workflow zu beschleunigen. Weitere Informationen findest du unter Abhängigkeiten zwischenspeichern um Workflows zu beschleunigen.
steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x' - name: Install dependencies run: python -m pip install --upgrade pip setuptools wheel
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: python -m pip install --upgrade pip setuptools wheel
Datei für „Requirements“ (Anforderungen)
Nach dem Aktualisieren von pip
werden in der Regel als Nächstes Abhängigkeiten aus der Datei requirements.txt installiert. Weitere Informationen finden Sie unter pip.
steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
Abhängigkeiten im Cache zwischenspeichern
Du kannst die Abhängigkeiten mithilfe der Aktion setup-python
zwischenspeichern und wiederherstellen.
Im folgenden Beispiel werden Abhängigkeiten für pip zwischengespeichert.
steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.12' cache: 'pip' - run: pip install -r requirements.txt - run: pip test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- run: pip install -r requirements.txt
- run: pip test
Standardmäßig sucht die Aktion setup-python
im gesamten Repository nach der Abhängigkeitsdatei (requirements.txt
für pip, Pipfile.lock
für pipenv oder poetry.lock
für poetry). Weitere Informationen finden Sie unter Zwischenspeichern von Paketabhängigkeiten in der Infodatei zu setup-python
.
Wenn du eine benutzerdefinierte Anforderung verwendest oder genauere Steuerungsmöglichkeiten zum Zwischenspeichern benötigst, kannst du die Aktion cache
verwendest. Mit pip werden Abhängigkeiten je nach dem Betriebssystem des Runners an anderen Speicherorten zwischengespeichert. Der Pfad, den du zwischenspeichern musst, unterscheidet sich möglicherweise vom obigen Ubuntu-Beispiel, je nachdem, welches Betriebssystem du verwendest. Weitere Informationen finden Sie unter Beispiele zum Zwischenspeichern bei Python im Repository der Aktion cache
.
Testen von Code
Du kannst die gleichen Befehle verwenden, die du auch lokal verwendest, um deinen Code zu bauen und zu testen.
Mit pytest und pytest-cov testen
In diesem Beispiel werden pytest
und pytest-cov
installiert und aktualisiert. Tests werden dann im JUnit-Format ausgeführt und ausgegeben, während die Code Coverage-Ergebnisse in Cobertura ausgegeben werden. Weitere Informationen finden Sie unter JUnit und Cobertura.
steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt - name: Test with pytest run: | pip install pytest pytest-cov pytest tests.py --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Test with pytest
run: |
pip install pytest pytest-cov
pytest tests.py --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html
Verwenden von Ruff zum Linten und/oder Formatieren von Code
Im folgenden Beispiel wird ruff
installiert oder aktualisiert und zum Linten aller Dateien verwendet. Weitere Informationen finden Sie unter Ruff.
steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x' - name: Install the code linting and formatting tool Ruff run: pipx install ruff - name: Lint code with Ruff run: ruff check --output-format=github --target-version=py39 - name: Check code formatting with Ruff run: ruff format --diff --target-version=py39 continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install the code linting and formatting tool Ruff
run: pipx install ruff
- name: Lint code with Ruff
run: ruff check --output-format=github --target-version=py39
- name: Check code formatting with Ruff
run: ruff format --diff --target-version=py39
continue-on-error: true
Der Formatierungsschritt hat continue-on-error: true
festgelegt. Dadurch wird verhindert, dass der Workflow fehlschlägt, wenn der Formatierungsschritt nicht erfolgreich ist. Nachdem Sie alle Formatierungsfehler behoben haben, können Sie diese Option entfernen, sodass der Workflow neue Probleme erkennt.
Tests mit Tox ausführen
Mit GitHub Actions kannst du Texts mit tox ausführen und die Arbeit auf mehrere Aufträge verteilen. Du musst tox mit der Option -e py
aufrufen, um die Version von Python im eigenen PATH
auszuwählen, statt eine bestimmte Version anzugeben. Weitere Informationen finden Sie unter tox.
name: Python package on: [push] jobs: build: runs-on: ubuntu-latest strategy: matrix: python: ["3.9", "3.11", "3.13"] steps: - uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: Install tox and any other packages run: pip install tox - name: Run tox # Run tox using the version of Python in `PATH` run: tox -e py
name: Python package
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python: ["3.9", "3.11", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Install tox and any other packages
run: pip install tox
- name: Run tox
# Run tox using the version of Python in `PATH`
run: tox -e py
Workflow-Daten als Artefakte paketieren
Du kannst Artefakte hochladen, um sie nach Abschluss eines Workflows anzuzeigen. Zum Beispiel kann es notwendig sein, Logdateien, Core Dumps, Testergebnisse oder Screenshots zu speichern. Weitere Informationen findest du unter Speichern und Freigeben von Daten aus einem Workflow.
Im folgenden Beispiel wird gezeigt, wie die Aktion upload-artifact
zum Archivieren von Testergebnissen aus der Ausführung von pytest
verwendet werden kann. Weitere Informationen findest du im Artikel über die Aktion upload-artifact
.
name: Python package on: [push] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 - name: Setup Python # Set Python version uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} # Install pip and pytest - name: Install dependencies run: | python -m pip install --upgrade pip pip install pytest - name: Test with pytest run: pytest tests.py --doctest-modules --junitxml=junit/test-results-${{ matrix.python-version }}.xml - name: Upload pytest test results uses: actions/upload-artifact@v3 with: name: pytest-results-${{ matrix.python-version }} path: junit/test-results-${{ matrix.python-version }}.xml # Use always() to always run this step to publish test results when there are test failures if: ${{ always() }}
name: Python package
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Setup Python # Set Python version
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
# Install pip and pytest
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
- name: Test with pytest
run: pytest tests.py --doctest-modules --junitxml=junit/test-results-${{ matrix.python-version }}.xml
- name: Upload pytest test results
uses: actions/upload-artifact@v3
with:
name: pytest-results-${{ matrix.python-version }}
path: junit/test-results-${{ matrix.python-version }}.xml
# Use always() to always run this step to publish test results when there are test failures
if: ${{ always() }}
Veröffentlichen in PyPI
Sie können Ihren Workflow so konfigurieren, dass das Python-Paket in einer Paketregistrierung veröffentlicht wird, wenn Ihre CI-Tests bestanden werden. In diesem Abschnitt erfährst du, wie du GitHub Actions verwenden kannst, um dein Paket bei einer Veröffentlichung eines Releases in PyPI hochzuladen. Weitere Informationen findest du unter Veröffentlichungen in einem Repository verwalten.
Im folgenden Beispielworkflow wird der vertrauenswürdige Herausgeber für die Authentifizierung bei PyPI verwendet, ohne dass ein manuell konfiguriertes API-Token erforderlich ist.
# Dieser Workflow verwendet Aktionen, die nicht von GitHub zertifiziert sind. # Sie werden von einem Drittanbieter bereitgestellt und unterliegen # separaten Nutzungsbedingungen, Datenschutzbestimmungen und Support # Onlinedokumentation. # GitHub empfiehlt, Aktionen an einen Commit-SHA anzuheften. # Um eine neuere Version zu erhalten, musst du den SHA aktualisieren. # Du kannst auch auf ein Tag oder einen Branch verweisen, aber die Aktion kann sich ohne Vorwarnung ändern. name: Upload Python Package on: release: types: [published] permissions: contents: read jobs: release-build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.x" - name: Build release distributions run: | # NOTE: put your own distribution build steps here. python -m pip install build python -m build - name: Upload distributions uses: actions/upload-artifact@v3 with: name: release-dists path: dist/ pypi-publish: runs-on: ubuntu-latest needs: - release-build permissions: # IMPORTANT: this permission is mandatory for trusted publishing id-token: write # Dedicated environments with protections for publishing are strongly recommended. environment: name: pypi # OPTIONAL: uncomment and update to include your PyPI project URL in the deployment status: # url: https://pypi.org/p/YOURPROJECT steps: - name: Retrieve release distributions uses: actions/download-artifact@v3 with: name: release-dists path: dist/ - name: Publish release distributions to PyPI uses: pypa/gh-action-pypi-publish@6f7e8d9c0b1a2c3d4e5f6a7b8c9d0e1f2a3b4c5d
# Dieser Workflow verwendet Aktionen, die nicht von GitHub zertifiziert sind.
# Sie werden von einem Drittanbieter bereitgestellt und unterliegen
# separaten Nutzungsbedingungen, Datenschutzbestimmungen und Support
# Onlinedokumentation.
# GitHub empfiehlt, Aktionen an einen Commit-SHA anzuheften.
# Um eine neuere Version zu erhalten, musst du den SHA aktualisieren.
# Du kannst auch auf ein Tag oder einen Branch verweisen, aber die Aktion kann sich ohne Vorwarnung ändern.
name: Upload Python Package
on:
release:
types: [published]
permissions:
contents: read
jobs:
release-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Build release distributions
run: |
# NOTE: put your own distribution build steps here.
python -m pip install build
python -m build
- name: Upload distributions
uses: actions/upload-artifact@v3
with:
name: release-dists
path: dist/
pypi-publish:
runs-on: ubuntu-latest
needs:
- release-build
permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write
# Dedicated environments with protections for publishing are strongly recommended.
environment:
name: pypi
# OPTIONAL: uncomment and update to include your PyPI project URL in the deployment status:
# url: https://pypi.org/p/YOURPROJECT
steps:
- name: Retrieve release distributions
uses: actions/download-artifact@v3
with:
name: release-dists
path: dist/
- name: Publish release distributions to PyPI
uses: pypa/gh-action-pypi-publish@6f7e8d9c0b1a2c3d4e5f6a7b8c9d0e1f2a3b4c5d