Repository: SciTools/iris (language: python)

Issue:
automate pypi manifest checking
## 🚀 Pull Request

### Description
<!-- Provide a clear description about your awesome pull request -->
<!-- Tell us all about your new feature, improvement, or bug fix -->
This PR ensures that the assets packaged for the PyPI sdist and wheel are as expected by performing an automated GHA check of the payload manifest.

Adopts the reusable `SciTools` GHA workflow, which will be automagically updated thanks to the already configured `dependabot`.

Note that, this PR fixes several discrepancies.

---
[Consult Iris pull request check list]( https://scitools-iris.readthedocs.io/en/latest/developers_guide/contributing_pull_request_checklist.html)

automate pypi manifest checking
## 🚀 Pull Request

### Description
<!-- Provide a clear description about your awesome pull request -->
<!-- Tell us all about your new feature, improvement, or bug fix -->
This PR ensures that the assets packaged for the PyPI sdist and wheel are as expected by performing an automated GHA check of the payload manifest.

Adopts the reusable `SciTools` GHA workflow, which will be automagically updated thanks to the already configured `dependabot`.

Note that, this PR fixes several discrepancies.

---
[Consult Iris pull request check list]( https://scitools-iris.readthedocs.io/en/latest/developers_guide/contributing_pull_request_checklist.html)

Support Python 3.11
## 🚀 Pull Request

### Description
<!-- Provide a clear description about your awesome pull request -->
<!-- Tell us all about your new feature, improvement, or bug fix -->
Python 3.11 has been out for [nearly 6 months now](https://www.python.org/downloads/release/python-3110/) so I think we should officially support for the upcoming release.

~I note @trexfeathers' comment at https://github.com/SciTools/iris/pull/5211#issuecomment-1484725649, so appreciate you won't want to merge this immediately. :eyes:~

Needs #5232 

---
[Consult Iris pull request check list]( https://scitools-iris.readthedocs.io/en/latest/developers_guide/contributing_pull_request_checklist.html)



Discussion / hints:


Wow I managed to break **_all_** the tests.  Seems I need some help!

I can report that the main tests and gallery tests all passed locally under 3.11.

Fix patch:
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000000..4f67c422f8
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,49 @@
+[flake8]
+# References:
+#   https://flake8.readthedocs.io/en/latest/user/configuration.html
+#   https://flake8.readthedocs.io/en/latest/user/error-codes.html
+#   https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes
+
+max-line-length = 80
+max-complexity = 50
+select = C,E,F,W,B,B950
+ignore =
+    # E203: whitespace before ':'
+    E203,
+    # E226: missing whitespace around arithmetic operator
+    E226,
+    # E231: missing whitespace after ',', ';', or ':'
+    E231,
+    # E402: module level imports on one line
+    E402,
+    # E501: line too long
+    E501,
+    # E731: do not assign a lambda expression, use a def
+    E731,
+    # W503: line break before binary operator
+    W503,
+    # W504: line break after binary operator
+    W504,
+exclude =
+    #
+    # ignore the following directories
+    #
+    .eggs,
+    build,
+    docs/src/sphinxext/*,
+    tools/*,
+    benchmarks/*,
+    #
+    # ignore auto-generated files
+    #
+    _ff_cross_refrences.py,
+    std_names.py,
+    um_cf_map.py,
+    #
+    # ignore third-party files
+    #
+    gitwash_dumper.py,
+    #
+    # convenience imports
+    #
+    lib/iris/common/__init__.py
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a6c6edfd90..fd05b03b68 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -47,7 +47,6 @@ repos:
     hooks:
     -   id: flake8
         types: [file, python]
-        args: [--config=./setup.cfg]
 
 -   repo: https://github.com/pycqa/isort
     rev: 5.12.0
diff --git a/MANIFEST.in b/MANIFEST.in
index 70b3d74294..94ee69d7c5 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,7 +4,9 @@ prune docs
 prune etc
 recursive-include lib *.cdl *.cml *.json *.md *.py *.template *.txt *.xml
 prune requirements
+recursive-include requirements *.txt
 prune tools
+exclude .flake8
 exclude .git-blame-ignore-revs
 exclude .git_archival.txt
 exclude .gitattributes
@@ -20,8 +22,8 @@ exclude Makefile
 exclude noxfile.py
 
 # files required to build iris.std_names module
-include tools/generate_std_names.py
 include etc/cf-standard-name-table.xml
+include tools/generate_std_names.py
 
 global-exclude *.py[cod]
 global-exclude __pycache__
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index 232ddb7c5a..cd0eca1554 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -8,6 +8,69 @@ requires = [
 # Defined by PEP 517
 build-backend = "setuptools.build_meta"
 
+[project]
+authors = [
+    {name = "Iris Contributors", email = "scitools.pub@gmail.com"}
+]
+classifiers = [
+    "Development Status :: 5 - Production/Stable",
+    "Intended Audience :: Science/Research",
+    "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)",
+    "Operating System :: MacOS",
+    "Operating System :: POSIX",
+    "Operating System :: POSIX :: Linux",
+    "Operating System :: Unix",
+    "Programming Language :: Python",
+    "Programming Language :: Python :: 3 :: Only",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: Implementation :: CPython",
+    "Topic :: Scientific/Engineering",
+    "Topic :: Scientific/Engineering :: Atmospheric Science",
+    "Topic :: Scientific/Engineering :: Visualization",
+]
+dynamic = [
+    "dependencies",
+    "readme",
+    "version",
+]
+description = "A powerful, format-agnostic, community-driven Python package for analysing and visualising Earth science data"
+keywords = [
+    "cf-metadata",
+    "data-analysis",
+    "earth-science",
+    "grib",
+    "netcdf",
+    "meteorology",
+    "oceanography",
+    "space-weather",
+    "ugrid",
+    "visualisation",
+]
+license = {text = "LGPL-3.0-or-later"}
+name = "scitools-iris"
+requires-python = ">=3.8"
+
+[project.urls]
+Code = "https://github.com/SciTools/iris"
+Discussions = "https://github.com/SciTools/iris/discussions"
+Documentation = "https://scitools-iris.readthedocs.io/en/stable/"
+Issues = "https://github.com/SciTools/iris/issues"
+
+[tool.setuptools]
+license-files = ["COPYING", "COPYING.LESSER"]
+zip-safe = false
+
+[tool.setuptools.dynamic]
+dependencies = {file = "requirements/pypi-core.txt"}
+readme = {file = "README.md", content-type = "text/markdown"}
+
+[tool.setuptools.packages.find]
+include = ["iris*"]
+where = ["lib"]
+
 [tool.setuptools_scm]
 write_to = "lib/iris/_version.py"
 local_scheme = "dirty-tag"
diff --git a/requirements/README.md b/requirements/README.md
new file mode 100644
index 0000000000..9d9368b9c2
--- /dev/null
+++ b/requirements/README.md
@@ -0,0 +1,8 @@
+# ⚠️
+
+This directory contains:
+
+- The `locks` directory which contains auto-generated `conda-lock` environment files for each `python` distribution and `platform` supported by `iris`.
+- The **top-level** `conda` environment `*.yml` files for each `python` distribution supported by `iris`.
+- The `pip` core package dependencies (`pypi-core.txt`) for the [scitools-iris](https://pypi.org/project/scitools-iris/) package on PyPI. Please reference the `pyproject.toml` in the repository root directory for further details.
+
diff --git a/requirements/pypi-core.txt b/requirements/pypi-core.txt
new file mode 100644
index 0000000000..f24002a16e
--- /dev/null
+++ b/requirements/pypi-core.txt
@@ -0,0 +1,12 @@
+cartopy>=0.21
+cf-units>=3.1
+cftime>=1.5.0
+dask[array]>=2022.9.0
+# libnetcdf<4.9 (not available on PyPI)
+matplotlib>=3.5
+netcdf4
+numpy>=1.19
+pyproj
+scipy
+shapely!=1.8.3
+xxhash
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 1d3ed46e5f..0000000000
--- a/setup.cfg
+++ /dev/null
@@ -1,141 +0,0 @@
-[metadata]
-author = SciTools Developers
-author_email = scitools.pub@gmail.com
-classifiers =
-    Development Status :: 5 - Production/Stable
-    Intended Audience :: Science/Research
-    License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
-    Operating System :: MacOS
-    Operating System :: POSIX
-    Operating System :: POSIX :: Linux
-    Operating System :: Unix
-    Programming Language :: Python
-    Programming Language :: Python :: 3 :: Only
-    Programming Language :: Python :: 3.8
-    Programming Language :: Python :: 3.9
-    Programming Language :: Python :: 3.10
-    Programming Language :: Python :: 3.11
-    Programming Language :: Python :: Implementation :: CPython
-    Topic :: Scientific/Engineering
-    Topic :: Scientific/Engineering :: Atmospheric Science
-    Topic :: Scientific/Engineering :: Visualization
-description = A powerful, format-agnostic, community-driven Python package for analysing and visualising Earth science data
-download_url = https://github.com/SciTools/iris
-keywords =
-    cf-metadata
-    data-analysis
-    earth-science
-    grib
-    netcdf
-    meteorology
-    oceanography
-    space-weather
-    ugrid
-    visualisation
-license = LGPL-3.0-or-later
-license_files = COPYING.LESSER
-long_description = file: README.md
-long_description_content_type = text/markdown
-name = scitools-iris
-project_urls =
-    Code = https://github.com/SciTools/iris
-    Discussions = https://github.com/SciTools/iris/discussions
-    Documentation = https://scitools-iris.readthedocs.io/en/stable/
-    Issues = https://github.com/SciTools/iris/issues
-url = https://github.com/SciTools/iris
-version = attr: iris.__version__
-
-[options]
-include_package_data = True
-install_requires =
-    cartopy>=0.21
-    cf-units>=3.1
-    cftime>=1.5.0
-    dask[array]>=2022.9.0
-    matplotlib>=3.5
-    netcdf4
-    numpy>=1.19
-    scipy
-    shapely!=1.8.3
-    xxhash
-packages = find_namespace:
-package_dir =
-    =lib
-python_requires =
-    >=3.8
-zip_safe = False
-
-[options.packages.find]
-where = lib
-
-[options.extras_require]
-docs =
-    sphinx<=5.3
-    sphinx-copybutton
-    sphinx-gallery>=0.11.0
-    sphinx-design
-    pydata-sphinx-theme>=0.13.0
-test =
-    filelock
-    imagehash>=4.0
-    pre-commit
-    requests
-    pytest
-    pytest-xdist
-all =
-    mo_pack
-    nc-time-axis>=1.4
-    pandas
-    stratify
-    %(docs)s
-    %(test)s
-
-[flake8]
-# References:
-#   https://flake8.readthedocs.io/en/latest/user/configuration.html
-#   https://flake8.readthedocs.io/en/latest/user/error-codes.html
-#   https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes
-
-max-line-length = 80
-max-complexity = 50
-select = C,E,F,W,B,B950
-ignore =
-    # E203: whitespace before ':'
-    E203,
-    # E226: missing whitespace around arithmetic operator
-    E226,
-    # E231: missing whitespace after ',', ';', or ':'
-    E231,
-    # E402: module level imports on one line
-    E402,
-    # E501: line too long
-    E501,
-    # E731: do not assign a lambda expression, use a def
-    E731,
-    # W503: line break before binary operator
-    W503,
-    # W504: line break after binary operator
-    W504,
-exclude =
-    #
-    # ignore the following directories
-    #
-    .eggs,
-    build,
-    docs/src/sphinxext/*,
-    tools/*,
-    benchmarks/*,
-    #
-    # ignore auto-generated files
-    #
-    _ff_cross_refrences.py,
-    std_names.py,
-    um_cf_map.py,
-    #
-    # ignore third-party files
-    #
-    gitwash_dumper.py,
-    #
-    # convenience imports
-    #
-    lib/iris/common/__init__.py


Test patch:
diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst
index 71372dcd2b..8e3903b802 100644
--- a/docs/src/whatsnew/latest.rst
+++ b/docs/src/whatsnew/latest.rst
@@ -89,6 +89,10 @@ This document explains the changes made to Iris for this release
 #. `@rcomer`_ removed a now redundant copying workaround from Resolve testing.
    (:pull:`5267`)
 
+#. `@bjlittle`_ and `@trexfeathers`_ (reviewer) migrated ``setup.cfg`` to
+   ``pyproject.toml``, as motivated by `PEP-0621`_. (:pull:`5262`)
+
+
 .. comment
     Whatsnew author names (@github name) in alphabetical order. Note that,
     core dev names are automatically included by the common_links.inc:
@@ -102,3 +106,4 @@ This document explains the changes made to Iris for this release
 .. _sphinx-panels: https://github.com/executablebooks/sphinx-panels
 .. _sphinx-design: https://github.com/executablebooks/sphinx-design
 .. _check-manifest: https://github.com/mgedmin/check-manifest
+.. _PEP-0621: https://peps.python.org/pep-0621/
diff --git a/lib/iris/tests/test_coding_standards.py b/lib/iris/tests/test_coding_standards.py
index bdeff96602..ed8ff0c566 100644
--- a/lib/iris/tests/test_coding_standards.py
+++ b/lib/iris/tests/test_coding_standards.py
@@ -79,7 +79,7 @@ def test_python_versions():
     benchmarks_dir = root_dir / "benchmarks"
 
     # Places that are checked:
-    setup_cfg_file = root_dir / "setup.cfg"
+    pyproject_toml_file = root_dir / "pyproject.toml"
     requirements_dir = root_dir / "requirements"
     nox_file = root_dir / "noxfile.py"
     ci_wheels_file = workflows_dir / "ci-wheels.yml"
@@ -89,10 +89,10 @@ def test_python_versions():
 
     text_searches: List[Tuple[Path, str]] = [
         (
-            setup_cfg_file,
+            pyproject_toml_file,
             "\n    ".join(
                 [
-                    "Programming Language :: Python :: " + ver
+                    f'"Programming Language :: Python :: {ver}",'
                     for ver in all_supported
                 ]
             ),