Skip to main content

PySpark antipattern linter for CI/CD pipelines

Project description

PyPI - Version Release PyPI - Python Version GitHub Issues or Pull Requests Documentation

pyspark-antipattern

A fast, opinionated PySpark linter that challenges your code against antipattern rules — written in Rust, installable as a Python package, and designed to run in CI/CD pipelines.

This linter is intentionally strict. It will flag patterns that are technically valid Python but known to cause performance, scalability, or maintainability problems in PySpark. Every violation is a conversation starter, not necessarily a hard blocker — it is up to you to decide whether to fix it, downgrade it to a warning, or suppress it for a specific line. The goal is to make the trade-offs visible before they become production incidents.


Why this exists

PySpark is easy to misuse. .collect() on a 10 GB DataFrame, .withColumn() called in a loop, UDFs where built-in functions exist — these patterns work fine locally and silently destroy performance at scale. This tool catches them early, at commit time, before they reach your cluster.


Installation

pip install pyspark-antipattern

Usage

Check a single file:

pyspark-antipattern check pipeline.py

Check an entire directory recursively:

pyspark-antipattern check src/

Use a custom config location:

pyspark-antipattern check src/ --config path/to/pyproject.toml

Exit codes

  • 0 — no errors (warnings are allowed)
  • 1 — one or more error-level violations found

CLI output

Default output — violations only:

Default behavior

Each violation line includes a colored severity badge — [HIGH] in red, [MEDIUM] in yellow, [LOW] in green — immediately after the rule ID:

error[D001][HIGH]: Avoid using collect()
  --> pipeline.py:42:10

Filter by your cluster's PySpark version to suppress rules for newer APIs:

pyspark-antipattern check src/ --pyspark-version=3.3  # suppress rules requiring 3.4+

Filter by severity directly from the CLI:

pyspark-antipattern check src/ --severity=high    # only HIGH violations
pyspark-antipattern check src/ --severity=medium  # MEDIUM and HIGH

With show_information = true — inline explanation for each rule:

Show information

With show_best_practice = true — best-practice guidance for each rule:

Show best practice


Rules

Full documentation is available at https://skanderboudawara.github.io/pyspark-antipattern/.

Rules are organized by category in the docs/rules/ folder. Each rule has its own markdown file with a full explanation, best-practice guidance, and a severity badge indicating its performance impact.

Category Folder Focus
ARR — Array docs/rules/arr/ Array function antipatterns
D — Driver docs/rules/driver/ Actions that pull data to the driver node
F — Format docs/rules/format/ Code style and DataFrame API misuse
L — Looping docs/rules/looping/ DataFrame operations inside loops
P — Pandas docs/rules/pandas/ Pandas interop pitfalls
PERF — Performance docs/rules/performance/ Runtime performance antipatterns
S — Shuffle docs/rules/shuffle/ Joins, partitioning, and data movement
U — UDF docs/rules/udf/ User-defined functions and their alternatives

Each rule carries a severity reflecting its performance impact:

Severity Meaning
🔴 HIGH Major performance impact — OOM risk, full scans, shuffle explosion
🟡 MEDIUM Moderate performance impact — avoidable overhead at scale
🟢 LOW Minor impact — style, API correctness, small inefficiencies

Configuration

Add a [tool.pyspark-antipattern] section to your project's pyproject.toml:

[tool.pyspark-antipattern]

# Show only these rules — everything else is silenced (default: all active)
# select = ["D001", "S"]

# Cluster PySpark version — silences rules requiring a newer version (default: all)
# pyspark_version = "3.3"     # suppress rules that require PySpark 3.4+

# Downgrade these rules from error to warning (exit code stays 0)
warn = ["F008", "F011"]

# Completely silence these rules — no output, no exit code impact
# Accepts exact rule IDs or single-letter group prefixes
ignore = ["S004"]                # silence one rule
# ignore = ["F"]                 # silence all F rules
# ignore = ["S", "L", "D001"]    # silence all S and L rules

# Only report violations at or above this performance-impact level (default: all)
# severity = "medium"            # show only MEDIUM and HIGH violations
# severity = "high"              # show only HIGH violations

# Show inline explanation for each rule that fired (default: false)
show_information = false

# Show best-practice guidance for each rule that fired (default: false)
show_best_practice = false

# PERF003: fire when more than N shuffle ops occur without a checkpoint (default: 9)
max_shuffle_operations = 9

# S004: flag when the weighted count of .distinct() calls exceeds this (default: 5)
distinct_threshold = 5

# S008: flag when the weighted count of explode() calls exceeds this (default: 3)
explode_threshold = 3

# L001/L002/L003: flag for-loops where range(N) > threshold;
#                 while-loops always assume 99 iterations (default: 10)
loop_threshold = 10

# Directories to skip during recursive scanning (default: common build/venv dirs)
# exclude_dirs = ["my_generated_code", "vendor"]

Suppressing a specific line

Add a # noqa: pap: RULE_ID comment to suppress one or more rules on that line:

result = df.collect()  # noqa: pap: D001
bad_join = df.crossJoin(other)  # noqa: pap: S010, S002

CI/CD integration

GitHub Actions

- name: Lint PySpark code
  run: |
    pip install pyspark-antipattern
    pyspark-antipattern check src/

The job fails automatically if any error-level rule fires. Warnings are reported but do not block the pipeline.

Pre-commit hook

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: pyspark-antipattern
        name: PySpark antipattern linter
        entry: pyspark-antipattern check
        language: system
        types: [python]
        pass_filenames: false
        args: ["src/"]

A word on strictness

This linter will challenge code that your team may have written deliberately and knowingly. That is by design.

Each violation is not a verdict — it is a question: "Did you mean to do this, and do you understand the trade-off?" If the answer is yes, suppress the rule on that line or downgrade it to a warning in your config. If the answer is no, you just avoided a production issue.

The strictest setup is the default: every rule is a hard error. Relax only what you have a documented reason to relax.


Author

Skander Boudawaraskander.education@proton.me

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pyspark_antipattern-0.3.0-py3-none-win_amd64.whl (1.5 MB view details)

Uploaded Python 3Windows x86-64

pyspark_antipattern-0.3.0-py3-none-manylinux_2_28_aarch64.whl (1.5 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ARM64

pyspark_antipattern-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.6 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

pyspark_antipattern-0.3.0-py3-none-macosx_11_0_arm64.whl (1.4 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

pyspark_antipattern-0.3.0-py3-none-macosx_10_12_x86_64.whl (1.5 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file pyspark_antipattern-0.3.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: pyspark_antipattern-0.3.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 1.5 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pyspark_antipattern-0.3.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 76ab3793ef0df27fca38e20461e78587c3720da4a39251fddd97cef102144ebd
MD5 a6defb8d3b3a15926976501fec2ac2ab
BLAKE2b-256 be1cb8771e8b29c0e10c959a77b8e8997e9cf20f68f7acd03fe4d1f6de415972

See more details on using hashes here.

File details

Details for the file pyspark_antipattern-0.3.0-py3-none-manylinux_2_28_aarch64.whl.

File metadata

  • Download URL: pyspark_antipattern-0.3.0-py3-none-manylinux_2_28_aarch64.whl
  • Upload date:
  • Size: 1.5 MB
  • Tags: Python 3, manylinux: glibc 2.28+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pyspark_antipattern-0.3.0-py3-none-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 f95ee706195e8fbf54a86ddb67a5059a7a00c5b01b82bae5e4a9cb6b9f930673
MD5 5ac7f4980afd0acafcf7d738608855f7
BLAKE2b-256 e1f9cab0a1b8fa9426cdaf748f0177c6c5099a9782935389ef8b0e31d9a88271

See more details on using hashes here.

File details

Details for the file pyspark_antipattern-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

  • Download URL: pyspark_antipattern-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
  • Upload date:
  • Size: 1.6 MB
  • Tags: Python 3, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pyspark_antipattern-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7631859af28dbe03682e730d2570971de4399e56fef90fb60105ac280748bb30
MD5 62e322f95fafcb799ccbf4a25a7ee39f
BLAKE2b-256 ec44111e962732fe3a43f6e68ff65b423277afa5c7731ddf01ed8132449d3e44

See more details on using hashes here.

File details

Details for the file pyspark_antipattern-0.3.0-py3-none-macosx_11_0_arm64.whl.

File metadata

  • Download URL: pyspark_antipattern-0.3.0-py3-none-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 1.4 MB
  • Tags: Python 3, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pyspark_antipattern-0.3.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 522fa67bfd1510eb40454354a1deb99e5e65b01f01cfffd2b31cb59d47ba54c9
MD5 689b15ca3b8ff90195662a255571aa4a
BLAKE2b-256 8080cb5ce801f10453d1ef626c19321ba7599c7dbfc045a27010dd045114868e

See more details on using hashes here.

File details

Details for the file pyspark_antipattern-0.3.0-py3-none-macosx_10_12_x86_64.whl.

File metadata

  • Download URL: pyspark_antipattern-0.3.0-py3-none-macosx_10_12_x86_64.whl
  • Upload date:
  • Size: 1.5 MB
  • Tags: Python 3, macOS 10.12+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pyspark_antipattern-0.3.0-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 113a7e242612bb9e84fafb155d45041343a5aabdecb823fa943c55d1bc553de3
MD5 e9f1b8e5746317e7aca15a46fea4d64d
BLAKE2b-256 13ddf1e1e0bdf90f96a0391ac6d58736010ef5963dde957cf8aebee2d78fbd40

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page