Skip to content

Commit

Permalink
Merge pull request #423 from takluyver/doc-pep621
Browse files Browse the repository at this point in the history
WIP: Document PEP 621 support
  • Loading branch information
takluyver authored Jul 31, 2021
2 parents ce9a6ae + 3f5255a commit 82f1055
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 80 deletions.
265 changes: 229 additions & 36 deletions doc/pyproject_toml.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,230 @@ Build system section
--------------------

This tells tools like pip to build your project with flit. It's a standard
defined by PEP 517. For any project using Flit, it will look like this:
defined by PEP 517. For any new project using Flit, it will look like this:

.. code-block:: toml
[build-system]
requires = ["flit_core >=2,<4"]
requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi"
Metadata section
----------------
Version constraints:

- For now, all packages should specify ``<4``, so they won't be impacted by
changes in the next major version.
- :ref:`pyproject_toml_project` requires ``flit_core >=3.2``
- :ref:`pyproject_old_metadata` requires ``flit_core >=2,<4``
- The older :doc:`flit.ini file <flit_ini>` requires ``flit_core <3``.
- Only ``flit_core`` 2.x can build packages on Python 2, so packages still
supporting Python 2 cannot use new-style metadata (the ``[project]`` table).

.. _pyproject_toml_project:

New style metadata
------------------

.. versionadded:: 3.2

The new standard way to specify project metadata is in a ``[project]`` table,
as defined by :pep:`621`. Flit works for now with either this or the older
``[tool.flit.metadata]`` table (:ref:`described below <pyproject_old_metadata>`),
but it won't allow you to mix them.

A simple ``[project]`` table might look like this:

.. code-block:: toml
[project]
name = "astcheck"
authors = [
{name = "Thomas Kluyver", email = "[email protected]"},
]
readme = "README.rst"
classifiers = [
"License :: OSI Approved :: MIT License",
]
requires-python = ">=3.5"
dynamic = ['version', 'description']
The allowed fields are:

name
The name your package will have on PyPI. This field is required. For Flit,
this also points to your package as an import name by default (see
:ref:`pyproject_module` if that needs to be different).
version
Version number as a string. If you want Flit to get this from a
``__version__`` attribute, leave it out of the TOML config and include
"version" in the ``dynamic`` field.
description
A one-line description of your project. If you want Flit to get this from
the module docstring, leave it out of the TOML config and include
"description" in the ``dynamic`` field.
readme
A path (relative to the .toml file) to a file containing a longer description
of your package to show on PyPI. This should be written in `reStructuredText
<http://docutils.sourceforge.net/docs/user/rst/quickref.html>`_, Markdown or
plain text, and the filename should have the appropriate extension
(``.rst``, ``.md`` or ``.txt``). Alternatively, ``readme`` can be a table with
either a ``file`` key (a relative path) or a ``text`` key (literal text), and
an optional ``content-type`` key (e.g. ``text/x-rst``).
requires-python
A version specifier for the versions of Python this requires, e.g. ``~=3.3`` or
``>=3.3,<4``, which are equivalents.
license
A table with either a ``file`` key (a relative path to a license file) or a
``text`` key (the license text).
authors
A list of tables with ``name`` and ``email`` keys (both optional) describing
the authors of the project.
maintainers
Same format as authors.
keywords
A list of words to help with searching for your package.
classifiers
A list of `Trove classifiers <https://pypi.python.org/pypi?%3Aaction=list_classifiers>`_.
Add ``Private :: Do Not Upload`` into the list to prevent a private package
from being uploaded to PyPI by accident.
dependencies & optional-dependencies
See :ref:`pyproject_project_dependencies`.
urls
See :ref:`pyproject_project_urls`.
scripts & gui-scripts
See :ref:`pyproject_project_scripts`.
entry-points
See :ref:`pyproject_project_entrypoints`.
dynamic
A list of field names which aren't specified here, for which Flit should
find a value at build time. Only "version" and "description" are accepted.

.. _pyproject_project_dependencies:

Dependencies
~~~~~~~~~~~~

The ``dependencies`` field is a list of other packages from PyPI that this
package needs. Each package may be followed by a version specifier like
``>=4.1``, and/or an `environment marker`_
after a semicolon. For example:

.. code-block:: toml
dependencies = [
"requests >=2.6",
"configparser; python_version == '2.7'",
]
The ``[project.optional-dependencies]`` table contains lists of packages needed
for every optional feature. The requirements are specified in the same format as
for ``dependencies``. For example:

.. code-block:: toml
[project.optional-dependencies]
test = [
"pytest >=2.7.3",
"pytest-cov",
]
doc = ["sphinx"]
You can call these optional features anything you want, although ``test`` and
``doc`` are common ones. You specify them for installation in square brackets
after the package name or directory, e.g. ``pip install '.[test]'``.

.. _pyproject_project_urls:

URLs table
~~~~~~~~~~

Your project's page on `pypi.org <https://pypi.org/>`_ can show a number of
links. You can point people to documentation or a bug tracker, for example.

This section is called ``[project.urls]`` in the file. You can use
any names inside it. Here it is for flit:

.. code-block:: toml
[project.urls]
Documentation = "https://flit.readthedocs.io/en/latest/"
Source = "https://github.com/takluyver/flit"
.. _pyproject_project_scripts:

Scripts section
~~~~~~~~~~~~~~~

This section is called ``[project.scripts]`` in the file.
Each key and value describes a shell command to be installed along with
your package. These work like setuptools 'entry points'. Here's the section
for flit:

.. code-block:: toml
[project.scripts]
flit = "flit:main"
This will create a ``flit`` command, which will call the function ``main()``
imported from :mod:`flit`.

A similar table called ``[project.gui-scripts]`` defines commands which launch
a GUI. This only makes a difference on Windows, where GUI scripts are run
without a console.

.. _pyproject_project_entrypoints:

Entry points sections
~~~~~~~~~~~~~~~~~~~~~

You can declare `entry points <http://entrypoints.readthedocs.io/en/latest/>`_
using sections named :samp:`[project.entry-points.{groupname}]`. E.g. to
provide a pygments lexer from your package:

.. code-block:: toml
[project.entry-points."pygments.lexers"]
dogelang = "dogelang.lexer:DogeLexer"
In each ``package:name`` value, the part before the colon should be an
importable module name, and the latter part should be the name of an object
accessible within that module. The details of what object to expose depend on
the application you're extending.

If the group name contains a dot, it must be quoted (``"pygments.lexers"``
above). Script entry points are defined in :ref:`scripts tables
<pyproject_project_scripts>`, so you can't use the group names
``console_scripts`` or ``gui_scripts`` here.

.. _pyproject_module:

Module section
~~~~~~~~~~~~~~

If your package will have different names for installation and import,
you should specify the install (PyPI) name in the ``[project]`` table
(:ref:`see above <pyproject_toml_project>`), and the import name in a
``[tool.flit.module]`` table:

.. code-block:: toml
[project]
name = "pynsist"
# ...
[tool.flit.module]
name = "nsist"
.. _pyproject_old_metadata:

Old style metadata
------------------

Flit's older way to specify metadata is in a ``[tool.flit.metadata]`` table,
along with ``[tool.flit.scripts]`` and ``[tool.flit.entrypoints]``, described
below. This is still recognised for now, but you can't mix it with
:ref:`pyproject_toml_project`.

This section is called ``[tool.flit.metadata]`` in the file.
There are three required fields:

module
Expand Down Expand Up @@ -56,8 +268,7 @@ home-page
requires
A list of other packages from PyPI that this package needs. Each package may
be followed by a version specifier like ``(>=4.1)`` or ``>=4.1``, and/or an
`environment marker
<https://www.python.org/dev/peps/pep-0345/#environment-markers>`_
`environment marker`_
after a semicolon. For example:

.. code-block:: toml
Expand Down Expand Up @@ -140,7 +351,7 @@ URLs subsection
~~~~~~~~~~~~~~~

Your project's page on `pypi.org <https://pypi.org/>`_ can show a number of
links, in addition to the required ``home-page`` URL described above. You can
links, in addition to the ``home-page`` URL described above. You can
point people to documentation or a bug tracker, for example.

This section is called ``[tool.flit.metadata.urls]`` in the file. You can use
Expand All @@ -156,38 +367,18 @@ any names inside it. Here it is for flit:
.. _pyproject_toml_scripts:

Scripts section
---------------

This section is called ``[tool.flit.scripts]`` in the file.
Each key and value describes a shell command to be installed along with
your package. These work like setuptools 'entry points'. Here's the section
for flit:

.. code-block:: toml
[tool.flit.scripts]
flit = "flit:main"
~~~~~~~~~~~~~~~

This will create a ``flit`` command, which will call the function ``main()``
imported from :mod:`flit`.
A ``[tool.flit.scripts]`` table can be used along with ``[tool.flit.metadata]``.
It is in the same format as the newer ``[project.scripts]`` table
:ref:`described above <pyproject_project_scripts>`.

Entry points sections
---------------------

You can declare `entry points <http://entrypoints.readthedocs.io/en/latest/>`_
using sections named :samp:`[tool.flit.entrypoints.{groupname}]`. E.g. to
provide a pygments lexer from your package:

.. code-block:: toml
~~~~~~~~~~~~~~~~~~~~~

[tool.flit.entrypoints."pygments.lexers"]
dogelang = "dogelang.lexer:DogeLexer"
In each ``package:name`` value, the part before the colon should be an
importable module name, and the latter part should be the name of an object
accessible within that module. The details of what object to expose depend on
the application you're extending.
``[tool.flit.entrypoints]`` tables can be used along with ``[tool.flit.metadata]``.
They are in the same format as the newer ``[project.entry-points]`` tables
:ref:`described above <pyproject_project_entrypoints>`.

.. _pyproject_toml_sdist:

Expand Down Expand Up @@ -221,3 +412,5 @@ These paths:
is platform dependent

Exclusions have priority over inclusions.

.. _environment marker: https://www.python.org/dev/peps/pep-0508/#environment-markers
48 changes: 32 additions & 16 deletions flit/init.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from collections import OrderedDict
from datetime import date
import json
import os
Expand Down Expand Up @@ -181,7 +180,7 @@ def initialise(self):
module = self.prompt_text('Module name', self.guess_module_name(),
str.isidentifier)
author = self.prompt_text('Author', self.defaults.get('author'),
lambda s: s != '')
lambda s: True)
author_email = self.prompt_text('Author email',
self.defaults.get('author_email'), self.validate_email)
if 'home_page_template' in self.defaults:
Expand All @@ -199,33 +198,50 @@ def initialise(self):
self.update_defaults(author=author, author_email=author_email,
home_page=home_page, module=module, license=license)

metadata = OrderedDict([
('module', module),
('author', author),
])
# Format information as TOML
# This is ugly code, but I want the generated pyproject.toml, which
# will mostly be edited by hand, to look a particular way - e.g. authors
# in inline tables. It's easier to 'cheat' with some string formatting
# than to do this through a TOML library.
author_info = []
if author:
author_info.append(f'name = {json.dumps(author)}')
if author_email:
metadata['author-email'] = author_email
if home_page:
metadata['home-page'] = home_page
author_info.append(f'email = {json.dumps(author_email)}')
if author_info:
authors_list = "[{%s}]" % ", ".join(author_info)
else:
authors_list = "[]"

classifiers = []
if license != 'skip':
metadata['classifiers'] = [license_names_to_classifiers[license]]
classifiers = [license_names_to_classifiers[license]]
self.write_license(license, author)
if readme:
metadata['description-file'] = readme

with (self.directory / 'pyproject.toml').open('w', encoding='utf-8') as f:
f.write(TEMPLATE.format(metadata=toml.dumps(metadata)))
f.write(TEMPLATE.format(
name=json.dumps(module), authors=authors_list
))
if readme:
toml.dump({'readme': readme}, f)
if classifiers:
f.write(f"classifiers = {json.dumps(classifiers)}\n")
f.write('dynamic = ["version", "description"]\n')
if home_page:
f.write("\n")
toml.dump({'project': {'urls': {'Home': home_page}}}, f)

print()
print("Written pyproject.toml; edit that file to add optional extra info.")

TEMPLATE = """\
[build-system]
requires = ["flit_core >=2,<4"]
requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi"
[tool.flit.metadata]
{metadata}
[project]
name = {name}
authors = {authors}
"""

if __name__ == '__main__':
Expand Down
Loading

0 comments on commit 82f1055

Please sign in to comment.