Skip to content

Commit 656c5ff

Browse files
committed
Add SonarQube/Codacy conventions and fix silent except
1 parent 68c4f8d commit 656c5ff

2 files changed

Lines changed: 79 additions & 3 deletions

File tree

CLAUDE.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,78 @@ All code must follow secure-by-default principles. Review every change against t
154154
- Do not add new dependencies without reviewing their security posture (maintained? known CVEs?)
155155
- Avoid transitive dependency bloat — prefer stdlib solutions when the alternative is a single-function dependency
156156

157+
## Code quality (SonarQube / Codacy compliance)
158+
159+
All code must satisfy common static-analysis rules enforced by SonarQube and Codacy. Review each change against the checklist below.
160+
161+
### Complexity & size
162+
- Cyclomatic complexity per function: ≤ 15 (hard cap 20). Break large branches into helpers
163+
- Cognitive complexity per function: ≤ 15. Flatten nested `if`/`for`/`try` chains with early returns or guard clauses
164+
- Function length: ≤ 75 lines of code (excluding docstring / blank lines). Extract helpers past that
165+
- Parameter count: ≤ 7 per function/method. Use a dataclass or typed dict when more are needed
166+
- Nesting depth: ≤ 4 levels of `if`/`for`/`while`/`try`. Refactor with early returns instead of pyramids
167+
- File length: ≤ 1000 lines — split modules past that
168+
- Class `__init__`: keep attribute count reasonable; if a class has > 15 instance attributes, split responsibilities
169+
170+
### Exception handling
171+
- Never use bare `except:` — always specify exception types
172+
- Avoid catching `Exception` or `BaseException` unless immediately re-raising or logging and re-raising with context
173+
- Never `pass` silently inside `except` — log the error via `pybreeze_logger` (at minimum `.debug()`) with context
174+
- Do not `return` / `break` / `continue` inside a `finally` block — it swallows exceptions
175+
- Custom exceptions must inherit from `ITEException`; never `raise Exception(...)` directly
176+
- Use `raise ... from err` (or `raise ... from None`) when re-raising to preserve / suppress the chain explicitly
177+
178+
### Pythonic correctness
179+
- Compare with `None` using `is` / `is not`, never `==` / `!=`
180+
- Type checks use `isinstance(obj, T)`, never `type(obj) == T`
181+
- Never use mutable default arguments (`def f(x=[])`) — use `None` and initialise inside
182+
- Prefer f-strings over `%` formatting or `str.format()`
183+
- Use context managers (`with open(...) as f:`) for every file / socket / lock — never leave resources to GC
184+
- Use `enumerate()` instead of `range(len(...))` when the index is needed alongside the item
185+
- Use `dict.get(key, default)` instead of `key in dict and dict[key]` patterns
186+
- Use set / dict comprehensions when clearer than manual loops; avoid comprehensions with side effects
187+
188+
### Naming & style (PEP 8)
189+
- `snake_case` for functions, methods, variables, module names
190+
- `PascalCase` for classes
191+
- `UPPER_SNAKE_CASE` for module-level constants
192+
- `_leading_underscore` for protected / internal members; never use `__dunder__` for custom attributes
193+
- No single-letter names except loop indices (`i`, `j`) or conventional math (`x`, `y`)
194+
- Do not shadow built-ins (`id`, `type`, `list`, `dict`, `input`, `file`, `open`, etc.) — rename the local variable
195+
196+
### Duplication & dead code
197+
- String literal used 3+ times in the same module → extract a module-level constant
198+
- Identical 6+ line blocks in 2+ places → extract a helper function
199+
- Remove unused imports, unused parameters, unused local variables, unreachable code after `return` / `raise`
200+
- No commented-out code blocks — delete them (git history is the archive)
201+
- No `TODO` / `FIXME` / `XXX` without an accompanying issue reference (`# TODO(#123): ...`)
202+
203+
### Logging, printing, assertions
204+
- Never use `print()` for diagnostics in library / runtime code — use `pybreeze_logger`
205+
- Use lazy logging (`logger.debug("x=%s", x)`) — avoid eager f-string formatting inside log calls on hot paths
206+
- Never use `assert` for runtime validation (Python strips assertions with `-O`). Use explicit `if … raise …` instead; `assert` is only for test code
207+
208+
### Hardcoded values & secrets
209+
- No hardcoded passwords, tokens, API keys, or secrets — use env vars or a config file excluded from VCS
210+
- No hardcoded IP addresses or hostnames outside of `localhost` / documented loopback — use config
211+
- Magic numbers (except 0, 1, -1) should be named constants when repeated or non-obvious
212+
213+
### Boolean & return hygiene
214+
- Replace `if cond: return True else: return False` with `return bool(cond)` or `return cond`
215+
- Replace `if x == True` / `if x == False` with `if x` / `if not x`
216+
- A function should have a consistent return type — never mix `return value` and bare `return` (returns `None`) on meaningful paths unless explicitly documented
217+
- Do not return inside a generator function (`yield` + `return value` is a syntax pitfall)
218+
219+
### Imports
220+
- One import per line for `import` statements; grouped `from x import a, b` is fine
221+
- Order: stdlib → third-party → first-party (`pybreeze.*`) — separated by blank lines
222+
- No wildcard imports (`from x import *`) outside of `__init__.py` re-exports
223+
- No relative imports beyond one level (`from ..pkg import x` OK, `from ...pkg import x` avoid)
224+
225+
### Running the linters
226+
- Before committing any non-trivial change, run `ruff check pybreeze/` locally to catch these rules — `ruff` covers the majority of SonarQube/Codacy Python rules
227+
- When adding a new rule exception, justify it in a `# noqa: RULE` comment with a short reason — never blanket-disable
228+
157229
## Commit & PR rules
158230

159231
- Commit messages: short imperative sentence (e.g., "Update stable version", "Fix github actions")

pybreeze/pybreeze_ui/diagram_editor/diagram_scene.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from je_editor import language_wrapper
1111

1212
from pybreeze.pybreeze_ui.diagram_editor.diagram_commands import DiagramSnapshotCommand
13-
from pybreeze.pybreeze_ui.diagram_editor.diagram_net_utils import safe_download_image
1413
from pybreeze.pybreeze_ui.diagram_editor.diagram_items import (
1514
ConnectionStyle,
1615
DiagramConnection,
@@ -19,6 +18,11 @@
1918
NodeShape,
2019
ResizeHandle,
2120
)
21+
from pybreeze.pybreeze_ui.diagram_editor.diagram_net_utils import (
22+
ImageDownloadError,
23+
safe_download_image,
24+
)
25+
from pybreeze.utils.logging.logger import pybreeze_logger
2226

2327

2428
class ToolMode(Enum):
@@ -595,8 +599,8 @@ def _try_load_image_source(self, img: DiagramImage, source: str) -> None:
595599
pix.loadFromData(data)
596600
if not pix.isNull():
597601
img.set_pixmap(pix, source)
598-
except Exception:
599-
pass
602+
except (ImageDownloadError, OSError) as err:
603+
pybreeze_logger.debug("safe_download_image failed: %s", err)
600604

601605
def load_from_dict(self, data: dict) -> None:
602606
self._clear_items()

0 commit comments

Comments
 (0)