Skip to content

Commit e2fb1c9

Browse files
committed
Enable upgrade, security rules
Signed-off-by: Stephen Finucane <[email protected]>
1 parent 189e2d3 commit e2fb1c9

File tree

8 files changed

+57
-60
lines changed

8 files changed

+57
-60
lines changed

pwclient/api.py

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def __init__(self, server, *, username=None, password=None, token=None):
159159
transport=transport,
160160
allow_none=True,
161161
)
162-
except (IOError, OSError):
162+
except OSError:
163163
raise exceptions.APIError(f'Unable to connect to {self._server}')
164164

165165
self._client = rpc
@@ -269,8 +269,8 @@ def patch_list(
269269
state_id = self._state_id_by_name(state)
270270
if state_id == 0:
271271
sys.stderr.write(
272-
"Note: No State found matching %s*, "
273-
"ignoring filter\n" % state
272+
f"Note: No State found matching {state}*, "
273+
f"ignoring filter\n"
274274
)
275275
else:
276276
filters['state_id'] = state_id
@@ -279,8 +279,8 @@ def patch_list(
279279
project_id = self._project_id_by_name(project)
280280
if project_id == 0:
281281
sys.stderr.write(
282-
"Note: No Project found matching %s, "
283-
"ignoring filter\n" % project
282+
f"Note: No Project found matching {project}, "
283+
f"ignoring filter\n"
284284
)
285285
else:
286286
filters['project_id'] = project_id
@@ -296,7 +296,7 @@ def patch_list(
296296
person_ids = self._person_ids_by_name(submitter)
297297
if len(person_ids) == 0:
298298
sys.stderr.write(
299-
"Note: Nobody found matching *%s*\n" % submitter
299+
f"Note: Nobody found matching *{submitter}*\n"
300300
)
301301
else:
302302
for person_id in person_ids:
@@ -308,9 +308,7 @@ def patch_list(
308308
patches = []
309309
delegate_ids = self._person_ids_by_name(delegate)
310310
if len(delegate_ids) == 0:
311-
sys.stderr.write(
312-
"Note: Nobody found matching *%s*\n" % delegate
313-
)
311+
sys.stderr.write(f"Note: Nobody found matching *{delegate}*\n")
314312
else:
315313
for delegate_id in delegate_ids:
316314
filters['delegate_id'] = delegate_id
@@ -324,7 +322,7 @@ def patch_get(self, patch_id):
324322
patch = self._client.patch_get(patch_id)
325323
if patch == {}:
326324
raise exceptions.APIError(
327-
'Unable to fetch patch %d; does it exist?' % patch_id
325+
f'Unable to fetch patch {patch_id}; does it exist?'
328326
)
329327

330328
return self._decode_patch(patch)
@@ -341,7 +339,7 @@ def patch_get_mbox(self, patch_id):
341339
mbox = self._client.patch_get_mbox(patch_id)
342340
if len(mbox) == 0:
343341
raise exceptions.APIError(
344-
'Unable to fetch mbox for patch %d; does it exist?' % patch_id
342+
f'Unable to fetch mbox for patch {patch_id}; does it exist?'
345343
)
346344

347345
return mbox, patch['filename']
@@ -361,9 +359,7 @@ def patch_set(
361359
if state:
362360
state_id = self._state_id_by_name(state)
363361
if state_id == 0:
364-
raise exceptions.APIError(
365-
'No State found matching %s*' % state
366-
)
362+
raise exceptions.APIError(f'No State found matching {state}*')
367363
sys.exit(1)
368364

369365
params['state'] = state_id
@@ -377,9 +373,7 @@ def patch_set(
377373
try:
378374
self._client.patch_set(patch_id, params)
379375
except xmlrpclib.Fault as f:
380-
raise exceptions.APIError(
381-
'Error updating patch: %s' % f.faultString
382-
)
376+
raise exceptions.APIError(f'Error updating patch: {f.faultString}')
383377

384378
# states
385379

@@ -423,9 +417,7 @@ def check_create(
423417
description,
424418
)
425419
except xmlrpclib.Fault as f:
426-
raise exceptions.APIError(
427-
'Error creating check: %s' % f.faultString
428-
)
420+
raise exceptions.APIError(f'Error creating check: {f.faultString}')
429421

430422

431423
class REST(API):

pwclient/patches.py

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#
55
# SPDX-License-Identifier: GPL-2.0-or-later
66

7-
import io
87
import itertools
98
import os
109
import re
@@ -48,12 +47,10 @@ def patch_field(matchobj):
4847
for patch in patches:
4948
print(format_field_re.sub(patch_field, format_str))
5049
else:
51-
print("%-7s %-12s %s" % ("ID", "State", "Name"))
52-
print("%-7s %-12s %s" % ("--", "-----", "----"))
50+
print("ID State Name")
51+
print("-- ----- ----")
5352
for patch in patches:
54-
print(
55-
"%-7d %-12s %s" % (patch['id'], patch['state'], patch['name'])
56-
)
53+
print(f"{patch['id']:<7} {patch['state']:<12} {patch['name']}")
5754

5855

5956
def action_list(
@@ -124,14 +121,14 @@ def action_info(api, patch_id):
124121
print(str(exc), file=sys.stderr)
125122
sys.exit(1)
126123

127-
s = "Information for patch id %d" % (patch_id)
124+
s = f"Information for patch id {patch_id}"
128125
print(s)
129126
print('-' * len(s))
130127
for key, value in sorted(patch.items()):
131128
if value != '':
132-
print("- %- 14s: %s" % (key, value))
129+
print(f"- {key:<14}: {value}")
133130
else:
134-
print("- %- 14s:" % key)
131+
print(f"- {key:<14}:")
135132

136133

137134
def action_get(api, patch_id):
@@ -145,12 +142,12 @@ def action_get(api, patch_id):
145142
fname += '.patch'
146143
i = 0
147144
while os.path.exists(fname):
148-
fname = "%s.%d.patch" % (base_fname, i)
145+
fname = f"{base_fname}.{i}.patch"
149146
i += 1
150147

151-
with io.open(fname, 'x', encoding='utf-8') as f:
148+
with open(fname, 'x', encoding='utf-8') as f:
152149
f.write(mbox)
153-
print('Saved patch to %s' % fname)
150+
print(f'Saved patch to {fname}')
154151

155152

156153
def action_view(api, patch_ids):
@@ -159,7 +156,7 @@ def action_view(api, patch_ids):
159156
for patch_id in patch_ids:
160157
try:
161158
mbox, _ = api.patch_get_mbox(patch_id)
162-
except Exception:
159+
except Exception: # noqa
163160
# TODO(stephenfin): We skip this for historical reasons, but should
164161
# we log/raise an error?
165162
continue
@@ -197,14 +194,13 @@ def action_apply(api, patch_id, apply_cmd=None):
197194
sys.exit(1)
198195

199196
if apply_cmd is None:
200-
print('Applying patch #%d to current directory' % patch_id)
197+
print(f'Applying patch #{patch_id} to current directory')
201198
apply_cmd = ['patch', '-p1']
202199
else:
203-
print(
204-
'Applying patch #%d using "%s"' % (patch_id, ' '.join(apply_cmd))
205-
)
200+
_cmd = ' '.join(apply_cmd)
201+
print(f'Applying patch #{patch_id} using "{_cmd}"')
206202

207-
print('Description: %s' % patch['name'])
203+
print(f"Description: {patch['name']}")
208204

209205
try:
210206
mbox, _ = api.patch_get_mbox(patch_id)

pwclient/shell.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def main(argv=sys.argv[1:]):
3939
action = args.subcmd
4040

4141
if not os.path.exists(CONFIG_FILE):
42-
sys.stderr.write('Config file not found at %s.\n' % CONFIG_FILE)
42+
sys.stderr.write(f'Config file not found at {CONFIG_FILE}.\n')
4343
sys.exit(1)
4444

4545
# grab settings from config files
@@ -52,8 +52,7 @@ def main(argv=sys.argv[1:]):
5252

5353
if not config.has_section('options'):
5454
sys.stderr.write(
55-
'No options section in %s. Did you forget to uncomment it?\n'
56-
% CONFIG_FILE
55+
f'No options section in {CONFIG_FILE}. Did you forget to uncomment it?\n'
5756
)
5857
sys.exit(1)
5958

@@ -67,19 +66,19 @@ def main(argv=sys.argv[1:]):
6766
configparser.NoOptionError,
6867
):
6968
sys.stderr.write(
70-
'No default project configured in %s\n' % CONFIG_FILE
69+
f'No default project configured in {CONFIG_FILE}\n'
7170
)
7271
sys.exit(1)
7372

7473
if not config.has_section(project_str):
7574
sys.stderr.write(
76-
'No section for project %s in %s\n' % (project_str, CONFIG_FILE)
75+
f'No section for project {project_str} in {CONFIG_FILE}\n'
7776
)
7877
sys.exit(1)
7978

8079
if not config.has_option(project_str, 'url'):
8180
sys.stderr.write(
82-
'No URL for project %s in %s\n' % (project_str, CONFIG_FILE)
81+
f'No URL for project {project_str} in {CONFIG_FILE}\n'
8382
)
8483
sys.exit(1)
8584

@@ -111,9 +110,9 @@ def main(argv=sys.argv[1:]):
111110
or config.has_option(project_str, 'token')
112111
):
113112
sys.stderr.write(
114-
"The %s action requires authentication, but no "
115-
"username/password or\n"
116-
"token is configured\n" % action
113+
f"The {action} action requires authentication, but no "
114+
f"username/password or\n"
115+
f"token is configured\n"
117116
)
118117
sys.exit(1)
119118
else:
@@ -122,9 +121,9 @@ def main(argv=sys.argv[1:]):
122121
and config.has_option(project_str, 'password')
123122
):
124123
sys.stderr.write(
125-
"The %s action requires authentication, but no "
126-
"username or password\n"
127-
"is configured\n" % action
124+
f"The {action} action requires authentication, but no "
125+
f"username or password\n"
126+
f"is configured\n"
128127
)
129128
sys.exit(1)
130129

@@ -195,7 +194,7 @@ def main(argv=sys.argv[1:]):
195194
for patch_id in patch_ids:
196195
ret = patches.action_apply(api, patch_id)
197196
if ret:
198-
sys.stderr.write("Apply failed with exit status %d\n" % ret)
197+
sys.stderr.write(f"Apply failed with exit status {ret}\n")
199198
sys.exit(1)
200199

201200
elif action == 'git_am':
@@ -237,7 +236,7 @@ def main(argv=sys.argv[1:]):
237236
for patch_id in patch_ids:
238237
ret = patches.action_apply(api, patch_id, cmd)
239238
if ret:
240-
sys.stderr.write("'git am' failed with exit status %d\n" % ret)
239+
sys.stderr.write(f"'git am' failed with exit status {ret}\n")
241240
sys.exit(1)
242241

243242
elif action == 'update':

pwclient/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
def migrate_old_config_file(config_file, config):
1313
"""Convert a config file to the Patchwork 1.0 format."""
14-
sys.stderr.write('%s is in the old format. Migrating it...' % config_file)
14+
sys.stderr.write(f'{config_file} is in the old format. Migrating it...')
1515

1616
old_project = config.get('base', 'project')
1717

@@ -35,7 +35,7 @@ def migrate_old_config_file(config_file, config):
3535

3636
sys.stderr.write(' Done.\n')
3737
sys.stderr.write(
38-
'Your old %s was saved to %s\n' % (config_file, old_config_file)
38+
f'Your old {config_file} was saved to {old_config_file}\n'
3939
)
4040
sys.stderr.write('and was converted to the new format. You may want to\n')
4141
sys.stderr.write('inspect it before continuing.\n')

pwclient/xmlrpc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def __init__(self, url):
2424
self.https = self.proxy.startswith('https')
2525

2626
def set_credentials(self, username=None, password=None):
27-
self.credentials = '%s:%s' % (username, password)
27+
self.credentials = f'{username}:{password}'
2828

2929
def make_connection(self, host):
3030
self.host = host
@@ -38,7 +38,7 @@ def make_connection(self, host):
3838
return xmlrpclib.Transport.make_connection(self, host)
3939

4040
def send_request(self, host, handler, request_body, debug):
41-
handler = '%s://%s%s' % (self.scheme, host, handler)
41+
handler = f'{self.scheme}://{host}{handler}'
4242
return xmlrpclib.Transport.send_request(
4343
self,
4444
host,

pyproject.toml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,13 @@ quote-style = "preserve"
77
docstring-code-format = true
88

99
[tool.ruff.lint]
10-
select = ["E4", "E7", "E9", "F"]
10+
select = ["E4", "E7", "E9", "F", "S", "U"]
11+
ignore = [
12+
# we only use asserts for type narrowing
13+
"S101",
14+
# this is client tool running on a user's own system: users can open
15+
# whatever urls they want to
16+
"S310",
17+
# ditto for running executables via subprocess
18+
"S603",
19+
]

tests/test_patches.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import builtins
12
from unittest import mock
23

34
import pytest
@@ -229,7 +230,7 @@ def test_action_info__invalid_id(capsys):
229230
assert captured.err == 'foo\n'
230231

231232

232-
@mock.patch.object(patches.io, 'open')
233+
@mock.patch.object(builtins, 'open')
233234
@mock.patch.object(patches.os.path, 'basename')
234235
@mock.patch.object(patches.os.path, 'exists')
235236
def test_action_get(mock_exists, mock_basename, mock_open, capsys):

tests/test_shell.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
_UNSET = object()
1616

1717

18-
class FakeConfig(object):
18+
class FakeConfig:
1919
def __init__(self, updates=None):
2020
self._data = {
2121
'options': {
@@ -119,7 +119,7 @@ def test_no_project_url(mock_config, capsys):
119119

120120
captured = capsys.readouterr()
121121

122-
assert 'No URL for project %s' % DEFAULT_PROJECT in captured.err
122+
assert f'No URL for project {DEFAULT_PROJECT}' in captured.err
123123
assert captured.out == ''
124124

125125

0 commit comments

Comments
 (0)