Skip to content

Commit 58d0e7d

Browse files
authored
Replace black with ruff linter-formatter (#173)
* turn on flake8 tests * fix pep8 formatting * test ruff linter * include flake8 for hacking plugin * update httpcore * fix merge conflict mistake
1 parent 3556ddf commit 58d0e7d

File tree

6 files changed

+82
-92
lines changed

6 files changed

+82
-92
lines changed

capi_janitor/openstack/openstack.py

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,28 @@
99

1010

1111
class UnsupportedAuthenticationError(Exception):
12-
"""
13-
Raised when an unsupported authentication method is used.
14-
"""
12+
"""Raised when an unsupported authentication method is used."""
1513

1614
def __init__(self, auth_type):
1715
super().__init__(f"unsupported authentication type: {auth_type}")
1816

1917

2018
class AuthenticationError(Exception):
21-
"""
22-
Raised when an unknown authentication error is encountered.
23-
"""
19+
"""Raised when an unknown authentication error is encountered."""
2420

2521
def __init__(self, user):
2622
super().__init__(f"failed to authenticate as user: {user}")
2723

2824

2925
class CatalogError(Exception):
30-
"""
31-
Raised when an unknown catalog service type is requested.
32-
"""
26+
"""Raised when an unknown catalog service type is requested."""
3327

3428
def __init__(self, name):
3529
super().__init__(f"service type {name} not found in OpenStack service catalog")
3630

3731

3832
class Auth(httpx.Auth):
39-
"""
40-
Authenticator class for OpenStack connections.
41-
"""
33+
"""Authenticator class for OpenStack connections."""
4234

4335
def __init__(
4436
self, auth_url, application_credential_id, application_credential_secret
@@ -52,8 +44,9 @@ def __init__(
5244

5345
@contextlib.asynccontextmanager
5446
async def _refresh_token(self):
55-
"""
56-
Context manager to ensure only one request at a time triggers a token refresh.
47+
"""Context manager to ensure only one request at a time
48+
49+
triggers a token refresh.
5750
"""
5851
token = self._token
5952
async with self._lock:
@@ -95,9 +88,7 @@ async def async_auth_flow(self, request):
9588

9689

9790
class Resource(rest.Resource):
98-
"""
99-
Base resource for OpenStack APIs.
100-
"""
91+
"""Base resource for OpenStack APIs."""
10192

10293
def __init__(self, client, name, prefix=None, plural_name=None, singular_name=None):
10394
super().__init__(client, name, prefix)
@@ -140,9 +131,7 @@ def _extract_one(self, response):
140131

141132

142133
class Client(rest.AsyncClient):
143-
"""
144-
Client for OpenStack APIs.
145-
"""
134+
"""Client for OpenStack APIs."""
146135

147136
def __init__(self, /, base_url, prefix=None, **kwargs):
148137
# Extract the path part of the base_url
@@ -173,9 +162,7 @@ def resource(self, name, prefix=None, plural_name=None, singular_name=None):
173162

174163

175164
class Cloud:
176-
"""
177-
Object for interacting with OpenStack clouds.
178-
"""
165+
"""Object for interacting with OpenStack clouds."""
179166

180167
def __init__(self, auth, transport, interface, region=None):
181168
self._auth = auth
@@ -219,29 +206,21 @@ async def __aexit__(self, exc_type, exc_value, traceback):
219206

220207
@property
221208
def is_authenticated(self):
222-
"""
223-
Returns True if the cloud is authenticated, False otherwise.
224-
"""
209+
"""Returns True if the cloud is authenticated, False otherwise."""
225210
return bool(self._endpoints)
226211

227212
@property
228213
def current_user_id(self):
229-
"""
230-
The ID of the current user.
231-
"""
214+
"""The ID of the current user."""
232215
return self._auth._user_id
233216

234217
@property
235218
def apis(self):
236-
"""
237-
The APIs supported by the cloud.
238-
"""
219+
"""The APIs supported by the cloud."""
239220
return list(self._endpoints.keys())
240221

241222
def api_client(self, name, prefix=None):
242-
"""
243-
Returns a client for the named API.
244-
"""
223+
"""Returns a client for the named API."""
245224
if name not in self._clients:
246225
self._clients[name] = Client(
247226
base_url=self._endpoints[name],

capi_janitor/openstack/operator.py

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,18 @@ async def on_cleanup(**kwargs):
5555

5656

5757
class FinalizerStillPresentError(Exception):
58-
"""
59-
Raised when a finalizer from another controller is preventing us from deleting an appcred.
58+
"""Raised when a finalizer from another controller is preventing us from
59+
60+
deleting an appcred.
6061
"""
6162

6263
def __init__(self, finalizer, cluster):
6364
super().__init__(f"finalizer '{finalizer}' still present for cluster {cluster}")
6465

6566

6667
class ResourcesStillPresentError(Exception):
67-
"""
68-
Raised when cluster resources are still present even after being deleted,
68+
"""Raised when cluster resources are still present even after being deleted,
69+
6970
e.g. while waiting for deletion.
7071
"""
7172

@@ -74,9 +75,7 @@ def __init__(self, resource, cluster):
7475

7576

7677
async def fips_for_cluster(resource, cluster):
77-
"""
78-
Async iterator for FIPs belonging to the specified cluster.
79-
"""
78+
"""Async iterator for FIPs belonging to the specified cluster."""
8079
async for fip in resource.list():
8180
if not fip.description.startswith(
8281
"Floating IP for Kubernetes external service"
@@ -88,18 +87,14 @@ async def fips_for_cluster(resource, cluster):
8887

8988

9089
async def lbs_for_cluster(resource, cluster):
91-
"""
92-
Async iterator for loadbalancers belonging to the specified cluster.
93-
"""
90+
"""Async iterator for loadbalancers belonging to the specified cluster."""
9491
async for lb in resource.list():
9592
if lb.name.startswith(f"kube_service_{cluster}_"):
9693
yield lb
9794

9895

9996
async def secgroups_for_cluster(resource, cluster):
100-
"""
101-
Async iterator for security groups belonging to the specified cluster.
102-
"""
97+
"""Async iterator for security groups belonging to the specified cluster."""
10398
async for sg in resource.list():
10499
if not sg.description.startswith("Security Group for"):
105100
continue
@@ -109,9 +104,8 @@ async def secgroups_for_cluster(resource, cluster):
109104

110105

111106
async def filtered_volumes_for_cluster(resource, cluster):
112-
"""
113-
Async iterator for volumes belonging to the specified cluster.
114-
"""
107+
"""Async iterator for volumes belonging to the specified cluster."""
108+
115109
async for vol in resource.list():
116110
# CSI Cinder sets metadata on the volumes that we can look for
117111
owner = vol.metadata.get("cinder.csi.openstack.org/cluster")
@@ -125,9 +119,7 @@ async def filtered_volumes_for_cluster(resource, cluster):
125119

126120

127121
async def snapshots_for_cluster(resource, cluster):
128-
"""
129-
Async iterator for snapshots belonging to the specified cluster.
130-
"""
122+
"""Async iterator for snapshots belonging to the specified cluster."""
131123
async for snapshot in resource.list():
132124
# CSI Cinder sets metadata on the volumes that we can look for
133125
owner = snapshot.metadata.get("cinder.csi.openstack.org/cluster")
@@ -136,9 +128,7 @@ async def snapshots_for_cluster(resource, cluster):
136128

137129

138130
async def empty(async_iterator):
139-
"""
140-
Returns True if the given async iterator is empty, False otherwise.
141-
"""
131+
"""Returns True if the given async iterator is empty, False otherwise."""
142132
try:
143133
_ = await async_iterator.__anext__()
144134
except StopAsyncIteration:
@@ -148,8 +138,7 @@ async def empty(async_iterator):
148138

149139

150140
async def try_delete(logger, resource, instances, **kwargs):
151-
"""
152-
Tries to delete the specified instances, catching 400 and 409 exceptions for retry.
141+
"""Tries to delete the specified instances, catches 400 & 409 exceptions for retry.
153142
154143
It returns a boolean indicating whether a check is required for the resource.
155144
"""
@@ -161,7 +150,7 @@ async def try_delete(logger, resource, instances, **kwargs):
161150
except httpx.HTTPStatusError as exc:
162151
if exc.response.status_code in {400, 409}:
163152
logger.warn(
164-
f"got status code {exc.response.status_code} when attempting to delete "
153+
f"got status code {exc.response.status_code} when trying to delete "
165154
f"{resource.singular_name} with ID {instance.id} - will retry"
166155
)
167156
else:
@@ -172,9 +161,8 @@ async def try_delete(logger, resource, instances, **kwargs):
172161
async def purge_openstack_resources(
173162
logger, clouds, cloud_name, cacert, name, include_volumes, include_appcred
174163
):
175-
"""
176-
Cleans up the OpenStack resources created by the OCCM and CSI for a cluster.
177-
"""
164+
"""Cleans up the OpenStack resources created by the OCCM and CSI for a cluster."""
165+
178166
# Use the credential to delete external resources as required
179167
async with openstack.Cloud.from_clouds(clouds, cloud_name, cacert) as cloud:
180168
if not cloud.is_authenticated:
@@ -202,7 +190,7 @@ async def purge_openstack_resources(
202190
)
203191
logger.info("deleted load balancers for LoadBalancer services")
204192

205-
# Delete any security groups associated with loadbalancer services for the cluster
193+
# Delete security groups associated with loadbalancer services for the cluster
206194
secgroups = networkapi.resource("security-groups")
207195
check_secgroups = await try_delete(
208196
logger, secgroups, secgroups_for_cluster(secgroups, name)
@@ -216,8 +204,8 @@ async def purge_openstack_resources(
216204
# a historically valid alias, see:
217205
# - https://docs.openstack.org/keystone/latest/contributor/service-catalog.html
218206
# - https://service-types.openstack.org/service-types.json
219-
# TODO: Make use of https://opendev.org/openstack/os-service-types to improve
220-
# service type alias handling?
207+
# TODO(sd109): Make use of https://opendev.org/openstack/os-service-types to
208+
# improve service type alias handling?
221209
try:
222210
volumeapi = cloud.api_client("volumev3")
223211
except KeyError:
@@ -282,9 +270,9 @@ async def purge_openstack_resources(
282270

283271

284272
async def patch_finalizers(resource, name, namespace, finalizers):
285-
"""
286-
Patches the finalizers of a resource. If the resource does not exist any
287-
more, that is classed as a success.
273+
"""Patches the finalizers of a resource.
274+
275+
If the resource does not exist anymore, that is classed as a success.
288276
"""
289277
try:
290278
await resource.patch(
@@ -300,8 +288,7 @@ async def patch_finalizers(resource, name, namespace, finalizers):
300288

301289

302290
def retry_event(handler):
303-
"""
304-
Decorator for retrying events on Kubernetes objects.
291+
"""Decorator for retrying events on Kubernetes objects.
305292
306293
Instead of retrying within the handler, potentially on stale data, the object is
307294
annotated with the number of times it has been retried. This triggers a new event
@@ -370,14 +357,13 @@ async def on_openstackcluster_event(
370357
async def _on_openstackcluster_event_impl(
371358
name, namespace, meta, labels, spec, logger, **kwargs
372359
):
373-
"""
374-
Executes whenever an event occurs for an OpenStack cluster.
375-
"""
360+
"""Executes whenever an event occurs for an OpenStack cluster."""
361+
376362
# Get the resource for manipulating OpenStackClusters at the preferred version
377363
openstackclusters = await _get_os_cluster_client()
378364

379-
# Use the value of the `cluster.x-k8s.io/cluster-name` label as the cluster name if it exists,
380-
# otherwise, fall back to the OpenStackCluster resource name.
365+
# Use the value of the `cluster.x-k8s.io/cluster-name` label as the cluster name
366+
# if it exists, otherwise, fall back to the OpenStackCluster resource name.
381367
clustername = labels.get("cluster.x-k8s.io/cluster-name", name)
382368
logger.debug(f"cluster name that will be used for cleanup: '{clustername}'")
383369

@@ -451,7 +437,8 @@ async def _on_openstackcluster_event_impl(
451437
await _delete_secret(clouds_secret.metadata["name"], namespace)
452438
logger.info("cloud credential secret deleted")
453439
elif remove_appcred:
454-
# If the annotation says delete but other controllers are still acting, go round again
440+
# If the annotation says delete but other controllers are still acting,
441+
# go round again
455442
raise FinalizerStillPresentError(
456443
next(f for f in finalizers if f != FINALIZER), name
457444
)

capi_janitor/tests/openstack/test_operator.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import base64
2-
import yaml
32
import unittest
43
from unittest import mock
4+
import yaml
55

66
import easykube
77
from easykube.rest.util import PropertyDict
@@ -11,7 +11,6 @@
1111

1212

1313
class TestOperator(unittest.IsolatedAsyncioTestCase):
14-
1514
async def test_operator(self):
1615
mock_easykube = mock.AsyncMock(spec=easykube.AsyncClient)
1716
operator.ekclient = mock_easykube

pyproject.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[tool.ruff]
2+
# Match Black's style
3+
line-length = 88
4+
target-version = "py310" # adjust to your minimum supported Python version
5+
6+
# Match Flake8/hacking exclude patterns
7+
exclude = [
8+
".venv",
9+
".git",
10+
".tox",
11+
"dist",
12+
"doc",
13+
"build",
14+
"*egg",
15+
"*lib/python*"
16+
]
17+
18+
# For OpenStack/_-based i18n
19+
builtins = ["_"]
20+
21+
# Optional: format with Ruff (in place of Black)
22+
[tool.ruff.format]
23+
quote-style = "double"
24+
indent-style = "space"
25+
line-ending = "auto"

test-requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
hacking>=6.1.0,<7.1 # Apache-2.0
22

3-
black~=24.0
3+
ruff~=0.11.7
44
coverage>=4.0,!=4.4 # Apache-2.0
55
stestr>=4.2.0 # Apache-2.0
66
codespell

0 commit comments

Comments
 (0)