diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 9afa612f2c..a65c70b42d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -4,7 +4,7 @@ default_language_version:
   python: python3
 repos:
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.5.0
+    rev: v4.6.0
     hooks:
       - id: trailing-whitespace
       - id: mixed-line-ending
@@ -18,12 +18,12 @@ repos:
         files: .*\.(yaml|yml)$
         args: ['--unsafe']
   - repo: https://github.com/psf/black
-    rev: 23.12.1
+    rev: 24.4.0
     hooks:
       - id: black
         args: ['-S', '-l', '79']
   - repo: https://github.com/PyCQA/bandit
-    rev: 1.7.6
+    rev: 1.7.8
     hooks:
       - id: bandit
         args: ['-x', 'tests', '-s', 'B105,B106,B107,B401,B404,B603,B606,B607,B110,B605,B101']
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 11ca9a36d6..ce0a75550d 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -1991,9 +1991,9 @@ class CreateServer(command.ShowOne):
                 )
                 raise exceptions.CommandError(msg)
 
-            boot_kwargs[
-                'hypervisor_hostname'
-            ] = parsed_args.hypervisor_hostname
+            boot_kwargs['hypervisor_hostname'] = (
+                parsed_args.hypervisor_hostname
+            )
 
         if parsed_args.hostname:
             if compute_client.api_version < api_versions.APIVersion("2.90"):
diff --git a/openstackclient/image/v2/cache.py b/openstackclient/image/v2/cache.py
index bf27b5e63d..c69b7b592f 100644
--- a/openstackclient/image/v2/cache.py
+++ b/openstackclient/image/v2/cache.py
@@ -36,16 +36,16 @@ def _format_image_cache(cached_images):
             for image in cached_images[item]:
                 image_obj = copy.deepcopy(image)
                 image_obj['state'] = 'cached'
-                image_obj[
-                    'last_accessed'
-                ] = datetime.datetime.utcfromtimestamp(
-                    image['last_accessed']
-                ).isoformat()
-                image_obj[
-                    'last_modified'
-                ] = datetime.datetime.utcfromtimestamp(
-                    image['last_modified']
-                ).isoformat()
+                image_obj['last_accessed'] = (
+                    datetime.datetime.utcfromtimestamp(
+                        image['last_accessed']
+                    ).isoformat()
+                )
+                image_obj['last_modified'] = (
+                    datetime.datetime.utcfromtimestamp(
+                        image['last_modified']
+                    ).isoformat()
+                )
                 image_list.append(image_obj)
         elif item == "queued_images":
             for image in cached_images[item]:
diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py
index 96a03cf6c1..59957617b3 100644
--- a/openstackclient/network/v2/network_segment_range.py
+++ b/openstackclient/network/v2/network_segment_range.py
@@ -43,8 +43,10 @@ def _get_ranges(item):
     item = sorted([int(i) for i in item])
     for a, b in itertools.groupby(enumerate(item), lambda xy: xy[1] - xy[0]):
         b = list(b)
-        yield "%s-%s" % (b[0][1], b[-1][1]) if b[0][1] != b[-1][1] else str(
-            b[0][1]
+        yield (
+            "%s-%s" % (b[0][1], b[-1][1])
+            if b[0][1] != b[-1][1]
+            else str(b[0][1])
         )
 
 
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index 51e2aba42f..e72d3e2f5e 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -106,9 +106,9 @@ def _get_external_gateway_attrs(client_manager, parsed_args):
     attrs = {}
 
     if parsed_args.external_gateways:
-        external_gateways: collections.defaultdict[
-            str, list[dict]
-        ] = collections.defaultdict(list)
+        external_gateways: collections.defaultdict[str, list[dict]] = (
+            collections.defaultdict(list)
+        )
         n_client = client_manager.network
         first_network_id = None
 
diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py
index 5806bf4344..f8a816b6ac 100644
--- a/openstackclient/tests/unit/compute/v2/fakes.py
+++ b/openstackclient/tests/unit/compute/v2/fakes.py
@@ -181,8 +181,7 @@ class TestComputev2(
     identity_fakes.FakeClientMixin,
     FakeClientMixin,
     utils.TestCommand,
-):
-    ...
+): ...
 
 
 def create_one_aggregate(attrs=None):
diff --git a/openstackclient/tests/unit/identity/v2_0/fakes.py b/openstackclient/tests/unit/identity/v2_0/fakes.py
index 70017c58a0..b9364e7f00 100644
--- a/openstackclient/tests/unit/identity/v2_0/fakes.py
+++ b/openstackclient/tests/unit/identity/v2_0/fakes.py
@@ -207,8 +207,7 @@ class FakeClientMixin:
 class TestIdentityv2(
     FakeClientMixin,
     utils.TestCommand,
-):
-    ...
+): ...
 
 
 class FakeExtension(object):
diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py
index 27d98b0679..b567624979 100644
--- a/openstackclient/tests/unit/identity/v3/fakes.py
+++ b/openstackclient/tests/unit/identity/v3/fakes.py
@@ -690,8 +690,7 @@ class FakeClientMixin:
 class TestIdentityv3(
     FakeClientMixin,
     utils.TestCommand,
-):
-    ...
+): ...
 
 
 # We don't use FakeClientMixin since we want a different fake legacy client
diff --git a/openstackclient/tests/unit/identity/v3/test_registered_limit.py b/openstackclient/tests/unit/identity/v3/test_registered_limit.py
index ad1a69c819..2f51b97726 100644
--- a/openstackclient/tests/unit/identity/v3/test_registered_limit.py
+++ b/openstackclient/tests/unit/identity/v3/test_registered_limit.py
@@ -246,9 +246,9 @@ class TestRegisteredLimitSet(TestRegisteredLimit):
 
     def test_registered_limit_set_description(self):
         registered_limit = copy.deepcopy(identity_fakes.REGISTERED_LIMIT)
-        registered_limit[
-            'description'
-        ] = identity_fakes.registered_limit_description
+        registered_limit['description'] = (
+            identity_fakes.registered_limit_description
+        )
         self.registered_limit_mock.update.return_value = fakes.FakeResource(
             None, registered_limit, loaded=True
         )
diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py
index 52d3ec0421..27f4777bc8 100644
--- a/openstackclient/tests/unit/image/v2/fakes.py
+++ b/openstackclient/tests/unit/image/v2/fakes.py
@@ -43,8 +43,7 @@ class TestImagev2(
     identity_fakes.FakeClientMixin,
     FakeClientMixin,
     utils.TestCommand,
-):
-    ...
+): ...
 
 
 def create_one_image(attrs=None):
diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py
index f71986270c..e8aafa8cf5 100644
--- a/openstackclient/tests/unit/image/v2/test_image.py
+++ b/openstackclient/tests/unit/image/v2/test_image.py
@@ -894,7 +894,7 @@ class TestImageList(TestImage):
         columns, data = self.cmd.take_action(parsed_args)
         self.image_client.images.assert_called_with(
             limit=ret_limit,
-            paginated=False
+            paginated=False,
             # marker=None
         )
 
diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py
index a357168517..538878e779 100644
--- a/openstackclient/tests/unit/network/v2/fakes.py
+++ b/openstackclient/tests/unit/network/v2/fakes.py
@@ -104,8 +104,7 @@ class TestNetworkV2(
     identity_fakes.FakeClientMixin,
     FakeClientMixin,
     utils.TestCommand,
-):
-    ...
+): ...
 
 
 def create_one_extension(attrs=None):
diff --git a/tox.ini b/tox.ini
index ece6be2fa2..9d3b9def91 100644
--- a/tox.ini
+++ b/tox.ini
@@ -124,8 +124,9 @@ show-source = true
 exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,releasenotes
 # E203 Black will put spaces after colons in list comprehensions
 # E501 Black takes care of line length for us
+# E704 Black will occasionally put multiple statements on one line
 # H301 Black will put commas after imports that can't fit on one line
 # W503 and W504 are disabled since they're not very useful
-ignore = E203,E501,H301,W503,W504
+ignore = E203, E501, E701, H301, W503, W504
 import-order-style = pep8
 application_import_names = openstackclient