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.
Zero current violations. Only the 6 PT codes ruff 0.16 enables by
default; the full flake8-pytest-style linter has thousands of hits
here and needs a separate, deliberate decision.
Zero current violations. Only the 13 PLR codes ruff 0.16 enables by
default; the rest of pylint-refactor (e.g. PLR2004, PLR0913) has
hundreds of hits here and needs a separate, deliberate decision.
Zero current violations. Full category (not just the ruff-0.16
default subset) since the rest is equally applicable async-blocking
guidance for this codebase's Channels/websocket code.
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