diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 90f94bc3..1f47f125 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,9 +1,18 @@
 version: 2
 updates:
-- package-ecosystem: "github-actions"
-  directory: "/"
-  schedule:
-    interval: "monthly"
-    day: "monday"
-    time: "16:00"
-    timezone: "UTC"
+  - package-ecosystem: github-actions
+    directory: /
+    schedule:
+      interval: monthly
+    groups:
+      github-actions:
+        patterns:
+          - '*'
+  - package-ecosystem: pip
+    directory: /requirements/
+    schedule:
+      interval: monthly
+    groups:
+      python-requirements:
+        patterns:
+          - '*'
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6ee28629..fadf5526 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,31 +3,27 @@ ci:
   autoupdate_schedule: monthly
 repos:
   - repo: https://github.com/asottile/pyupgrade
-    rev: v3.10.1
+    rev: v3.15.0
     hooks:
       - id: pyupgrade
         args: ["--py37-plus"]
   - repo: https://github.com/asottile/reorder-python-imports
-    rev: v3.10.0
+    rev: v3.12.0
     hooks:
       - id: reorder-python-imports
         args: ["--application-directories", "src"]
         additional_dependencies: ["setuptools>60.9"]
   - repo: https://github.com/psf/black
-    rev: 23.7.0
+    rev: 24.1.1
     hooks:
       - id: black
   - repo: https://github.com/PyCQA/flake8
-    rev: 6.1.0
+    rev: 7.0.0
     hooks:
       - id: flake8
         additional_dependencies: [flake8-bugbear]
-  - repo: https://github.com/peterdemin/pip-compile-multi
-    rev: v2.6.3
-    hooks:
-      - id: pip-compile-multi-verify
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.4.0
+    rev: v4.5.0
     hooks:
       - id: fix-byte-order-marker
       - id: trailing-whitespace
diff --git a/requirements/build.txt b/requirements/build.txt
index 196545d0..9129982e 100644
--- a/requirements/build.txt
+++ b/requirements/build.txt
@@ -1,13 +1,22 @@
-# SHA1:80754af91bfb6d1073585b046fe0a474ce868509
 #
-# This file is autogenerated by pip-compile-multi
-# To update, run:
+# This file is autogenerated by pip-compile with Python 3.7
+# by the following command:
 #
-#    pip-compile-multi
+#    pip-compile build.in
 #
-build==0.10.0
-    # via -r requirements/build.in
-packaging==23.1
+build==1.0.3
+    # via -r build.in
+importlib-metadata==6.7.0
+    # via build
+packaging==23.2
     # via build
 pyproject-hooks==1.0.0
     # via build
+tomli==2.0.1
+    # via
+    #   build
+    #   pyproject-hooks
+typing-extensions==4.7.1
+    # via importlib-metadata
+zipp==3.15.0
+    # via importlib-metadata
diff --git a/requirements/dev.in b/requirements/dev.in
index 99f5942f..cc6adb72 100644
--- a/requirements/dev.in
+++ b/requirements/dev.in
@@ -1,6 +1,5 @@
 -r docs.in
 -r tests.in
 -r typing.in
-pip-compile-multi
 pre-commit
 tox
diff --git a/requirements/dev.txt b/requirements/dev.txt
index ed462080..e645a4a0 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -1,64 +1,141 @@
-# SHA1:54b5b77ec8c7a0064ffa93b2fd16cb0130ba177c
 #
-# This file is autogenerated by pip-compile-multi
-# To update, run:
+# This file is autogenerated by pip-compile with Python 3.7
+# by the following command:
 #
-#    pip-compile-multi
+#    pip-compile dev.in
 #
--r docs.txt
--r tests.txt
--r typing.txt
-build==0.10.0
-    # via pip-tools
-cachetools==5.3.1
+alabaster==0.7.13
+    # via sphinx
+babel==2.14.0
+    # via sphinx
+cachetools==5.3.2
     # via tox
+certifi==2024.2.2
+    # via requests
 cfgv==3.3.1
     # via pre-commit
-chardet==5.1.0
+chardet==5.2.0
     # via tox
-click==8.1.3
-    # via
-    #   pip-compile-multi
-    #   pip-tools
+charset-normalizer==3.3.2
+    # via requests
 colorama==0.4.6
     # via tox
-distlib==0.3.6
+distlib==0.3.8
     # via virtualenv
+docutils==0.19
+    # via sphinx
+exceptiongroup==1.2.0
+    # via pytest
 filelock==3.12.2
     # via
     #   tox
     #   virtualenv
 identify==2.5.24
     # via pre-commit
+idna==3.6
+    # via requests
+imagesize==1.4.1
+    # via sphinx
+importlib-metadata==6.7.0
+    # via
+    #   pallets-sphinx-themes
+    #   pluggy
+    #   pre-commit
+    #   pytest
+    #   sphinx
+    #   tox
+    #   virtualenv
+iniconfig==2.0.0
+    # via pytest
+jinja2==3.1.3
+    # via sphinx
+markupsafe==2.1.4
+    # via jinja2
+mypy==1.4.1
+    # via -r typing.in
+mypy-extensions==1.0.0
+    # via mypy
 nodeenv==1.8.0
     # via pre-commit
-pip-compile-multi==2.6.3
-    # via -r requirements/dev.in
-pip-tools==6.13.0
-    # via pip-compile-multi
-platformdirs==3.8.0
+packaging==23.2
+    # via
+    #   pallets-sphinx-themes
+    #   pyproject-api
+    #   pytest
+    #   sphinx
+    #   tox
+pallets-sphinx-themes==2.0.3
+    # via -r docs.in
+platformdirs==4.0.0
     # via
     #   tox
     #   virtualenv
-pre-commit==3.3.3
-    # via -r requirements/dev.in
-pyproject-api==1.5.2
+pluggy==1.2.0
+    # via
+    #   pytest
+    #   tox
+pre-commit==2.21.0
+    # via -r dev.in
+pygments==2.17.2
+    # via sphinx
+pyproject-api==1.5.3
     # via tox
-pyproject-hooks==1.0.0
-    # via build
-pyyaml==6.0
+pytest==7.4.4
+    # via -r tests.in
+pytz==2024.1
+    # via babel
+pyyaml==6.0.1
     # via pre-commit
-toposort==1.10
-    # via pip-compile-multi
-tox==4.6.3
-    # via -r requirements/dev.in
-virtualenv==20.23.1
+requests==2.31.0
+    # via sphinx
+snowballstemmer==2.2.0
+    # via sphinx
+sphinx==5.3.0
+    # via
+    #   -r docs.in
+    #   pallets-sphinx-themes
+    #   sphinx-issues
+    #   sphinxcontrib-log-cabinet
+sphinx-issues==3.0.1
+    # via -r docs.in
+sphinxcontrib-applehelp==1.0.2
+    # via sphinx
+sphinxcontrib-devhelp==1.0.2
+    # via sphinx
+sphinxcontrib-htmlhelp==2.0.0
+    # via sphinx
+sphinxcontrib-jsmath==1.0.1
+    # via sphinx
+sphinxcontrib-log-cabinet==1.0.1
+    # via -r docs.in
+sphinxcontrib-qthelp==1.0.3
+    # via sphinx
+sphinxcontrib-serializinghtml==1.1.5
+    # via sphinx
+tomli==2.0.1
+    # via
+    #   mypy
+    #   pyproject-api
+    #   pytest
+    #   tox
+tox==4.8.0
+    # via -r dev.in
+typed-ast==1.5.5
+    # via mypy
+typing-extensions==4.7.1
+    # via
+    #   importlib-metadata
+    #   mypy
+    #   platformdirs
+    #   tox
+urllib3==2.0.7
+    # via requests
+virtualenv==20.25.0
     # via
     #   pre-commit
     #   tox
-wheel==0.40.0
-    # via pip-tools
+zipp==3.15.0
+    # via importlib-metadata
 
 # The following packages are considered to be unsafe in a requirements file:
-# pip
 # setuptools
diff --git a/requirements/docs.in b/requirements/docs.in
index 7ec501b6..88fd7721 100644
--- a/requirements/docs.in
+++ b/requirements/docs.in
@@ -1,4 +1,4 @@
-Pallets-Sphinx-Themes
-Sphinx
+pallets-sphinx-themes
+sphinx
 sphinx-issues
 sphinxcontrib-log-cabinet
diff --git a/requirements/docs.txt b/requirements/docs.txt
index e125c59a..e48ec9a9 100644
--- a/requirements/docs.txt
+++ b/requirements/docs.txt
@@ -1,61 +1,70 @@
-# SHA1:45c590f97fe95b8bdc755eef796e91adf5fbe4ea
 #
-# This file is autogenerated by pip-compile-multi
-# To update, run:
+# This file is autogenerated by pip-compile with Python 3.7
+# by the following command:
 #
-#    pip-compile-multi
+#    pip-compile docs.in
 #
 alabaster==0.7.13
     # via sphinx
-babel==2.12.1
+babel==2.14.0
     # via sphinx
-certifi==2023.5.7
+certifi==2024.2.2
     # via requests
-charset-normalizer==3.1.0
+charset-normalizer==3.3.2
     # via requests
-docutils==0.20.1
+docutils==0.19
     # via sphinx
-idna==3.4
+idna==3.6
     # via requests
 imagesize==1.4.1
     # via sphinx
-jinja2==3.1.2
+importlib-metadata==6.7.0
+    # via
+    #   pallets-sphinx-themes
+    #   sphinx
+jinja2==3.1.3
     # via sphinx
-markupsafe==2.1.3
+markupsafe==2.1.4
     # via jinja2
-packaging==23.1
+packaging==23.2
     # via
     #   pallets-sphinx-themes
     #   sphinx
-pallets-sphinx-themes==2.1.1
-    # via -r requirements/docs.in
-pygments==2.15.1
+pallets-sphinx-themes==2.0.3
+    # via -r docs.in
+pygments==2.17.2
     # via sphinx
+pytz==2024.1
+    # via babel
 requests==2.31.0
     # via sphinx
 snowballstemmer==2.2.0
     # via sphinx
-sphinx==7.0.1
+sphinx==5.3.0
     # via
-    #   -r requirements/docs.in
+    #   -r docs.in
     #   pallets-sphinx-themes
     #   sphinx-issues
     #   sphinxcontrib-log-cabinet
 sphinx-issues==3.0.1
-    # via -r requirements/docs.in
-sphinxcontrib-applehelp==1.0.4
+    # via -r docs.in
+sphinxcontrib-applehelp==1.0.2
     # via sphinx
 sphinxcontrib-devhelp==1.0.2
     # via sphinx
-sphinxcontrib-htmlhelp==2.0.1
+sphinxcontrib-htmlhelp==2.0.0
     # via sphinx
 sphinxcontrib-jsmath==1.0.1
     # via sphinx
 sphinxcontrib-log-cabinet==1.0.1
-    # via -r requirements/docs.in
+    # via -r docs.in
 sphinxcontrib-qthelp==1.0.3
     # via sphinx
 sphinxcontrib-serializinghtml==1.1.5
     # via sphinx
-urllib3==2.0.3
+typing-extensions==4.7.1
+    # via importlib-metadata
+urllib3==2.0.7
     # via requests
+zipp==3.15.0
+    # via importlib-metadata
diff --git a/requirements/tests.txt b/requirements/tests.txt
index 6a680886..94ea08e5 100644
--- a/requirements/tests.txt
+++ b/requirements/tests.txt
@@ -1,15 +1,26 @@
-# SHA1:0eaa389e1fdb3a1917c0f987514bd561be5718ee
 #
-# This file is autogenerated by pip-compile-multi
-# To update, run:
+# This file is autogenerated by pip-compile with Python 3.7
+# by the following command:
 #
-#    pip-compile-multi
+#    pip-compile tests.in
 #
+exceptiongroup==1.2.0
+    # via pytest
+importlib-metadata==6.7.0
+    # via
+    #   pluggy
+    #   pytest
 iniconfig==2.0.0
     # via pytest
-packaging==23.1
+packaging==23.2
     # via pytest
 pluggy==1.2.0
     # via pytest
-pytest==7.4.0
-    # via -r requirements/tests.in
+pytest==7.4.4
+    # via -r tests.in
+tomli==2.0.1
+    # via pytest
+typing-extensions==4.7.1
+    # via importlib-metadata
+zipp==3.15.0
+    # via importlib-metadata
diff --git a/requirements/typing.txt b/requirements/typing.txt
index 13afd660..858a9485 100644
--- a/requirements/typing.txt
+++ b/requirements/typing.txt
@@ -1,13 +1,16 @@
-# SHA1:7983aaa01d64547827c20395d77e248c41b2572f
 #
-# This file is autogenerated by pip-compile-multi
-# To update, run:
+# This file is autogenerated by pip-compile with Python 3.7
+# by the following command:
 #
-#    pip-compile-multi
+#    pip-compile typing.in
 #
 mypy==1.4.1
-    # via -r requirements/typing.in
+    # via -r typing.in
 mypy-extensions==1.0.0
     # via mypy
-typing-extensions==4.6.3
+tomli==2.0.1
+    # via mypy
+typed-ast==1.5.5
+    # via mypy
+typing-extensions==4.7.1
     # via mypy
diff --git a/tox.ini b/tox.ini
index 79a91c73..e53ce076 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
 [tox]
 envlist =
-    py3{12,11,10,9,8}
+    py3{12,11,10,9,8,7}
     pypy310
     style
     typing
@@ -24,3 +24,17 @@ commands = mypy
 [testenv:docs]
 deps = -r requirements/docs.txt
 commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html
+
+[testenv:update-requirements]
+deps =
+    pip-tools
+    pre-commit
+skip_install = true
+change_dir = requirements
+commands =
+    pre-commit autoupdate -j4
+    pip-compile -U build.in
+    pip-compile -U docs.in
+    pip-compile -U tests.in
+    pip-compile -U typing.in
+    pip-compile -U dev.in