Skip to content

Commit fda90c5

Browse files
Fix EM102 linter errors: assign f-strings to variables before raising exceptions
Agent-Logs-Url: https://github.com/GitHubSecurityLab/seclab-taskflow-agent/sessions/ccb4b71f-fdce-4a18-bfc2-ef68f24b8187 Co-authored-by: kevinbackhouse <4358136+kevinbackhouse@users.noreply.github.com>
1 parent fe02b5d commit fda90c5

15 files changed

Lines changed: 84 additions & 55 deletions

File tree

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ target-version = "py310"
171171
ignore = [
172172
# Style choices — deliberate project conventions
173173
"EM101", # Exception string literals
174-
"EM102", # Exception f-strings
175174
"G004", # Logging f-strings
176175
"T201", # print() used for user output
177176
"TRY003", # Raise with inline message strings

src/seclab_taskflow_agent/agent.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ def __init__(
172172
if token:
173173
resolved_token = os.getenv(token, "")
174174
if not resolved_token:
175-
raise RuntimeError(f"Token env var {token!r} is not set")
175+
msg = f"Token env var {token!r} is not set"
176+
raise RuntimeError(msg)
176177
else:
177178
resolved_token = get_AI_token()
178179

src/seclab_taskflow_agent/available_tools.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -108,18 +108,18 @@ def _load(self, tooltype: AvailableToolType, toolname: str) -> DocumentModel:
108108
# Resolve package and filename from dotted path
109109
components = toolname.rsplit(".", 1)
110110
if len(components) != 2:
111-
raise BadToolNameError(
111+
msg = (
112112
f'Not a valid toolname: "{toolname}". '
113113
f'Expected format: "packagename.filename"'
114114
)
115+
raise BadToolNameError(msg)
115116
package, filename = components
116117

117118
try:
118119
pkg_dir = importlib.resources.files(package)
119120
if not pkg_dir.is_dir():
120-
raise BadToolNameError(
121-
f"Cannot load {toolname} because {pkg_dir} is not a valid directory."
122-
)
121+
msg = f"Cannot load {toolname} because {pkg_dir} is not a valid directory."
122+
raise BadToolNameError(msg)
123123
filepath = pkg_dir.joinpath(filename + ".yaml")
124124
with filepath.open() as fh:
125125
raw = yaml.safe_load(fh)
@@ -128,17 +128,17 @@ def _load(self, tooltype: AvailableToolType, toolname: str) -> DocumentModel:
128128
header = raw.get("seclab-taskflow-agent", {})
129129
filetype = header.get("filetype", "")
130130
if filetype != tooltype.value:
131-
raise FileTypeException(
131+
msg = (
132132
f"Error in {filepath}: expected filetype {tooltype.value!r}, "
133133
f"got {filetype!r}."
134134
)
135+
raise FileTypeException(msg)
135136

136137
# Parse into the appropriate Pydantic model
137138
model_cls = DOCUMENT_MODELS.get(filetype)
138139
if model_cls is None:
139-
raise BadToolNameError(
140-
f"Unknown filetype {filetype!r} in {toolname}"
141-
)
140+
msg = f"Unknown filetype {filetype!r} in {toolname}"
141+
raise BadToolNameError(msg)
142142

143143
try:
144144
doc = model_cls(**raw)
@@ -147,9 +147,8 @@ def _load(self, tooltype: AvailableToolType, toolname: str) -> DocumentModel:
147147
for err in exc.errors():
148148
if "Unsupported version" in str(err.get("msg", "")):
149149
raise VersionException(str(err["msg"])) from exc
150-
raise BadToolNameError(
151-
f"Validation error loading {toolname}: {exc}"
152-
) from exc
150+
msg = f"Validation error loading {toolname}: {exc}"
151+
raise BadToolNameError(msg) from exc
153152

154153
# Cache and return
155154
if tooltype not in self._cache:
@@ -158,10 +157,11 @@ def _load(self, tooltype: AvailableToolType, toolname: str) -> DocumentModel:
158157
return doc
159158

160159
except ModuleNotFoundError as exc:
161-
raise BadToolNameError(f"Cannot load {toolname}: {exc}") from exc
160+
msg = f"Cannot load {toolname}: {exc}"
161+
raise BadToolNameError(msg) from exc
162162
except FileNotFoundError:
163-
raise BadToolNameError(
164-
f"Cannot load {toolname} because {filepath} is not a valid file."
165-
)
163+
msg = f"Cannot load {toolname} because {filepath} is not a valid file."
164+
raise BadToolNameError(msg)
166165
except ValueError as exc:
167-
raise BadToolNameError(f"Cannot load {toolname}: {exc}") from exc
166+
msg = f"Cannot load {toolname}: {exc}"
167+
raise BadToolNameError(msg) from exc

src/seclab_taskflow_agent/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
def _parse_global(value: str) -> tuple[str, str]:
3737
"""Parse a ``KEY=VALUE`` string into a (key, value) pair."""
3838
if "=" not in value:
39-
raise typer.BadParameter(f"Invalid global variable format: {value!r}. Expected KEY=VALUE.")
39+
msg = f"Invalid global variable format: {value!r}. Expected KEY=VALUE."
40+
raise typer.BadParameter(msg)
4041
key, _, val = value.partition("=")
4142
return key.strip(), val.strip()
4243

src/seclab_taskflow_agent/mcp_lifecycle.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ def _print_err(line: str) -> None:
116116
client_session_timeout_seconds=client_session_timeout,
117117
)
118118
case _:
119-
raise ValueError(f"Unsupported MCP transport: {params['kind']}")
119+
msg = f"Unsupported MCP transport: {params['kind']}"
120+
raise ValueError(msg)
120121

121122
entries.append(MCPServerEntry(MCPNamespaceWrap(confirms, mcp_server), server_proc, name=tb))
122123

src/seclab_taskflow_agent/mcp_servers/codeql/client.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,8 @@ def _file_uri_to_path(uri):
467467
# note: don't try to parse paths like "file://a/b" because that returns "/b", should be "file:///a/b"
468468
parsed = urlparse(uri)
469469
if parsed.scheme != "file":
470-
raise ValueError(f"Not a file:// uri: {uri}")
470+
msg = f"Not a file:// uri: {uri}"
471+
raise ValueError(msg)
471472
path = unquote(parsed.path)
472473
region = None
473474
if ":" in path:
@@ -605,7 +606,8 @@ def run_query(
605606
if target:
606607
target_pos = get_query_position(query_path, target)
607608
if not target_pos:
608-
raise ValueError(f"Could not resolve quick eval target for {target}")
609+
msg = f"Could not resolve quick eval target for {target}"
610+
raise ValueError(msg)
609611
try:
610612
with (
611613
QueryServer(database, keep_alive=keep_alive, log_stderr=log_stderr) as server,
@@ -635,5 +637,6 @@ def run_query(
635637
case _:
636638
raise ValueError("Unsupported output format {fmt}")
637639
except Exception as e:
638-
raise RuntimeError(f"Error in run_query: {e}") from e
640+
msg = f"Error in run_query: {e}"
641+
raise RuntimeError(msg) from e
639642
return result

src/seclab_taskflow_agent/mcp_servers/codeql/jsonrpyc/__init__.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ def check_id(cls, id: str | int | None, *, allow_empty: bool = False) -> None:
8686
:raises TypeError: When *id* is invalid.
8787
"""
8888
if (id is not None or not allow_empty) and not isinstance(id, (int, str)):
89-
raise TypeError(f"id must be an integer or string, got {id} ({type(id)})")
89+
msg = f"id must be an integer or string, got {id} ({type(id)})"
90+
raise TypeError(msg)
9091

9192
@classmethod
9293
def check_method(cls, method: str, /) -> None:
@@ -98,7 +99,8 @@ def check_method(cls, method: str, /) -> None:
9899
:raises TypeError: When *method* is invalid.
99100
"""
100101
if not isinstance(method, str):
101-
raise TypeError(f"method must be a string, got {method} ({type(method)})")
102+
msg = f"method must be a string, got {method} ({type(method)})"
103+
raise TypeError(msg)
102104

103105
@classmethod
104106
def check_code(cls, code: int, /) -> None:
@@ -113,7 +115,8 @@ def check_code(cls, code: int, /) -> None:
113115
try:
114116
get_error(code)
115117
except Exception:
116-
raise TypeError(f"invalid error code, got {code} ({type(code)})")
118+
msg = f"invalid error code, got {code} ({type(code)})"
119+
raise TypeError(msg)
117120

118121
@classmethod
119122
def request(
@@ -502,7 +505,8 @@ def _handle_method(self, req: dict[str, Any]) -> None:
502505
try:
503506
method = req["method"]
504507
if method not in self.method_handlers:
505-
raise ValueError(f"No handler defined for method: {method}")
508+
msg = f"No handler defined for method: {method}"
509+
raise ValueError(msg)
506510
result = self.method_handlers[method](req["params"])
507511
if "id" in req:
508512
res = Spec.response(req["id"], result)
@@ -752,7 +756,8 @@ def run(self) -> None:
752756
print(f"Incoming jsonrpc message: {decoded_msg}")
753757
self.rpc._handle(decoded_msg)
754758
else:
755-
raise ValueError(f"Don't know how to handle: {decoded_line}")
759+
msg = f"Don't know how to handle: {decoded_line}"
760+
raise ValueError(msg)
756761
else:
757762
self._stopper.wait(self.interval)
758763

@@ -836,13 +841,16 @@ class MyCustomRPCError(RPCError):
836841
title = "My custom error"
837842
"""
838843
if not issubclass(cls, RPCError):
839-
raise TypeError(f"'{cls}' is not a subclass of RPCError")
844+
msg = f"'{cls}' is not a subclass of RPCError"
845+
raise TypeError(msg)
840846

841847
# check duplicates
842848
if cls.code in error_map_code:
843-
raise AttributeError(f"duplicate RPC error code {cls.code}")
849+
msg = f"duplicate RPC error code {cls.code}"
850+
raise AttributeError(msg)
844851
if cls.code_range in error_map_code_range:
845-
raise AttributeError(f"duplicate RPC error code range {cls.code_range}")
852+
msg = f"duplicate RPC error code range {cls.code_range}"
853+
raise AttributeError(msg)
846854

847855
# register
848856
error_map_code[cls.code] = cls
@@ -867,7 +875,8 @@ def get_error(code: int) -> type[RPCError]:
867875
if lower <= code <= upper:
868876
return cls
869877

870-
raise ValueError(f"unknown error code '{code}' ({type(code)})")
878+
msg = f"unknown error code '{code}' ({type(code)})"
879+
raise ValueError(msg)
871880

872881

873882
@register_error

src/seclab_taskflow_agent/mcp_servers/codeql/mcp_server.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@
5353
def _resolve_query_path(language: str, query: str) -> Path:
5454
global TEMPLATED_QUERY_PATHS
5555
if language not in TEMPLATED_QUERY_PATHS:
56-
raise RuntimeError(f"Error: Language `{language}` not supported!")
56+
msg = f"Error: Language `{language}` not supported!"
57+
raise RuntimeError(msg)
5758
query_path = TEMPLATED_QUERY_PATHS[language].get(query)
5859
if not query_path:
59-
raise RuntimeError(f"Error: query `{query}` not supported for `{language}`!")
60+
msg = f"Error: query `{query}` not supported for `{language}`!"
61+
raise RuntimeError(msg)
6062
return Path(query_path)
6163

6264

@@ -69,7 +71,8 @@ def _resolve_db_path(relative_db_path: str | Path):
6971
absolute_path = CODEQL_DBS_BASE_PATH / relative_db_path
7072
if not absolute_path.is_dir():
7173
_debug_log(f"Database path not found: {absolute_path}")
72-
raise RuntimeError(f"Error: Database not found at {absolute_path}!")
74+
msg = f"Error: Database not found at {absolute_path}!"
75+
raise RuntimeError(msg)
7376
return absolute_path
7477

7578

src/seclab_taskflow_agent/mcp_transport.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ async def async_wait_for_connection(
109109
host = parsed.hostname
110110
port = parsed.port
111111
if host is None or port is None:
112-
raise ValueError(f"URL must include a host and port: {self.url}")
112+
msg = f"URL must include a host and port: {self.url}"
113+
raise ValueError(msg)
113114
deadline = asyncio.get_event_loop().time() + timeout
114115
while True:
115116
try:
@@ -119,7 +120,8 @@ async def async_wait_for_connection(
119120
return
120121
except (OSError, ConnectionRefusedError):
121122
if asyncio.get_event_loop().time() > deadline:
122-
raise TimeoutError(f"Could not connect to {host}:{port} after {timeout} seconds")
123+
msg = f"Could not connect to {host}:{port} after {timeout} seconds"
124+
raise TimeoutError(msg)
123125
await asyncio.sleep(poll_interval)
124126

125127
def wait_for_connection(
@@ -139,15 +141,17 @@ def wait_for_connection(
139141
host = parsed.hostname
140142
port = parsed.port
141143
if host is None or port is None:
142-
raise ValueError(f"URL must include a host and port: {self.url}")
144+
msg = f"URL must include a host and port: {self.url}"
145+
raise ValueError(msg)
143146
deadline = time.time() + timeout
144147
while True:
145148
try:
146149
with socket.create_connection((host, port), timeout=2):
147150
return
148151
except OSError:
149152
if time.time() > deadline:
150-
raise TimeoutError(f"Could not connect to {host}:{port} after {timeout} seconds")
153+
msg = f"Could not connect to {host}:{port} after {timeout} seconds"
154+
raise TimeoutError(msg)
151155
time.sleep(poll_interval)
152156

153157
def run(self) -> None:

src/seclab_taskflow_agent/mcp_utils.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ def mcp_client_params(
202202
logging.debug(f"Initializing streamable toolbox: {tb}\nargs:\n{args}\nenv:\n{env}\n")
203203
exe = shutil.which(sp.command)
204204
if exe is None:
205-
raise FileNotFoundError(f"Could not resolve path to {sp.command}")
205+
msg = f"Could not resolve path to {sp.command}"
206+
raise FileNotFoundError(msg)
206207
start_cmd = [exe]
207208
if args:
208209
for i, v in enumerate(args):
@@ -220,7 +221,8 @@ def mcp_client_params(
220221
server_params["env"] = env
221222

222223
case _:
223-
raise ValueError(f"Unsupported MCP transport {kind}")
224+
msg = f"Unsupported MCP transport {kind}"
225+
raise ValueError(msg)
224226

225227
client_params[tb] = (
226228
server_params,

0 commit comments

Comments
 (0)