Full category (10/10 codes are all default in ruff 0.16). Of 54
hits, 46 were in test fixture code constructing naive datetimes for
comparison/input purposes only - added DTZ to the existing
per-file-ignores for */tests/*.py alongside E501/SIM117.
The 8 production hits:
- documents/consumer.py, documents/views.py (index_last_modified):
suppressed with noqa - timezone.make_aware() requires a naive
datetime, so wrapping fromtimestamp() in tz= would break it
- documents/double_sided.py (x2): switched to
datetime.now(tz=UTC).timestamp() - behavior-identical since
.timestamp() returns the same epoch value regardless of the
attached tz, but now explicit
- documents/views.py (x2, upload temp file mtime): suppressed with
noqa - mktime() requires a local time tuple, so an aware/UTC now()
would introduce a timezone-offset bug
- documents/workflows/ai.py (AI suggested date parsing): suppressed
with noqa - only .date() is used, time/tz is discarded
- paperless_mail/mail.py (IMAP fetch date filter): switched
date.today() to timezone.localdate(), which respects
settings.TIME_ZONE instead of the system clock - a real
correctness improvement when they differ
- paperless_mail/views.py (placeholder name string): switched
datetime.datetime.now() to timezone.now(), matching the app's
existing aware-datetime convention
Only the 5 default-subset codes; the rest of tryceratops is opt-in.
- 9 TRY201 (raise e -> raise) autofixed, preserving the traceback
identically while dropping the redundant exception name
- 26 TRY401 (redundant exception object passed to logger.exception,
which already logs it) fixed by removing the duplicate from the
message; three sites still needed the exception object for
something else (re-raising, or a separate logger.error call) and
kept their binding
- 6 TRY002 (raise bare Exception): 2 production sites (documents/
matching.py, paperless_mail/preprocessor.py) got dedicated
exception classes, with their tests narrowed to match instead of
asserting a blind Exception; the other 4 are deliberate generic
failures in test doubles/fixtures, suppressed with noqa
Full category (not just the ruff-0.16 default subset). 35 hits:
- 4 PLW0108 (unnecessary lambda) autofixed
- 6 PLW2901 (loop/with variable shadowed) renamed to distinct names
- 2 PLW1510 (subprocess.run without explicit check) given check=False,
matching existing behavior exactly
- 6 PLW0602 (global declared but never assigned) removed - these were
all in-place mutations (.append/.insert), not reassignments, so
`global` was already a no-op
- 7 PLW0603 (global statement) suppressed with noqa - these are
genuine lazy-init singletons with no class to hold the state;
refactoring them is a separate, larger change
- 6 PLW1508 (non-str/None env var default) fixed using the existing
get_int_from_env/get_float_from_env typed helpers instead of raw
os.getenv, which also fixes a real bug: LOGROTATE_MAX_SIZE and
LOGROTATE_MAX_BACKUPS were never wrapped in int(), so a string env
var value would have flowed into RotatingFileHandler as a string
- 1 PLW1641 (__eq__ without __hash__) fixed by adding __hash__ to
PlaceholderString
All 4 hits in documents/validators.py were f-strings inside gettext
_() calls, which resolves the string before translation and breaks
extraction (confirmed: locale .po files literally contain the raw
"{value}" placeholder as msgid text). Fixed by using %(name)s-style
placeholders with Django ValidationError's existing params= kwarg,
which was already being passed but silently unused.
3 S110 (try-except-pass) hits, all fixed by adding a log call in the
except block rather than silently swallowing the exception, matching
this codebase's existing %s lazy-formatting logging convention.
Behavior is unchanged (still no re-raise) in all three spots.
3 B009 (getattr with a constant string, rewrite as attribute access)
hits autofixed. 7 B017 (assert blind Exception) hits: one narrowed
to the actual ValueError raised by bulk_edit.edit_pdf, the other six
suppressed with noqa since the code under test genuinely raises (or
a mock genuinely injects) a bare Exception, so a narrower assertion
would be wrong.
Only the 29 B codes ruff 0.16 enables by default; the rest of
flake8-bugbear needs a separate, deliberate decision.
G202 (redundant exc_info=True passed to logger.exception, which
already includes the traceback) had 2 hits in documents/views.py,
fixed manually since ruff has no autofix for it. G101 (hardcoded
password string) had zero hits.
All part of ruff 0.16's expanded default rule set. FA and G010 had
zero existing violations; PERF402's one occurrence needed a manual
fix since ruff can't safely autofix a multi-line call expression.
C4 is part of ruff 0.16's expanded default rule set and is almost
entirely autofixable, making it a low-risk first step towards
adopting the new defaults.
* Security: validate remote OCR endpoint against internal SSRF
Adds PAPERLESS_REMOTE_OCR_ALLOW_INTERNAL_ENDPOINTS (default true)
and validates remote_ocr_endpoint via validate_outbound_http_url
on the config serializer, matching the existing LLM endpoint handling.
* Validates te outbound url again right before use
* cover empty-value branch of validate_remote_ocr_endpoint because coverage
* re-validate remote OCR endpoint on every outbound request