Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion source/guides/writing-pyproject-toml.rst
Original file line number Diff line number Diff line change
Expand Up @@ -399,13 +399,21 @@ To prevent a package from being uploaded to PyPI, use the special ``Private ::
Do Not Upload`` classifier. PyPI will always reject packages with classifiers
beginning with ``Private ::``.

.. _writing-pyproject-toml-urls:

``urls``
--------

A list of URLs associated with your project, displayed on the left
sidebar of your PyPI project page.

.. note::

See :ref:`well-known-labels` for a listing
of labels that PyPI and other packaging tools are specifically aware of,
and `PyPI's project metadata docs <https://docs.pypi.org/project_metadata/#project-urls>`_
for PyPI-specific URL processing.

.. code-block:: toml

[project.urls]
Expand All @@ -415,11 +423,34 @@ sidebar of your PyPI project page.
Issues = "https://github.com/me/spam/issues"
Changelog = "https://github.com/me/spam/blob/master/CHANGELOG.md"

Note that if the key contains spaces, it needs to be quoted, e.g.,
Note that if the label contains spaces, it needs to be quoted, e.g.,
``Website = "https://example.com"`` but
``"Official Website" = "https://example.com"``.

Users are advised to use :ref:`well-known-labels` for their project URLs
where appropriate, since consumers of metadata (like package indices) can
specialize their presentation.

For example in the following metadata, neither ``MyHomepage`` nor
``"Download Link"`` is a well-known label, so they will be rendered verbatim:

.. code-block:: toml

[project.urls]
MyHomepage = "https://example.com"
"Download Link" = "https://example.com/abc.tar.gz"


Whereas in this metadata ``HomePage`` and ``DOWNLOAD`` both have
well-known equivalents (``homepage`` and ``download``), and can be presented
with those semantics in mind (the project's home page and its external
download location, respectively).

.. code-block:: toml

[project.urls]
HomePage = "https://example.com"
DOWNLOAD = "https://example.com/abc.tar.gz"

Advanced plugins
================
Expand Down
64 changes: 38 additions & 26 deletions source/specifications/core-metadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -341,32 +341,6 @@ Example::
These tools have been very widely used for many years, so it was
easier to update the specification to match the de facto standard.

.. _home-page-optional:
.. _core-metadata-home-page:

Home-page
=========

.. versionadded:: 1.0

A string containing the URL for the distribution's home page.

Example::

Home-page: http://www.example.com/~cschultz/bvote/

.. _core-metadata-download-url:

Download-URL
============

.. versionadded:: 1.1

A string containing the URL from which this version of the distribution
can be downloaded. (This means that the URL can't be something like
".../BeagleVote-latest.tgz", but instead must be ".../BeagleVote-0.45.tgz".)


.. _author-optional:
.. _core-metadata-author:

Expand Down Expand Up @@ -669,6 +643,10 @@ Example::

The label is free text limited to 32 characters.

Starting with :pep:`753`, project metadata consumers (such as the Python
Package Index) can use a standard normalization process to discover "well-known"
labels, which can then be given special presentations when being rendered
for human consumption. See :ref:`well-known-project-urls`.

.. _metadata_provides_extra:
.. _core-metadata-provides-extra:
Expand Down Expand Up @@ -819,6 +797,40 @@ Examples::
Deprecated Fields
=================

.. _home-page-optional:
.. _core-metadata-home-page:

Home-page
---------

.. versionadded:: 1.0

.. deprecated:: 1.2

Per :pep:`753`, use :ref:`core-metadata-project-url` instead.

A string containing the URL for the distribution's home page.

Example::

Home-page: http://www.example.com/~cschultz/bvote/

.. _core-metadata-download-url:

Download-URL
------------

.. versionadded:: 1.1

.. deprecated:: 1.2

Per :pep:`753`, use :ref:`core-metadata-project-url` instead.

A string containing the URL from which this version of the distribution
can be downloaded. (This means that the URL can't be something like
"``.../BeagleVote-latest.tgz``", but instead must be
"``.../BeagleVote-0.45.tgz``".)

Requires
--------

Expand Down
3 changes: 2 additions & 1 deletion source/specifications/pyproject-toml.rst
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,8 @@ Trove classifiers which apply to the project.
:ref:`Project-URL <core-metadata-project-url>`

A table of URLs where the key is the URL label and the value is the
URL itself.
URL itself. See :ref:`well-known-project-urls` for normalization rules
and well-known rules when processing metadata for presentation.


Entry points
Expand Down
1 change: 1 addition & 0 deletions source/specifications/section-distribution-metadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Package Distribution Metadata
pyproject-toml
inline-script-metadata
platform-compatibility-tags
well-known-project-urls
176 changes: 176 additions & 0 deletions source/specifications/well-known-project-urls.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
.. _`well-known-project-urls`:

===================================
Well-known Project URLs in Metadata
===================================

.. important::

This document is primarily of interest to metadata *consumers*,
who should use the normalization rules and well-known list below
to make their presentation of project URLs consistent across the
Python ecosystem.

Metadata *producers* (such as build tools and individual package
maintainers) may continue to use any labels they please, within the
overall ``Project-URL`` length restrictions. However, when possible, users are
*encouraged* to pick meaningful labels that normalize to well-known
labels.

.. note::

See :ref:`Writing your pyproject.toml - urls <writing-pyproject-toml-urls>`
for user-oriented guidance on choosing project URL labels in your package's
metadata.

.. note:: This specification was originally defined in :pep:`753`.

:pep:`753` deprecates the :ref:`core-metadata-home-page` and
:ref:`core-metadata-download-url` metadata fields in favor of
:ref:`core-metadata-project-url`, and defines a normalization and
lookup procedure for determining whether a ``Project-URL`` is
"well-known," i.e. has the semantics assigned to ``Home-page``,
``Download-URL``, or other common project URLs.

This allows indices (such as the Python Package Index) and other downstream
metadata consumers to present project URLs in a
consistent manner.

.. _project-url-label-normalization:

Label normalization
===================

.. note::

Label normalization is performed by metadata *consumers*, not metadata
producers.

To determine whether a ``Project-URL`` label is "well-known," metadata
consumers should normalize the label before comparing it to the
:ref:`list of well-known labels <well-known-labels>`.

The normalization procedure for ``Project-URL`` labels is defined
by the following Python function:

.. code-block:: python

import string

def normalize_label(label: str) -> str:
chars_to_remove = string.punctuation + string.whitespace
removal_map = str.maketrans("", "", chars_to_remove)
return label.translate(removal_map).lower()

In plain language: a label is *normalized* by deleting all ASCII punctuation
and whitespace, and then converting the result to lowercase.

The following table shows examples of labels before (raw) and after
normalization:

.. list-table::
:header-rows: 1

* - Raw
- Normalized
* - ``Homepage``
- ``homepage``
* - ``Home-page``
- ``homepage``
* - ``Home page``
- ``homepage``
* - ``Change_Log``
- ``changelog``
* - ``What's New?``
- ``whatsnew``
* - ``github``
- ``github``

.. _well-known-labels:

Well-known labels
=================

.. note::

The list of well-known labels is a living standard, maintained as part of
this document.

The following table lists labels that are well-known for the purpose of
specializing the presentation of ``Project-URL`` metadata:

.. list-table::
:header-rows: 1

* - Label (Human-readable equivalent)
- Description
- Aliases
* - ``homepage`` (Homepage)
- The project's home page
- *(none)*
* - ``source`` (Source Code)
- The project's hosted source code or repository
- ``repository``, ``sourcecode``, ``github``
* - ``download`` (Download)
- A download URL for the current distribution, equivalent to ``Download-URL``
- *(none)*
* - ``changelog`` (Changelog)
- The project's comprehensive changelog
- ``changes``, ``whatsnew``, ``history``
* - ``releasenotes`` (Release Notes)
- The project's curated release notes
- *(none)*
* - ``documentation`` (Documentation)
- The project's online documentation
- ``docs``
* - ``issues`` (Issue Tracker)
- The project's bug tracker
- ``bugs``, ``issue``, ``tracker``, ``issuetracker``, ``bugtracker``
* - ``funding`` (Funding)
- Funding Information
- ``sponsor``, ``donate``, ``donation``

Package metadata consumers may choose to render aliased labels the same as
their "parent" well known label, or further specialize them.

Example behavior
================

The following shows the flow of project URL metadata from
``pyproject.toml`` to core metadata to a potential index presentation:

.. code-block:: toml
:caption: Example project URLs in standard configuration

[project.urls]
"Home Page" = "https://example.com"
DOCUMENTATION = "https://readthedocs.org"
Repository = "https://upstream.example.com/me/spam.git"
GitHub = "https://github.com/example/spam"

.. code-block:: email
:caption: Core metadata representation

Project-URL: Home page, https://example.com
Project-URL: DOCUMENTATION, https://readthedocs.org
Project-URL: Repository, https://upstream.example.com/me/spam.git
Project-URL: GitHub, https://github.com/example/spam

.. code-block:: text
:caption: Potential rendering

Homepage: https://example.com
Documentation: https://readthedocs.org
Source Code: https://upstream.example.com/me/spam.git
Source Code (GitHub): https://github.com/example/spam

Observe that the core metadata appears in the form provided by the user
(since metadata *producers* do not perform normalization), but the
metadata *consumer* normalizes and identifies appropriate
human-readable equivalents based on the normalized form:

* ``Home page`` becomes ``homepage``, which is rendered as ``Homepage``
* ``DOCUMENTATION`` becomes ``documentation``, which is rendered as ``Documentation``
* ``Repository`` becomes ``repository``, which is rendered as ``Source Code``
* ``GitHub`` becomes ``github``, which is rendered as ``Source Code (GitHub)``
(as a specialization of ``Source Code``)
Loading