Skip to content

Add comprehensive ruff linting configuration#159

Merged
jaredmixpanel merged 2 commits intomasterfrom
jared-ruff-linting
Mar 23, 2026
Merged

Add comprehensive ruff linting configuration#159
jaredmixpanel merged 2 commits intomasterfrom
jared-ruff-linting

Conversation

@jaredmixpanel
Copy link
Contributor

Summary

Builds on #157 (ruff formatting) by adding full ruff linting with select = ["ALL"] — the "allowlist by exclusion" approach where every rule is enabled and only explicitly documented exceptions are ignored. This brings the ruff configuration into conformance with our strict Python guide.

What Changed

Configuration (pyproject.toml)

Added comprehensive [tool.ruff] configuration:

  • select = ["ALL"] — every ruff rule enabled by default
  • Explicit ignores with documented rationale for each:
    • Rule conflicts (D203/D211, COM812/ISC001 formatter conflicts)
    • ANN (type annotations) — 150+ violations, separate effort
    • D1xx (missing docstrings) — separate effort
    • FBT (boolean arguments) — public API, can't change
    • EM101/EM103/TRY003 (exception message style) — too invasive
    • PLR0913 (too-many-arguments) — public API signatures
    • E501 (line-too-long) — formatter handles code; remaining are strings/comments
    • FA100 (future annotations) — interacts with Pydantic runtime, deferred
  • Per-file ignores for test files (S101, PLR2004, D, etc.), demo files (T201, INP001), docs/conf.py, and mixpanel/flags/types.py (A005 — shadows stdlib types module)
  • isort with known-first-party = ["mixpanel"]
  • pydocstyle with convention = "google"

CI (.github/workflows/test.yml)

Added ruff check . step alongside existing ruff format --check . in the lint job.

Pre-commit (.pre-commit-config.yaml — new file)

Added ruff pre-commit hooks for local development:

  • ruff-format — auto-format on commit
  • ruff with --fix — auto-fix lint issues on commit

Added pre-commit to [project.optional-dependencies] dev.

Code Fixes

Type annotation modernization:

  • Replaced typing.Dictdict, typing.Listlist, typing.Tupletuple (PEP 585, safe on Python 3.9+)
  • Added from __future__ import annotations to flags module files
  • Removed deprecated typing imports (Optional, Dict, List, Tuple)
  • Converted Optional[X]X | None (safe with __future__ annotations)

Logging improvements:

  • Converted f-string logging to lazy %s formatting (G004) — avoids string interpolation when log level is disabled
  • Replaced root logger calls with module-level logger = logging.getLogger(__name__) (LOG015)

Docstring fixes:

  • Added blank lines between summary and description (D205)
  • Added terminal punctuation to summary lines (D415)
  • Collapsed unnecessary multiline docstrings (D200)
  • Replaced empty docstring with meaningful description (D419)

Code quality fixes:

  • == True/== Falseis True/is False (E712)
  • Added from None to re-raised exception (B904)
  • Extracted f-strings from exception constructors (EM102)
  • Combined collapsible if statements (SIM102)
  • Removed .keys() from dict iteration (SIM118)
  • Removed unnecessary format string indices (UP030)
  • Replaced dict() with {} (C408)
  • Prefixed unused loop variables with _ (B007)
  • Removed unused variables (F841)
  • Replaced os.path.abspath with Path.resolve() (PTH100)
  • Converted parametrize name strings to tuples (PT006)
  • Fixed TODO tag format (TD001)

Pragmatic noqa comments (with rationale):

  • DTZ005/DTZ001 — datetime timing measurements where timezone is irrelevant
  • N818MixpanelException is a public API name, can't add Error suffix
  • N803reportExposure parameter matches external API convention
  • RUF006 — intentional fire-and-forget asyncio.create_task

What's Deferred (future PRs)

Category Rule(s) Reason
Type annotations ANN 150+ violations, requires dedicated effort
Missing docstrings D100-D107 80+ violations, separate documentation pass
Future annotations FA100 Requires from __future__ import annotations in all files + Pydantic testing
Boolean arguments FBT Public API design, would break callers

Test Plan

  • uvx ruff check . — zero violations
  • uvx ruff format --check . — no formatting changes needed
  • pytest — all 124 tests pass
  • CI lint job passes both format and check steps
  • Verify pre-commit hooks work: pip install pre-commit && pre-commit run --all-files

Build on the ruff formatting added in #157 by adding full linting
with select = ["ALL"] per our strict Python guide. This enforces
all ruff rules with explicit, documented exclusions for rules that
are inappropriate for the existing codebase.

Changes:
- Add [tool.ruff.lint] config to pyproject.toml with select = ["ALL"]
- Add per-file-ignores for test, demo, and docs files
- Add isort config with known-first-party = ["mixpanel"]
- Add pydocstyle convention = "google"
- Add ruff check step to CI workflow
- Add .pre-commit-config.yaml with ruff format + lint hooks
- Add pre-commit to dev dependencies
- Modernize type annotations (Dict -> dict, List -> list)
- Add from __future__ import annotations to flags modules
- Convert logging f-strings to lazy %s formatting
- Add module-level loggers replacing root logger calls
- Fix docstring formatting (blank lines, punctuation)
- Fix true/false comparisons (== True -> is True)
- Fix raise-without-from in exception handler
- Extract f-strings from exception constructors
- Various small fixes (SIM102, SIM118, UP030, C408, etc.)
The module name shadows stdlib `types`, but renaming would break
imports across the codebase.
@codecov
Copy link

codecov bot commented Mar 23, 2026

Codecov Report

❌ Patch coverage is 96.18321% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.20%. Comparing base (7aa7123) to head (c371f44).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
mixpanel/__init__.py 85.71% 2 Missing ⚠️
mixpanel/flags/local_feature_flags.py 96.66% 0 Missing and 1 partial ⚠️
mixpanel/flags/remote_feature_flags.py 97.14% 1 Missing ⚠️
test_mixpanel.py 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #159      +/-   ##
==========================================
- Coverage   94.28%   94.20%   -0.08%     
==========================================
  Files           9        9              
  Lines        1557     1554       -3     
  Branches      101      100       -1     
==========================================
- Hits         1468     1464       -4     
- Misses         54       56       +2     
+ Partials       35       34       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jerrylin3321
Copy link
Member

96% test coverage

python -m coverage run -m pytest 2>&1 && python -m coverage report -m 2>&1

OUT
============================= test session starts ==============================

============================= 124 passed in 2.35s ==============================
Name                                          Stmts   Miss  Cover   Missing
---------------------------------------------------------------------------
mixpanel/__init__.py                            213     32    85%   82, 87, 92, 100-104, 109-113, 184, 203, 236, 277, 414, 576, 603, 607, 610-613, 616, 619-622, 684, 733, 737, 752-754
mixpanel/flags/__init__.py                        0      0   100%
mixpanel/flags/local_feature_flags.py           250     16    94%   87, 98, 113, 144, 272, 331, 349, 374, 405, 409, 413, 483-484, 516, 521, 524
mixpanel/flags/remote_feature_flags.py          144      4    97%   161, 272, 347, 350
mixpanel/flags/test_local_feature_flags.py      429      1    99%   83
mixpanel/flags/test_remote_feature_flags.py     168      1    99%   199
mixpanel/flags/test_utils.py                     13      0   100%
mixpanel/flags/types.py                          53      0   100%
mixpanel/flags/utils.py                          22      0   100%
test_mixpanel.py                                262      2    99%   26, 549
---------------------------------------------------------------------------
TOTAL                                          1554     56    96%
96% overall coverage — 124 tests all passing.

File	Coverage	Missing Lines
mixpanel/init.py	85%	32 lines (import/alias/serialization paths)
mixpanel/flags/local_feature_flags.py	94%	16 lines
mixpanel/flags/remote_feature_flags.py	97%	4 lines
mixpanel/flags/types.py	100%	—
mixpanel/flags/utils.py	100%	—
Test files	99%	—

@jaredmixpanel jaredmixpanel merged commit ab2ab57 into master Mar 23, 2026
21 checks passed
@jaredmixpanel jaredmixpanel deleted the jared-ruff-linting branch March 23, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants