diff --git a/.github/workflows/ciwheel.yml b/.github/workflows/ciwheel.yml
index 77f094fbcbf7add4b20a973498353388dcaf53ae..fc20275873d47fc563897cb4f5ecac5d5ddaf92e 100644
--- a/.github/workflows/ciwheel.yml
+++ b/.github/workflows/ciwheel.yml
@@ -24,38 +24,10 @@ jobs:
         with:
           fetch-depth: 0
           submodules: recursive
-      - name: Update pip
-        run: python -m pip install --upgrade pip
-
-      - name: Build wheels Linux
-        if: ${{ startsWith(matrix.os, 'ubuntu') }}
-        uses: pypa/cibuildwheel@v2.3.0
-        with:
-          output-dir: dist
-        env:
-          CIBW_BEFORE_ALL: yum -y install libxml2-devel
-          CIBW_BEFORE_BUILD: python -m pip install numpy setuptools scikit-build ninja cmake
-          CIBW_BUILD: "cp3*-manylinux_x86_64"
-          CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
-          CIBW_ARCHS_LINUX: x86_64
-          CIBW_REPAIR_WHEEL_COMMAND: 'auditwheel repair -w {dest_dir} {wheel} && python /project/scripts/patchwheel.py {dest_dir}'
-          CIBW_TEST_COMMAND: python -m unittest discover -v -s {project}/python
-
-      - name: Build wheels macos
-        if: ${{ startsWith(matrix.os, 'macos') }}
-        uses: pypa/cibuildwheel@v2.3.0
-        with:
-          output-dir: dist
-        env:
-          MACOSX_DEPLOYMENT_TARGET: "10.15" #needed to undo some CIBW settings
-          CIBW_BEFORE_BUILD: python -m pip install numpy setuptools scikit-build ninja cmake
-          CIBW_BUILD: "cp3*-macosx_x86_64"
-          CIBW_ARCHS_MACOS: x86_64 universal2
-          CIBW_TEST_COMMAND: python -m unittest discover -v -s {project}/python
-
-      # this action runs auditwheel automatically with the following args:
-      # https://cibuildwheel.readthedocs.io/en/stable/options/#repair-wheel-command
-
+      - name: Install cibuildwheel
+        run: python3 -m pip install cibuildwheel
+      - name: Build wheels
+        run: python3 -m cibuildwheel --output-dir dist
       - uses: actions/upload-artifact@v2
         with:
           name: dist
@@ -68,18 +40,16 @@ jobs:
     steps:
       - name: Set up Python
         uses: actions/setup-python@v2
-      - name: Update pip
-        run: python -m pip install --upgrade pip
       - name: Get packages
-        run: python -m pip install numpy setuptools scikit-build ninja cmake
+        run: python3 -m pip install build
       - uses: actions/checkout@v2
         with:
           fetch-depth: 0
           submodules: recursive
       - name: Make sdist
-        run:  python setup.py sdist
+        run: python3 -m build -s
       - name: Install sdist
-        run:  python -m pip install dist/arbor*.tar.gz
+        run:  python3 -m pip install dist/arbor*.tar.gz
       - name: Run Python tests
         run: python3 -m unittest discover -v -s python
       - name: Run Python examples
diff --git a/.github/workflows/test-everything.yml b/.github/workflows/test-everything.yml
index d540ee525605118675c9cfaf15519f28e95a8a6f..21c7d6063595642257c237ba0793ab839cabb300 100644
--- a/.github/workflows/test-everything.yml
+++ b/.github/workflows/test-everything.yml
@@ -18,7 +18,7 @@ jobs:
             os:    "ubuntu-18.04",
             cc:    "gcc-8",
             cxx:   "g++-8",
-            py:    "3.6",
+            py:    "3.7",
             cmake: "3.18.x",
             mpi:   "ON",
             simd:  "OFF"
@@ -28,7 +28,7 @@ jobs:
             os:    "ubuntu-18.04",
             cc:    "clang-8",
             cxx:   "clang++-8",
-            py:    "3.6",
+            py:    "3.7",
             cmake: "3.18.x",
             mpi:   "ON",
             simd:  "OFF"
@@ -38,7 +38,7 @@ jobs:
             os:    "macos-10.15",
             cc:    "clang",
             cxx:   "clang++",
-            py:    "3.6",
+            py:    "3.7",
             cmake: "3.18.x",
             mpi:   "ON",
             simd:  "OFF"
@@ -48,7 +48,7 @@ jobs:
             os:    "ubuntu-20.04",
             cc:    "gcc-10",
             cxx:   "g++-10",
-            py:    "3.9",
+            py:    "3.10",
             cmake: "3.22.x",
             mpi:   "ON",
             simd:  "OFF"
@@ -58,7 +58,7 @@ jobs:
             os:    "ubuntu-20.04",
             cc:    "gcc-10",
             cxx:   "g++-10",
-            py:    "3.9",
+            py:    "3.10",
             cmake: "3.22.x",
             mpi:   "OFF",
             simd:  "ON"
@@ -68,7 +68,7 @@ jobs:
             os:    "ubuntu-20.04",
             cc:    "clang-10",
             cxx:   "clang++-10",
-            py:    "3.9",
+            py:    "3.10",
             cmake: "3.22.x",
             mpi:   "ON",
             simd:  "OFF"
@@ -78,7 +78,7 @@ jobs:
             os:    "macos-11",
             cc:    "clang",
             cxx:   "clang++",
-            py:    "3.9",
+            py:    "3.10",
             cmake: "3.22.x",
             mpi:   "ON",
             simd:  "OFF"
@@ -202,16 +202,12 @@ jobs:
     name: "Pip build test + Python examples test"
     runs-on: ubuntu-latest
     steps:
-      - name: Update pip
-        run: python -m pip install --upgrade pip
-      - name: Install Python packages
-        run:  pip install numpy setuptools scikit-build ninja cmake
       - name: Clone w/ submodules
         uses: actions/checkout@v2
         with:
           submodules: recursive
       - name: Build and install Arbor using pip + build flags
-        run: python3 -m pip install --verbose --install-option="-DARB_VECTORIZE=ON" --install-option="-DARB_ARCH=native" .
+        run: CMAKE_ARGS="-DARB_VECTORIZE=ON -DARB_ARCH=native" python3 -m pip install .
       - name: Check that build flags match
         run: |
           python3 -c "import arbor; print(arbor.config())" | grep -q "'arch': 'native'"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 48acc89b8d20808bb25ae986d0df45edb74731c6..69c18331746ed3afa08df83e028c1d42d38a5c92 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -302,7 +302,7 @@ endif()
 #----------------------------------------------------------
 
 # The minimum version of Python supported by Arbor.
-set(arb_py_version 3.6.0)
+set(arb_py_version 3.7.0)
 
 if(DEFINED PYTHON_EXECUTABLE)
     set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE})
@@ -311,7 +311,7 @@ endif()
 if(ARB_WITH_PYTHON)
     cmake_dependent_option(ARB_USE_BUNDLED_PYBIND11 "Use bundled pybind11" ON "ARB_WITH_PYTHON;ARB_USE_BUNDLED_LIBS" OFF)
 
-    if(DEFINED ENV{CIBUILDWHEEL})
+    if(DEFINED ENV{CIBUILDWHEEL} AND (UNIX AND NOT APPLE))
         find_package(Python3 ${arb_py_version} COMPONENTS Interpreter Development.Module REQUIRED)
     else()
         find_package(Python3 ${arb_py_version} COMPONENTS Interpreter Development REQUIRED)
diff --git a/doc/dependencies.csv b/doc/dependencies.csv
index 8cbc63c4bc747b683c8c864c5d68ba121e9732e9..f11919b43e305e9e530c15f88f7672ca862b8deb 100644
--- a/doc/dependencies.csv
+++ b/doc/dependencies.csv
@@ -19,7 +19,7 @@ bench,Google-benchmark,,submodule ``ext/google-benchmark``,
 --,fmt,,submodule ``ext/fmt``,
 --,tinyopt,,source copy ``ext/tinyopt``,
 ARB_WITH_PYTHON,pybind11,,submodule ``python/pybind11``,
-ARB_WITH_PYTHON,Python,3.6,Python compatiblity is the range between the latest officially released point version and the minimum here specified.,"* it is not more advanced than the version specified by NEP 29.
+ARB_WITH_PYTHON,Python,3.7,Python compatiblity is the range between the latest officially released point version and the minimum here specified.,"* it is not more advanced than the version specified by NEP 29.
 * it is available as a cray-python version on Piz Daint
 * it is available on labs.ebrains.eu
 
diff --git a/doc/install/build_install.rst b/doc/install/build_install.rst
index de6f6edec7b98eb55fbe99d92392478543431fb0..de3d12dee9ccbe43c37dfd748b8bc33fbc8404f9 100644
--- a/doc/install/build_install.rst
+++ b/doc/install/build_install.rst
@@ -135,7 +135,7 @@ More information on building with MPI is in the `HPC cluster section <cluster_>`
 Python
 ~~~~~~
 
-Arbor has a Python frontend, for which a minimum of Python 3.6 is required.
+Arbor has a Python frontend, for which a minimum of Python 3.7 is required.
 In addition, `numpy` is a runtime requirement for the Python package.
 In order to use MPI in combination with the python frontend the
 `mpi4py <https://mpi4py.readthedocs.io/en/stable/install.html#>`_
diff --git a/doc/install/python.rst b/doc/install/python.rst
index c4f7ab46893a3a349f6f80f144243ad015687c0c..78fbf6cc7cbe6720a1a16c7e73d436ae3df7d15f 100644
--- a/doc/install/python.rst
+++ b/doc/install/python.rst
@@ -6,7 +6,7 @@ Python Installation
 Arbor's Python API will be the most convenient interface for most users.
 
 .. note::
-    Arbor requires Python version 3.6 and later. It is advised that you update ``pip`` as well.
+    Arbor requires Python version 3.7 and later. It is advised that you update ``pip`` as well.
     We strongly encourage using ``pip`` to install Arbor.
     
     To get help in case of problems installing with pip, run pip with the ``--verbose`` flag, and attach the output
@@ -42,15 +42,11 @@ You are now ready to use Arbor! You can continue reading these documentation pag
     for any other platforms than listed above, ``pip`` will attempt a build from source and thus require these
     packages as well.
 
-    * Ubuntu/Debian: `git cmake gcc python3-dev python3-pip libxml2-dev`
-    * Fedora/CentOS/OpenSuse: `git cmake gcc-c++ python3-devel python3-pip libxml2-devel`
-    * MacOS: get `brew` `here <https://brew.sh>`_ and run `brew install cmake clang python3 libxml2`
+    * Ubuntu/Debian: ``git cmake gcc python3-dev python3-pip libxml2-dev``
+    * Fedora/CentOS/OpenSuse: ``git cmake gcc-c++ python3-devel python3-pip libxml2-devel``
+    * MacOS: get ``brew`` `here <https://brew.sh>`_ and run ``brew install cmake clang python3 libxml2``
     * Windows: the simplest way is to use `WSL <https://docs.microsoft.com/en-us/windows/wsl/install-win10>`_ and then follow the instructions for Ubuntu.
 
-    In addition, you'll need a few Python packages present:
-
-    ``pip3 install ninja scikit-build wheel setuptools numpy``
-
 .. _in_python_custom:
 
 Customising Arbor
@@ -79,12 +75,11 @@ Every time you make changes to the code, you'll have to repeat the second step.
 Advanced options
 ^^^^^^^^^^^^^^^^
 
-By default Arbor is installed with multi-threading enabled. To enable more
-advanced forms of parallelism and other features, Arbor comes with a few
-compilation options. These are of the form ``-D<KEY>=<VALUE>``, must be appended
-to the ``pip`` invocation via ``--install-option="-D<...>" --install-option="-D<...>" ...`` and can
-be used on both local (``pip3 install ./arbor``) and remote (``pip3 install
-arbor``) copies of Arbor. See the examples below.
+Arbor comes with a few compilation options, some of them related to advanced forms of parallelism and other features.
+The options and flags are the same :ref:`as documented for the CMAKE build <quickstart>`, but they are passed differently.
+To enable more, they must be placed in the ``CMAKE_ARGS`` environment variable.
+The simplest way to do this is by prepending the ``pip`` command with ``CMAKE_ARGS=""``,
+where you place the arguments separated by space inside the quotes.
 
 .. Note::
 
@@ -92,9 +87,6 @@ arbor``) copies of Arbor. See the examples below.
    to remove the ``_skbuild`` directory. If you had Arbor installed already,
    you may need to remove it first before you can (re)compile it with the flags you need.
 
-   Also, make sure to pass each option individually via
-   ``--install-option="..."``.
-
 The following flags can be used to configure the installation:
 
 * ``ARB_WITH_NEUROML=<ON|OFF>``: Enable support for NeuroML2 morphologies,
@@ -119,6 +111,14 @@ The following flags can be used to configure the installation:
    and ``CMake`` under the hood, so all flags and options valid in ``CMake`` can
    be used in this fashion.
 
+   Allthough the
+   `scikit-build documentation <https://scikit-build.readthedocs.io/en/latest/usage.html#environment-variable-configuration>`_
+   mentions that you can also pass the build options with ``--install-option=""``,
+   this will cause ``pip`` to build all dependencies, including all build-dependencies,
+   instead of downloading them from PyPI.
+   ``CMAKE_ARGS=""`` saves you the build time, and also downloading and setting up the dependencies they in turn require to be present.
+   Setting ``CMAKE_ARGS=""`` is in addition compatible with build front-ends like `build <https://pypa-build.readthedocs.io>`_.
+
    Detailed instructions on how to install using CMake are in the :ref:`Python
    configuration <install-python>` section of the :ref:`installation guide
    <in_build_install>`. CMake is recommended if you need more control over
@@ -138,33 +138,33 @@ In the examples below we assume you are installing from a local copy.
 
 .. code-block:: bash
 
-    pip3 install ./arbor --install-option="-DARB_WITH_MPI=ON"
+    CMAKE_ARGS="-DARB_WITH_MPI=ON" pip3 install ./arbor
 
 **Compile with** :ref:`vectorization <install-vectorize>` on a system with a SkyLake
 :ref:`architecture <install-architecture>`:
 
 .. code-block:: bash
 
-    pip3 install ./arbor --install-option="-DARB_VECTORIZE=ON" --install-option="-DARB_ARCH=skylake"
-
+    CMAKE_ARGS="-DARB_VECTORIZE=ON -DARB_ARCH=skylake" pip3 install ./arbor
+    
 **Enable NVIDIA GPUs (compiled with nvcc)**. This requires the :ref:`CUDA toolkit <install-gpu>`:
 
 .. code-block:: bash
 
-    pip3 install ./arbor --install-option="-DARB_GPU=cuda"
+    CMAKE_ARGS="-DARB_GPU=cuda" pip3 install ./arbor
 
 **Enable NVIDIA GPUs (compiled with clang)**. This also requires the :ref:`CUDA toolkit <install-gpu>`:
 
 .. code-block:: bash
 
-    pip3 install ./arbor --install-option="-DARB_GPU=cuda-clang"
+    CMAKE_ARGS="-DARB_GPU=cuda-clang" pip3 install ./arbor
 
 **Enable AMD GPUs (compiled with hipcc)**. This requires setting the ``CC`` and ``CXX``
-:ref:`environment variables <install-gpu>`
+:ref:`environment variables <install-gpu>`:
 
 .. code-block:: bash
 
-    pip3 install ./arbor --install-option="-DARB_GPU=hip"
+    CC=clang CXX=hipcc CMAKE_ARGS="-DARB_GPU=hip" pip3 install ./arbor
 
 Note on performance
 -------------------
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..745f74710d7b01a838aece2d6350d092704554c5
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,72 @@
+[project]
+name = "arbor"
+dynamic = ["version", "readme"]
+license = {file = "LICENSE"}
+description = "High performance simulation of networks of multicompartment neurons."
+requires-python = ">=3.7"
+keywords = ["simulator", "neuroscience", "morphological detail", "HPC", "GPU", "C++"]
+authors = [
+    {name = "Arbor Dev Team", email = "contact@arbor-sim.org"}
+]
+maintainers = [
+    {name = "Arbor Dev Team", email = "contact@arbor-sim.org"}
+]
+classifiers = [
+    "Development Status :: 5 - Production/Stable",
+    "Intended Audience :: Science/Research",
+    "License :: OSI Approved :: BSD License",
+    "Programming Language :: Python",
+    "Programming Language :: Python :: 3.7",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: C++"
+]
+dependencies = [
+    "numpy"
+]
+
+[tool.setuptools]
+py-modules = ["arbor"]
+
+[tool.setuptools.dynamic]
+version = {file = ["VERSION"]}
+readme = {file = ["python/readme.md"]}
+
+[project.urls]
+homepage = "https://arbor-sim.org"
+documentation = "https://docs.arbor-sim.org"
+repository = "https://github.com/arbor-sim/arbor"
+changelog = "https://github.com/arbor-sim/arbor/releases"
+
+[build-system]
+requires = [
+    "setuptools",
+    "wheel",
+    "scikit-build",
+    "cmake>=3.18",
+    "ninja",
+]
+build-backend = "setuptools.build_meta"
+
+[tool.cibuildwheel]
+build-frontend = "build"
+build = ["cp3*-manylinux_x86_64","cp3*-macosx_universal2"]#,"cp3*-musllinux_x86_64","cp3*-musllinux_aarch64"]
+skip = "cp36-*"
+test-command = "python3 -m unittest discover -v -s {project}/python"
+
+[tool.cibuildwheel.macos]
+archs = ["x86_64", "universal2", "arm64"]
+
+[tool.cibuildwheel.macos.environment]
+MACOSX_DEPLOYMENT_TARGET = "10.15"
+
+[tool.cibuildwheel.linux]
+archs = ["x86_64"]
+manylinux-x86_64-image = "manylinux2014"
+before-all = "yum -y install libxml2-devel"
+repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel} && python3 /project/scripts/patchwheel.py {dest_dir}"
+
+[[tool.cibuildwheel.overrides]]
+select = "*-musllinux*"
+before-all = "apk add libxml2-dev"
diff --git a/python/example/single_cell_allen.py b/python/example/single_cell_allen.py
index eac24649175f0d8d0f8b7e6f66066a4663357e6a..359b62e245174ff3d6f461d1a645c3dacd7c9691 100644
--- a/python/example/single_cell_allen.py
+++ b/python/example/single_cell_allen.py
@@ -1,6 +1,7 @@
 #!/usr/bin/env python3
 
 from collections import defaultdict
+from dataclasses import dataclass
 import json
 import arbor
 import seaborn
@@ -15,13 +16,12 @@ def load_allen_fit(fit):
         fit = json.load(fd)
 
     # cable parameters convenience class
+    @dataclass
     class parameters:
-        def __init__(self, **kwargs):
-            self.cm = None
-            self.tempK = None
-            self.Vm = None
-            self.rL = None
-            self.__dict__.update(kwargs)
+        cm: float = None
+        tempK: float = None
+        Vm: float = None
+        rL: float = None
 
     param = defaultdict(parameters)
     mechs = defaultdict(dict)
diff --git a/scripts/build-wheels.sh b/scripts/build-wheels.sh
index 0b881c71b09dfe9fa288056b4270da65dc23be32..4b6412da948b8ce2191980cc375f977870b35194 100755
--- a/scripts/build-wheels.sh
+++ b/scripts/build-wheels.sh
@@ -14,14 +14,14 @@
 set -e -u -x
 
 yum -y install libxml2-devel
-/opt/python/cp310-cp310/bin/pip install ninja cmake
 
 rm -rf /src_dir/arbor/_skbuild
+rm -rf /opt/python/cp36-cp36m # Python build toolchain does not work on Py3.6
 
 export CIBUILDWHEEL=1 #Set condition for cmake
 
-for PYBIN in /opt/python/cp*/bin; do
-    "${PYBIN}/python" -m pip install wheel scikit-build auditwheel
+for PYBIN in /opt/python/cp3*/bin; do
+    "${PYBIN}/python" -m pip install pip auditwheel -U
     export PATH="${PYBIN}":$PATH
     "${PYBIN}/python" -m pip wheel --wheel-dir="/src_dir/builtwheel${PYBIN}/" /src_dir/arbor
     "${PYBIN}/python" -m auditwheel repair /src_dir/builtwheel${PYBIN}/arbor*.whl -w /src_dir/wheelhouse
diff --git a/setup.py b/setup.py
index 7511cd70a39c603025b58be2661f849b74003b78..30f9ed589d923cfb63b839faa9aff9ffd2484cc5 100644
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,3 @@
-from pathlib import Path
 from sys import executable as python
 from skbuild import setup
 
@@ -13,23 +12,7 @@ with_nml = True
 use_libs = True
 build_type = "Release"  # this is ok even for debugging, as we always produce info
 
-# Find our dir; *should* be the arbor checkout
-here = Path(__file__).resolve().parent
-# Read version file
-with open(here / "VERSION") as fd:
-    arbor_version = fd.read().strip()
-# Get the contents of the readme
-with open(here / "python" / "readme.md", encoding="utf-8") as fd:
-    long_description = fd.read()
-
 setup(
-    name="arbor",
-    version=arbor_version,
-    python_requires=">=3.6",
-    install_requires=["numpy"],
-    setup_requires=[],
-    zip_safe=False,
-    packages=["arbor"],
     cmake_args=[
         "-DARB_WITH_PYTHON=on",
         f"-DPYTHON_EXECUTABLE={python}",
@@ -41,25 +24,4 @@ setup(
         f"-DARB_USE_BUNDLED_LIBS={use_libs}",
         f"-DCMAKE_BUILD_TYPE={build_type}",
     ],
-    author="The Arbor dev team.",
-    url="https://arbor-sim.org",
-    description="High performance simulation of networks of multicompartment neurons.",
-    long_description=long_description,
-    long_description_content_type="text/markdown",
-    classifiers=[
-        "Development Status :: 5 - Production/Stable",
-        "Intended Audience :: Science/Research",
-        "License :: OSI Approved :: BSD License",
-        "Programming Language :: Python :: 3.6",
-        "Programming Language :: Python :: 3.7",
-        "Programming Language :: Python :: 3.8",
-        "Programming Language :: Python :: 3.9",
-        "Programming Language :: Python :: 3.10",
-        "Programming Language :: C++",
-    ],
-    project_urls={
-        "Source": "https://github.com/arbor-sim/arbor",
-        "Documentation": "https://docs.arbor-sim.org",
-        "Bug Reports": "https://github.com/arbor-sim/arbor/issues",
-    },
 )
diff --git a/spack/package.py b/spack/package.py
index 016265041e30243c1169edbcf6c83588a6b80512..c5028e59f0eff89e7151f1151cc5fdfe22e872ad 100644
--- a/spack/package.py
+++ b/spack/package.py
@@ -69,14 +69,14 @@ class Arbor(CMakePackage, CudaPackage):
 
     # python (bindings)
     extends("python", when="+python")
-    depends_on("python@3.6:", when="+python", type=("build", "run"))
+    depends_on("python@3.7:", when="+python", type=("build", "run"))
     depends_on("py-numpy", when="+python", type=("build", "run"))
     with when("+python"):
-        depends_on("py-pybind11@2.6:", type=("build", "run"))
-        depends_on("py-pybind11@2.8.1:", when="@0.5.3:", type=("build", "run"))
+        depends_on("py-pybind11@2.6:", type=("build"))
+        depends_on("py-pybind11@2.8.1:", when="@0.5.3:", type=("build"))
 
     # sphinx based documentation
-    depends_on("python@3.6:", when="+doc", type="build")
+    depends_on("python@3.7:", when="+doc", type="build")
     depends_on("py-sphinx", when="+doc", type="build")
     depends_on("py-svgwrite", when="+doc", type="build")