Skip to content

Commit e28e18e

Browse files
style: pre-commit auto-fixes
[pre-commit.ci] auto-applied fixes from configured hooks
1 parent 2e96cce commit e28e18e

24 files changed

Lines changed: 182 additions & 208 deletions

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ Headline release: schema-aware linting via the new **Contracts pack**, three com
219219
- `test_duration_tracked` no longer fails on fast hardware where
220220
`time.perf_counter` resolution is coarser than the scan duration.
221221
Assertion relaxed from `> 0` to `>= 0` with an explicit float type check.
222-
- Added W014: warn on OVER() without ORDER BY / PARTITION BY to flag non-deterministic window
222+
- Added W014: warn on OVER() without ORDER BY / PARTITION BY to flag non-deterministic window
223223
functions.
224224

225225
## [0.4.1] - 2026-04-19

scripts/scaffold_rule.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def check_line(self, line: str, line_number: int, file: str):
7676
'''
7777

7878

79-
TEST_TEMPLATE = '''\
79+
TEST_TEMPLATE = """\
8080
def test_{code_lower}_fires_on_bad_sql():
8181
rule = {klass}()
8282
finding = _line(rule, {bad!r})
@@ -87,7 +87,7 @@ def test_{code_lower}_fires_on_bad_sql():
8787
def test_{code_lower}_passes_on_safe_sql():
8888
rule = {klass}()
8989
assert _line(rule, {good!r}) is None
90-
'''
90+
"""
9191

9292

9393
def main() -> int:
@@ -96,8 +96,12 @@ def main() -> int:
9696
formatter_class=argparse.RawDescriptionHelpFormatter,
9797
epilog=__doc__.split("Example:")[1] if "Example:" in __doc__ else "",
9898
)
99-
parser.add_argument("--code", required=True, help="Rule code, e.g. W024 (must match [EWTSP]NNN)")
100-
parser.add_argument("--name", required=True, help="Rule name in kebab-case, e.g. negate-of-equality")
99+
parser.add_argument(
100+
"--code", required=True, help="Rule code, e.g. W024 (must match [EWTSP]NNN)"
101+
)
102+
parser.add_argument(
103+
"--name", required=True, help="Rule name in kebab-case, e.g. negate-of-equality"
104+
)
101105
parser.add_argument("--description", required=True, help="One-line description for the rule")
102106
parser.add_argument("--regex", default="YOUR_PATTERN_HERE", help="Regex pattern to match")
103107
parser.add_argument("--message", help="Finding message (defaults to description)")
@@ -106,8 +110,12 @@ def main() -> int:
106110
default="Refactor for clarity and performance",
107111
help="Suggested fix shown alongside the finding",
108112
)
109-
parser.add_argument("--bad", default="-- TODO: bad SQL example", help="SQL example that should fire")
110-
parser.add_argument("--good", default="-- TODO: safe SQL example", help="SQL example that should not fire")
113+
parser.add_argument(
114+
"--bad", default="-- TODO: bad SQL example", help="SQL example that should fire"
115+
)
116+
parser.add_argument(
117+
"--good", default="-- TODO: safe SQL example", help="SQL example that should not fire"
118+
)
111119
args = parser.parse_args()
112120

113121
code = args.code.upper()

sql_guard/checker.py

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -127,29 +127,35 @@ def check_file(
127127
try:
128128
content = path.read_text(encoding="latin-1")
129129
except Exception as e:
130-
return [Finding(
130+
return [
131+
Finding(
132+
rule_id="SYS",
133+
severity="error",
134+
file=file_str,
135+
line=0,
136+
message=f"Cannot read file: {e}",
137+
)
138+
]
139+
except PermissionError:
140+
return [
141+
Finding(
131142
rule_id="SYS",
132143
severity="error",
133144
file=file_str,
134145
line=0,
135-
message=f"Cannot read file: {e}",
136-
)]
137-
except PermissionError:
138-
return [Finding(
139-
rule_id="SYS",
140-
severity="error",
141-
file=file_str,
142-
line=0,
143-
message="Permission denied",
144-
)]
146+
message="Permission denied",
147+
)
148+
]
145149
except OSError as e:
146-
return [Finding(
147-
rule_id="SYS",
148-
severity="error",
149-
file=file_str,
150-
line=0,
151-
message=f"Cannot read file: {e}",
152-
)]
150+
return [
151+
Finding(
152+
rule_id="SYS",
153+
severity="error",
154+
file=file_str,
155+
line=0,
156+
message=f"Cannot read file: {e}",
157+
)
158+
]
153159

154160
single_pass_rules = [r for r in rules if not r.multiline]
155161
multi_line_rules = [r for r in rules if r.multiline]

sql_guard/cli.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,13 @@
3030
@app.command("check")
3131
def check_cmd(
3232
paths: list[str] = typer.Argument(default=None, help="Files or directories to check."),
33-
severity: str = typer.Option("warning", "--severity", "-s", help="Minimum severity: error or warning."),
33+
severity: str = typer.Option(
34+
"warning", "--severity", "-s", help="Minimum severity: error or warning."
35+
),
3436
fail_fast: bool = typer.Option(False, "--fail-fast", help="Stop after first error."),
35-
disable: Optional[list[str]] = typer.Option(None, "--disable", "-d", help="Rule IDs to disable."),
37+
disable: Optional[list[str]] = typer.Option(
38+
None, "--disable", "-d", help="Rule IDs to disable."
39+
),
3640
include_python: bool = typer.Option(
3741
False,
3842
"--include-python",
@@ -96,14 +100,10 @@ def check_cmd(
96100
try:
97101
contract = Contract.from_file(effective_contract_path)
98102
except FileNotFoundError:
99-
console.print(
100-
f"[red]Contract file not found:[/red] {effective_contract_path}"
101-
)
103+
console.print(f"[red]Contract file not found:[/red] {effective_contract_path}")
102104
raise typer.Exit(code=2)
103105
except Exception as exc:
104-
console.print(
105-
f"[red]Failed to load contract {effective_contract_path}:[/red] {exc}"
106-
)
106+
console.print(f"[red]Failed to load contract {effective_contract_path}:[/red] {exc}")
107107
raise typer.Exit(code=2)
108108

109109
if changed_only:
@@ -200,12 +200,7 @@ def validate_contract_cmd(
200200

201201
column_count = sum(len(t.columns) for t in contract.tables.values())
202202
pk_count = sum(len(t.primary_keys) for t in contract.tables.values())
203-
fk_count = sum(
204-
1
205-
for t in contract.tables.values()
206-
for c in t.columns.values()
207-
if c.foreign_key
208-
)
203+
fk_count = sum(1 for t in contract.tables.values() for c in t.columns.values() if c.foreign_key)
209204

210205
console.print(
211206
f"[green]OK[/green] {contract_path}: {table_count} tables, "
@@ -251,9 +246,7 @@ def schema_snapshot_cmd(
251246
from sql_guard import snapshot as snapshot_mod
252247

253248
try:
254-
data = snapshot_mod.introspect(
255-
dsn=dsn, schema=schema, include_tables=include_table
256-
)
249+
data = snapshot_mod.introspect(dsn=dsn, schema=schema, include_tables=include_table)
257250
except snapshot_mod.SnapshotError as exc:
258251
console.print(f"[red]{exc}[/red]")
259252
raise typer.Exit(code=2)
@@ -264,9 +257,7 @@ def schema_snapshot_cmd(
264257
snapshot_mod.write_snapshot(data, output_path)
265258

266259
table_count = len(data.get("tables") or {})
267-
console.print(
268-
f"[green]OK[/green] wrote {output_path} with {table_count} tables."
269-
)
260+
console.print(f"[green]OK[/green] wrote {output_path} with {table_count} tables.")
270261

271262

272263
@app.command()

sql_guard/fluent.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,7 @@ def summary(self) -> str:
6767
if not parts:
6868
return f"no issues in {self.files_checked} file{'s' if self.files_checked != 1 else ''}"
6969
count_str = ", ".join(parts)
70-
return (
71-
f"{count_str} in "
72-
f"{self.files_checked} file{'s' if self.files_checked != 1 else ''}"
73-
)
70+
return f"{count_str} in {self.files_checked} file{'s' if self.files_checked != 1 else ''}"
7471

7572
def __len__(self) -> int:
7673
return len(self.findings)
@@ -123,7 +120,10 @@ def scan(self, sql_string: str) -> ScanResult:
123120
rules = get_rules(enabled_ids=self._enabled, disabled_ids=self._disabled)
124121

125122
with tempfile.NamedTemporaryFile(
126-
mode="w", suffix=".sql", delete=False, encoding="utf-8",
123+
mode="w",
124+
suffix=".sql",
125+
delete=False,
126+
encoding="utf-8",
127127
) as tmp:
128128
tmp.write(sql_string)
129129
tmp_path = Path(tmp.name)

sql_guard/git_filter.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,7 @@ def changed_files(base: str | None = None) -> set[Path] | None:
5454
return out
5555

5656

57-
def filter_to_changed(
58-
discovered: list[Path], base: str | None = None
59-
) -> tuple[list[Path], bool]:
57+
def filter_to_changed(discovered: list[Path], base: str | None = None) -> tuple[list[Path], bool]:
6058
"""Filter ``discovered`` to only the files git reports as changed.
6159
6260
Returns ``(filtered_paths, used_git)``. When git isn't available the

sql_guard/python_scanner.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,9 @@ def visit_Call(self, node: "cst.Call") -> None: # noqa: N802 - libcst API
175175
if target is None:
176176
return
177177
if not (
178-
isinstance(target, (cst.SimpleString, cst.ConcatenatedString, cst.FormattedString, cst.Name))
178+
isinstance(
179+
target, (cst.SimpleString, cst.ConcatenatedString, cst.FormattedString, cst.Name)
180+
)
179181
or isinstance(target, cst.BinaryOperation)
180182
or _is_dot_format(target)
181183
):

sql_guard/reporters/sarif.py

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717

1818
INFO_URI = "https://github.com/Pawansingh3889/sql-guard"
1919
SARIF_SCHEMA = (
20-
"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/"
21-
"sarif-schema-2.1.0.json"
20+
"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json"
2221
)
2322

2423

@@ -31,14 +30,16 @@ def _all_rule_descriptors() -> list[dict]:
3130
"""Build the SARIF rule catalogue."""
3231
descriptors: list[dict] = []
3332
for rule in [*ALL_RULES, *PYTHON_RULES]:
34-
descriptors.append({
35-
"id": rule.id,
36-
"name": rule.name,
37-
"shortDescription": {"text": rule.description},
38-
"fullDescription": {"text": rule.description},
39-
"defaultConfiguration": {"level": _level(rule.severity)},
40-
"helpUri": INFO_URI,
41-
})
33+
descriptors.append(
34+
{
35+
"id": rule.id,
36+
"name": rule.name,
37+
"shortDescription": {"text": rule.description},
38+
"fullDescription": {"text": rule.description},
39+
"defaultConfiguration": {"level": _level(rule.severity)},
40+
"helpUri": INFO_URI,
41+
}
42+
)
4243
return descriptors
4344

4445

@@ -50,14 +51,16 @@ def build(result: CheckResult) -> dict:
5051
"ruleId": finding.rule_id,
5152
"level": _level(finding.severity),
5253
"message": {"text": finding.message},
53-
"locations": [{
54-
"physicalLocation": {
55-
"artifactLocation": {
56-
"uri": str(Path(finding.file).as_posix()),
54+
"locations": [
55+
{
56+
"physicalLocation": {
57+
"artifactLocation": {
58+
"uri": str(Path(finding.file).as_posix()),
59+
},
60+
"region": {"startLine": max(1, finding.line)},
5761
},
58-
"region": {"startLine": max(1, finding.line)},
59-
},
60-
}],
62+
}
63+
],
6164
}
6265
if finding.suggestion:
6366
sarif_result["message"]["text"] += f" -- {finding.suggestion}"
@@ -66,17 +69,19 @@ def build(result: CheckResult) -> dict:
6669
return {
6770
"$schema": SARIF_SCHEMA,
6871
"version": "2.1.0",
69-
"runs": [{
70-
"tool": {
71-
"driver": {
72-
"name": "sql-guard",
73-
"version": __version__,
74-
"informationUri": INFO_URI,
75-
"rules": _all_rule_descriptors(),
72+
"runs": [
73+
{
74+
"tool": {
75+
"driver": {
76+
"name": "sql-guard",
77+
"version": __version__,
78+
"informationUri": INFO_URI,
79+
"rules": _all_rule_descriptors(),
80+
},
7681
},
77-
},
78-
"results": results,
79-
}],
82+
"results": results,
83+
}
84+
],
8085
}
8186

8287

sql_guard/rules/contracts.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,7 @@ def check_statement(self, statement: str, start_line: int, file: str) -> Finding
8484
f"for table '{table_name}'"
8585
),
8686
suggestion=(
87-
f"Either add '{ref_col}' to the contract or correct "
88-
f"the column name"
87+
f"Either add '{ref_col}' to the contract or correct the column name"
8988
),
9089
)
9190
return None
@@ -162,9 +161,7 @@ def check_statement(self, statement: str, start_line: int, file: str) -> Finding
162161
if table is None:
163162
return None
164163

165-
listed_cols = {
166-
c.strip().strip("[]`\"").lower() for c in m.group(2).split(",")
167-
}
164+
listed_cols = {c.strip().strip('[]`"').lower() for c in m.group(2).split(",")}
168165
missing = [c for c in table.required_columns if c not in listed_cols]
169166

170167
if missing:
@@ -216,9 +213,7 @@ def check_statement(self, statement: str, start_line: int, file: str) -> Finding
216213
if table is None or not table.primary_keys:
217214
return None
218215

219-
listed_cols = {
220-
c.strip().strip("[]`\"").lower() for c in m.group(2).split(",")
221-
}
216+
listed_cols = {c.strip().strip('[]`"').lower() for c in m.group(2).split(",")}
222217
missing_pks = [
223218
pk
224219
for pk in table.primary_keys
@@ -274,8 +269,11 @@ class UnmappedForeignKey(ContractRule):
274269
)
275270

276271
def _fk_resolves(
277-
self, source_table_name: str, source_col: str,
278-
target_table_name: str, target_col: str,
272+
self,
273+
source_table_name: str,
274+
source_col: str,
275+
target_table_name: str,
276+
target_col: str,
279277
) -> bool:
280278
if self.contract is None:
281279
return False
@@ -287,8 +285,7 @@ def _fk_resolves(
287285
return False
288286
ref_table, _, ref_col = col.foreign_key.partition(".")
289287
return (
290-
ref_table.lower() == target_table_name.lower()
291-
and ref_col.lower() == target_col.lower()
288+
ref_table.lower() == target_table_name.lower() and ref_col.lower() == target_col.lower()
292289
)
293290

294291
def check_statement(self, statement: str, start_line: int, file: str) -> Finding | None:

sql_guard/rules/python_rules.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,7 @@ def check(self, hit: ExtractedSql, file: str) -> Finding | None:
148148
file=file,
149149
line=hit.line,
150150
message="f-string passed to sqlalchemy.text() -- SQL injection risk",
151-
suggestion=(
152-
"Use bound parameters: text(\"... WHERE id = :id\"), {\"id\": user_id}"
153-
),
151+
suggestion=('Use bound parameters: text("... WHERE id = :id"), {"id": user_id}'),
154152
)
155153
return None
156154

0 commit comments

Comments
 (0)