Tools: Update: Codacy for Python: Code Quality and Static Analysis

Tools: Update: Codacy for Python: Code Quality and Static Analysis

How Codacy analyzes Python code

Pylint in Codacy: what it covers and how to configure it

What Pylint catches

Configuring Pylint through Codacy's Code Patterns

Using a .pylintrc for advanced configuration

Bandit: Python security scanning in Codacy

What Bandit detects in your Python code

Tuning Bandit in Codacy

Prospector and Radon: deeper Python analysis

Prospector

Radon: complexity metrics that matter

Code coverage reporting with pytest

Setting up pytest coverage upload

Coverage for Django projects

Django and Flask: framework-specific patterns Codacy catches

Django patterns

Flask patterns

Quality gates for Python projects

Recommended Python quality gate settings

Enforcing gates on GitHub

Custom rules and configuration in Codacy

1. Code Patterns (dashboard)

2. Tool configuration files in your repository

3. .codacy.yml for global settings

What you cannot do with Codacy custom rules

A modern alternative: CodeAnt AI

Setting up Codacy for a Python project: quick start

Integrating with the broader Python toolchain

What Codacy Python analysis is not

Conclusion

Further Reading

Frequently Asked Questions

Does Codacy support Python?

Which Python tools does Codacy run internally?

Can Codacy detect Django security vulnerabilities?

How does Codacy handle Python code coverage?

Can I configure custom rules for Python in Codacy?

Does Codacy work with Flask projects?

Does Codacy support Python type checking with mypy?

What Python version does Codacy support?

How do I set up Codacy coverage reporting with pytest?

Can Codacy enforce quality gates on Python pull requests?

Is Codacy a good alternative to running Pylint and Bandit directly? Python is one of those languages where automated code quality tools earn their keep almost immediately. The dynamic type system creates bug categories that compilers in other languages catch for free. The flexibility that makes Python productive also makes it easy to write code that is technically correct but brittle, insecure, or painful to maintain at scale. For teams looking to enforce consistent quality standards across Python codebases, Codacy offers a practical entry point - a single platform that aggregates multiple Python analysis tools and presents their results in a unified dashboard. This guide covers everything you need to know about using Codacy specifically for Python: which analysis tools it runs under the hood, how to configure them for your project, how to set up coverage reporting with pytest, what to expect for Django and Flask security scanning, and how to think about custom rules. If you are comparing Codacy against other Python-focused quality platforms, the best code review tools for Python roundup gives you a broader view of the ecosystem. Codacy does not build its own Python analyzer from scratch. Instead, it integrates established open-source tools and orchestrates them in a unified pipeline. When you connect a Python repository, Codacy runs a combination of these analyzers on every pull request and full repository scan: The results from all of these tools are deduplicated, organized by severity and category, and surfaced in Codacy's dashboard alongside metrics like coverage trends, duplication percentages, and quality gate status. From a developer's perspective, you see a single stream of findings on every pull request rather than having to check the output of each tool individually. This aggregation approach is both Codacy's strength and its limitation. The strength is immediate breadth - you get multi-dimensional Python analysis without manually configuring each tool. The limitation is that Codacy's integration of each tool is not as deep as running that tool directly with a fully custom configuration. Teams with specialized Pylint plugin setups or custom Bandit configurations may find that Codacy's wrappers do not expose all the knobs they need. For most Python teams, the trade-off is worth it. Let's dig into what each integrated tool actually covers and how to get the most out of it. Pylint is the most capable Python-specific linter available, and it forms the backbone of Codacy's Python quality analysis. Unlike faster syntactic linters such as Ruff (which is not currently integrated into Codacy), Pylint builds a full abstract syntax tree and performs semantic analysis - which means it catches issues that require understanding what code does, not just how it is written. Within Codacy's Pylint integration, you get analysis across several categories: Errors (E prefix): Issues that are almost certainly bugs. These include accessing attributes on objects that do not have them, using variables before assignment, calling objects that are not callable, and returning values from __init__. These findings should be treated as blocking - any error-level Pylint finding that is not a known false positive should stop a merge. Warnings (W prefix): Patterns that are likely problems but not always wrong. Unused variables and imports, redefining names from outer scope, using * imports, and pointless statements. These are worth reviewing on every PR. Conventions (C prefix): Style and naming issues. Missing docstrings, naming convention violations (class names not in CamelCase, constants not in UPPER_CASE), and line length. Whether to enforce these is a team preference, but consistency matters. Refactoring opportunities (R prefix): Code that works but could be structured better. Too many branches, too many return statements, duplicate code, and unnecessary elif after return. These are quality signals rather than bugs. Design issues (W5xx and beyond): Structural concerns like too many arguments to a function, too many instance attributes, and too many local variables. Codacy uses these to feed its complexity scores alongside Radon's metrics. Codacy exposes Pylint rules through its Code Patterns interface. Navigate to your repository settings, select Code Patterns, and filter by the Pylint engine. From there you can enable or disable individual rules and adjust severity levels. A few practical tuning decisions that most Python teams need to make: Docstring requirements: Pylint's missing-module-docstring, missing-class-docstring, and missing-function-docstring rules are disabled in many teams' configurations because they generate noise on test files and private helper functions. You can disable them globally in Codacy's Code Patterns and rely on documentation standards enforced through code review instead. Import order rules: Pylint has opinions about import order that may conflict with your team's existing isort or Ruff configuration. If you use a separate formatter for import ordering, disable Pylint's import-related convention rules in Codacy to avoid duplicate findings. Naming conventions: Python naming conventions are fairly standard (PEP 8), but projects with legacy code may have inconsistent naming that would trigger hundreds of Pylint convention findings. Configure Codacy to ignore existing issues and only enforce naming rules on new code through the "Issues" tab's baseline feature. For more granular control, you can add a .pylintrc file to your repository root. Codacy respects this file when running Pylint: One important note: Codacy's Code Patterns settings in the dashboard take precedence in some cases. If you find that your .pylintrc settings are not being respected, check whether the relevant rules are overridden in the dashboard settings. Bandit is the standard open-source Python security scanner, and Codacy's integration of it is one of the most useful parts of its Python offering. Bandit checks for common security issues that are specific to Python code - patterns that generic security tools either miss or produce excessive noise on. Within Codacy, Bandit covers these Python security categories: Injection vulnerabilities: Insecure cryptography: Dangerous Python functions: Hardcoded credentials: Insecure network and file operations: Here is a concrete example of what Bandit flags in a Django project: Codacy surfaces all four of these as security findings, categorized by severity (HIGH for the subprocess and pickle issues, MEDIUM for MD5 and the hardcoded password). Not every Bandit finding requires the same response. Codacy lets you adjust Bandit rule severity and enable or disable individual checks through Code Patterns. Some common tuning decisions: B101 (assert used): Bandit flags assert statements because Python's -O flag strips them at runtime. This is worth disabling in test file contexts - test code that uses assert liberally is correct behavior. Configure per-file ignores or disable this check if it generates noise in your test suite. B311 (random module): Bandit flags any use of the random module for security-sensitive contexts. This is high-signal for authentication and token generation code but low-signal for simulation code, data shuffling, and other non-security uses. Consider the context carefully before ignoring this finding. B603 (subprocess without shell): Even subprocess.call() without shell=True gets flagged as a lower-severity warning. This is informational - it means Bandit is noting any subprocess usage, not necessarily flagging a vulnerability. Most teams accept this finding and review it case by case. For Python projects in security-sensitive domains, Codacy's Bandit integration is a solid baseline, but it does not replace the cross-file dataflow analysis that tools like Semgrep and Snyk Code provide. Bandit analyzes each file individually - it cannot follow user input from a Django view through multiple function calls to a dangerous sink in another module. See the Codacy vs Semgrep comparison for more on this distinction. Beyond Pylint and Bandit, Codacy also runs Prospector and Radon to round out its Python analysis. Prospector is a Python code analysis tool that aggregates the output of multiple other tools - including pyflakes, pycodestyle, dodgy, and optionally mypy and vulture. Within Codacy, Prospector adds checks that complement Pylint rather than duplicate it: You can configure Prospector through a .prospector.yaml file in your repository: Radon is where Codacy gets its complexity and maintainability data for Python. It computes: Cyclomatic Complexity (CC): The number of linearly independent paths through a function. Radon grades functions A (CC 1-5, simple), B (CC 6-10, more complex), C (CC 11-15, complex), D (CC 16-20, very complex), E (CC 21-25, dangerously complex), and F (CC 26+, too complex). Codacy's complexity thresholds use these grades to flag functions that need refactoring. Maintainability Index (MI): A composite metric based on Halstead volume, cyclomatic complexity, and lines of code. Scores range from 0 to 100, where higher is more maintainable. Codacy uses MI to identify modules that have accumulated enough complexity to warrant a refactoring investment. Lines of Code (LOC) metrics: Raw and logical LOC at the module and function level. Long functions are a proxy for complexity even when cyclomatic complexity stays manageable. These metrics feed into Codacy's issue tracking and dashboards. A function with F-grade cyclomatic complexity shows up as a high-severity finding. A module with an MI below 20 is flagged for attention in the repository overview. The practical value of these metrics is in trend tracking. A single complex function is a finding to address. A repository where the average MI is declining over six months is a systemic problem that warrants a broader refactoring strategy. Coverage tracking is one of the most useful features Codacy provides for Python teams. Connecting coverage data to your pull request workflow means developers can see immediately whether their changes reduce overall coverage or leave new code untested. The setup process involves three steps: generating coverage data in your CI pipeline, formatting it in a way Codacy accepts, and uploading it with the Codacy Coverage Reporter. Step 1: Configure pytest with coverage Install pytest-cov and add coverage configuration to your pyproject.toml or setup.cfg: Running pytest with these settings generates coverage.xml in the standard Cobertura XML format that Codacy accepts. Step 2: Upload coverage in GitHub Actions Step 3: Configure coverage quality gates In your repository settings in Codacy, configure the coverage quality gate thresholds: The new code coverage gate is the most practical for most teams. Enforcing coverage on new code without requiring the entire legacy codebase to meet a threshold avoids the situation where adding any new feature fails the quality gate because of existing uncovered code. Django projects require some additional configuration to get meaningful coverage numbers. The test runner needs to be configured to use the Django test settings, and coverage needs to exclude migrations, admin registrations, and other generated or configuration code: For Django, you also need to set the DJANGO_SETTINGS_MODULE environment variable in your test run: Python web frameworks have security and quality concerns that are framework-specific. Codacy's Bandit integration handles the security side reasonably well for both Django and Flask. SQL injection via ORM bypass: Django's ORM is safe by default, but developers bypass it for complex queries. Codacy flags these patterns: Template safety: Codacy flags uses of mark_safe() with variables that could carry user input, and direct use of |safe filters in template contexts that Bandit can statically detect. Settings security: Bandit detects hardcoded SECRET_KEY values, DEBUG = True in non-development settings files, and ALLOWED_HOSTS = ['*']. Insecure deserialization: Django's session backend can be configured with insecure settings. Bandit catches cases where the session serializer is set to PickleSerializer, which is vulnerable to deserialization attacks. For deeper Django security analysis - especially cross-file taint tracking from view parameters through service layers to database calls - Codacy's Bandit integration is a good baseline but not comprehensive. Teams with strict Django security requirements benefit from adding Semgrep with the p/django ruleset in CI alongside Codacy. Flask applications have a different risk profile because Flask does not include security batteries like Django does. Codacy's Bandit integration catches: Flask's missing CSRF protection by default is not something Bandit catches automatically (since it requires understanding what extensions are installed and configured). For Flask CSRF analysis, you need either a custom Semgrep rule or a manual audit process. Quality gates are where Codacy's Python analysis delivers concrete workflow value. Rather than just reporting issues, quality gates block pull requests from merging when they fail to meet defined thresholds. For a mid-size Python team (5-20 developers), a practical starting configuration looks like this: The most important principle for quality gates is starting from where you are. If your current test coverage is 45%, setting a gate at 80% will block every PR and create developer frustration. Set the gate at your current coverage level, then increment it by 2-5% every sprint as you improve. Codacy posts a status check to every pull request that maps to your quality gate configuration. In your GitHub repository settings under "Branches", add a branch protection rule that requires the Codacy status check to pass before merging. This makes quality gate enforcement automatic - developers cannot merge failing PRs even with admin access. Codacy's "custom rules" capability is primarily about configuring the tools it runs rather than authoring new rules from scratch. There are three levels of customization: The primary configuration interface. For each integrated tool (Pylint, Bandit, Prospector), you can enable or disable individual rules and adjust their severity. This is the easiest way to tune Codacy for your Python project. Navigate to Repository Settings - Code Patterns - select the tool - toggle rules on/off or change severity. Changes apply to all future analyses. As mentioned earlier, Codacy respects tool-specific configuration files in your repository root: These files give you access to configuration options that Codacy's dashboard does not expose. If you need to configure Pylint plugins (pylint-django, pylint-celery) or adjust Bandit test severity by category, configuration files are the right approach. The .codacy.yml file in your repository root controls which tools run and which paths are excluded: Excluding test files and migrations from analysis is particularly important for Python projects. Test code intentionally violates some quality rules (using assert, testing private methods, long setup functions), and Django migrations are auto-generated and verbose by design. Running Pylint and Bandit over them generates noise that dilutes the signal from your actual application code. Codacy does not support authoring entirely new static analysis rules using pattern syntax (the way Semgrep does). If you need to enforce a project-specific convention - "all Django views must have a permission decorator", "all API functions must have type annotations", "never use pickle in this codebase" - you need to either write a custom Pylint plugin (which Codacy may or may not support depending on how it invokes Pylint) or run Semgrep with custom rules alongside Codacy in your CI pipeline. For teams with sophisticated custom rule requirements, Sourcery or DeepSource may provide a better fit. Sourcery allows custom coding guidelines in natural language that its AI interpreter applies during reviews. DeepSource supports custom analyzers and has a more flexible rule authoring story than Codacy. Since the tools frontmatter lists DeepSource and Sourcery, it is worth being specific about when to choose each one for Python work. Codacy is the right choice when you need broad coverage at the lowest price point - SAST, SCA, secrets detection, coverage tracking, and quality gates in one platform at $15/user/month. Its Python analysis is solid but not specialized. It will catch the issues that matter most without requiring significant tuning on a greenfield Python project. DeepSource invests more in Python-specific analysis quality. Its sub-5% false positive rate claim is meaningful for Python teams that have struggled with Pylint noise on dynamic codebases. DeepSource's Autofix feature generates correct Python fixes that you can apply directly from the PR comment - a productivity benefit that Codacy does not offer. The trade-off is price ($24/user/month versus Codacy's $15) and narrower language support. See the DeepSource Python guide for a detailed look at DeepSource's Python-specific capabilities. Sourcery is Python-first by design and remains the best option for teams that want AI-powered refactoring suggestions specifically for Python idioms. Where Codacy and DeepSource flag issues, Sourcery explains the Pythonic alternative. Its understanding of list comprehensions, generator expressions, context managers, and Python's standard library patterns is genuinely superior to what multi-language platforms provide. Sourcery's free tier is limited to open-source, and its multi-language support is thinner than Codacy's. If you find Codacy's Python analysis useful but want deeper AI-powered insights alongside the security scanning, CodeAnt AI ($24-40/user/month) is worth evaluating. It is a Y Combinator-backed code health platform that combines AI PR reviews, SAST, secrets detection, IaC security, and DORA metrics in a single platform. Where Codacy's AI reviewer is one feature among many, CodeAnt AI's AI analysis is the core product - which means the PR-level review quality for Python code tends to be more nuanced and context-aware. For teams that want both comprehensive security scanning and sophisticated AI review in one tool, CodeAnt AI is one of the more compelling newer entrants to the market. See the broader Codacy alternatives guide for a full comparison. If you are starting fresh with Codacy on a Python project, here is a practical setup sequence that avoids the common pitfalls: 1. Connect your repository and run the initial analysis Sign up at codacy.com, connect your GitHub or GitLab account, and add your Python repository. Codacy runs its first analysis automatically. Do not panic at the number of findings - every new repository generates a backlog. 2. Configure the baseline In your repository's Issues tab, use "Ignore existing issues" to suppress everything in the current state of the main branch. This ensures that quality gates and PR comments only flag new issues going forward, not the accumulated backlog. This is the single most important step for teams adopting Codacy on existing codebases. 3. Tune Code Patterns Go through the Pylint, Bandit, and Prospector rules in Code Patterns. Disable rules that are not relevant to your project (docstring requirements for private helpers, complexity rules for intentionally verbose migration files) and verify that security rules are enabled at the right severity. Exclude test directories, migrations, generated code, and any vendor directories from analysis. This immediately reduces noise and focuses Codacy's output on code you actually maintain. Configure pytest-cov, add the Codacy Coverage Reporter to your CI workflow, and set an initial coverage gate that matches your current coverage level. Start with gates that block HIGH-severity security findings and prevent coverage from dropping more than 5% in any single PR. Add more gates as your team gets comfortable with the workflow. For a detailed walkthrough of the setup process, the how to setup Codacy guide covers every step from account creation to quality gate configuration. Codacy is most effective when it is one layer in a broader Python quality stack rather than the only tool running. The recommended combination for teams already using Codacy: This layered approach means developers get fast feedback from Ruff before they even push, type errors caught by mypy before a PR is reviewed, and Codacy's multi-dimensional analysis surfaced as PR comments and status checks. For teams comparing Codacy's GitHub-specific behavior to running these tools directly in GitHub Actions, the Codacy GitHub integration guide goes into detail on how Codacy's status checks, inline comments, and PR summary comments work in practice. It is worth being direct about what Codacy cannot do for Python, so you set the right expectations: No cross-file taint analysis: Codacy's Bandit integration analyzes individual files. It cannot trace SQL injection risks from a Django view parameter through three layers of service classes to a raw database query in a separate module. For that level of security analysis, Semgrep or Snyk Code is the right tool. No mypy integration: Codacy does not run mypy. If type safety is a priority (and for most production Python codebases, it should be), you need to run mypy in CI separately. No autofix: Codacy identifies issues but does not generate or apply fixes. DeepSource's Autofix feature does this for Python, and it is a meaningful productivity difference for teams with large issue backlogs. No Ruff integration: Ruff has largely replaced flake8 and similar linters for Python in 2026, but Codacy has not yet integrated it. Pylint and Prospector cover much of the same ground, but Ruff's speed and rule coverage are not available within Codacy's analysis pipeline. Codacy provides solid Python code quality and security analysis by integrating Pylint, Bandit, Prospector, and Radon into a unified platform with dashboards, quality gates, and PR-level feedback. For Python teams that want a single tool to cover quality analysis, security scanning, and coverage tracking without assembling and maintaining a multi-tool CI configuration, Codacy delivers that consolidation at a price point ($15/user/month) that is competitive for what it includes. The sweet spot for Codacy on Python is teams of 5 to 30 developers working on Django, Flask, or FastAPI projects who want to enforce consistent quality standards without becoming static analysis experts. The setup is fast, the PR integration works smoothly across GitHub and GitLab, and the quality gate system makes quality enforcement automatic rather than depending on reviewer vigilance. Where Codacy falls short for Python specifically is in cross-file security analysis, mypy integration, and the depth of Python-specific refactoring suggestions that a purpose-built Python tool like Sourcery provides. For teams where those capabilities are critical, supplementing Codacy with Semgrep for security and running mypy in CI covers most of the gaps. For a broader view of Codacy's overall capabilities beyond Python, the full Codacy review covers pricing, AI review features, and comparisons with SonarQube and DeepSource. And if you are still evaluating whether Codacy is the right platform for your Python team, the Codacy alternatives guide covers ten options worth considering alongside it. Yes. Codacy supports Python through a suite of integrated tools including Pylint, Bandit, Prospector, and Radon. It provides code quality analysis, security scanning, complexity metrics, and coverage tracking for Python projects on GitHub, GitLab, and Bitbucket. Codacy runs Pylint for code quality and style, Bandit for security vulnerability detection, Prospector for additional Python-specific checks, and Radon for cyclomatic complexity and maintainability index calculations. All results are unified in Codacy's dashboard. Yes. Codacy's integrated Bandit and Semgrep analyzers detect Django-specific security issues including raw SQL injection via objects.raw(), missing CSRF middleware, use of mark_safe() with user input, insecure settings like DEBUG = True, and weak secret key configurations. Codacy integrates with pytest via the Codacy Coverage Reporter. You run pytest with coverage, generate an LCOV or Cobertura report, and upload it to Codacy using the coverage reporter CLI. Codacy then tracks coverage trends and shows coverage delta on every pull request. Yes. Codacy lets you enable or disable individual rules from its integrated analyzers (Pylint, Bandit, Prospector) through the Code Patterns interface. You can also add a .codacy.yml file to your repository to customize which tools run, file exclusions, and per-analyzer settings. Yes. Codacy analyzes Flask applications with security rules that detect debug mode in production, weak secret key handling, missing CSRF protection, and Jinja2 template injection risks. Bandit rules specific to Flask patterns run automatically as part of Codacy's Python analysis. Codacy runs multiple Python tools (Pylint, Bandit, Prospector, Radon) and provides broad coverage at $15/user/month. DeepSource focuses on signal quality with a sub-5% false positive rate, deeper Python-specific checks, and better autofix capabilities at $24/user/month. Choose Codacy for breadth at lower cost; choose DeepSource for precision. Codacy does not run mypy as an integrated analyzer. For type checking, you should run mypy in your CI pipeline separately and optionally upload its results via the Codacy CLI. Codacy's own Python analysis covers code quality, security, and complexity rather than formal type verification. Codacy supports Python 3.8 through 3.12 for its integrated analyzers. The underlying tools (Pylint, Bandit, Prospector) are updated regularly to support newer Python syntax including pattern matching (match/case), the walrus operator (:=), and the newer type alias syntax. Install pytest-cov in your project, run pytest --cov=src --cov-report=xml to generate a coverage XML report, then run the Codacy Coverage Reporter using CODACY_PROJECT_TOKEN from your repository settings to upload the report. Add this as a step in your GitHub Actions or GitLab CI workflow. Yes. Codacy's quality gates let you block pull requests that introduce new issues, drop below a coverage threshold, or exceed complexity limits. You configure these thresholds in the repository settings, and Codacy posts a status check to GitHub or GitLab that must pass before merging is allowed. For teams that want a unified platform with dashboards, trend tracking, PR integration, and quality gates, Codacy provides significant value over running Pylint and Bandit directly. If you only need linting and security scanning in CI without dashboards or team features, running those tools directly in GitHub Actions is simpler and free. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ [MASTER] # Ignore generated files and migrations ignore=migrations,generated [MESSAGES CONTROL] # Disable checks handled by other tools -weight: 500;">disable= C0114, # missing-module-docstring (review-enforced) C0115, # missing-class-docstring C0116, # missing-function-docstring W0611, # unused-import (handled by Ruff in pre-commit) R0903, # too-few-public-methods (noisy on data classes) [FORMAT] max-line-length=120 [DESIGN] max-args=7 max-locals=15 max-returns=6 max-branches=12 max-statements=50 [MASTER] # Ignore generated files and migrations ignore=migrations,generated [MESSAGES CONTROL] # Disable checks handled by other tools -weight: 500;">disable= C0114, # missing-module-docstring (review-enforced) C0115, # missing-class-docstring C0116, # missing-function-docstring W0611, # unused-import (handled by Ruff in pre-commit) R0903, # too-few-public-methods (noisy on data classes) [FORMAT] max-line-length=120 [DESIGN] max-args=7 max-locals=15 max-returns=6 max-branches=12 max-statements=50 [MASTER] # Ignore generated files and migrations ignore=migrations,generated [MESSAGES CONTROL] # Disable checks handled by other tools -weight: 500;">disable= C0114, # missing-module-docstring (review-enforced) C0115, # missing-class-docstring C0116, # missing-function-docstring W0611, # unused-import (handled by Ruff in pre-commit) R0903, # too-few-public-methods (noisy on data classes) [FORMAT] max-line-length=120 [DESIGN] max-args=7 max-locals=15 max-returns=6 max-branches=12 max-statements=50 # Bandit B303: use of insecure MD5 hash def hash_password(password: str) -> str: return hashlib.md5(password.encode()).hexdigest() # Bandit B602: subprocess call with shell=True def run_command(user_input: str) -> str: result = subprocess.check_output(user_input, shell=True) return result.decode() # Bandit B301: pickle deserialization (unsafe with untrusted data) def load_model(data: bytes): return pickle.loads(data) # Bandit B105: hardcoded password string DATABASE_PASSWORD = "prod_secret_2024" # Bandit B303: use of insecure MD5 hash def hash_password(password: str) -> str: return hashlib.md5(password.encode()).hexdigest() # Bandit B602: subprocess call with shell=True def run_command(user_input: str) -> str: result = subprocess.check_output(user_input, shell=True) return result.decode() # Bandit B301: pickle deserialization (unsafe with untrusted data) def load_model(data: bytes): return pickle.loads(data) # Bandit B105: hardcoded password string DATABASE_PASSWORD = "prod_secret_2024" # Bandit B303: use of insecure MD5 hash def hash_password(password: str) -> str: return hashlib.md5(password.encode()).hexdigest() # Bandit B602: subprocess call with shell=True def run_command(user_input: str) -> str: result = subprocess.check_output(user_input, shell=True) return result.decode() # Bandit B301: pickle deserialization (unsafe with untrusted data) def load_model(data: bytes): return pickle.loads(data) # Bandit B105: hardcoded password string DATABASE_PASSWORD = "prod_secret_2024" # .prospector.yaml strictness: medium doc-warnings: false test-warnings: false ignore-paths: - migrations - tests - docs pep8: options: max-line-length: 120 mccabe: options: max-complexity: 10 # .prospector.yaml strictness: medium doc-warnings: false test-warnings: false ignore-paths: - migrations - tests - docs pep8: options: max-line-length: 120 mccabe: options: max-complexity: 10 # .prospector.yaml strictness: medium doc-warnings: false test-warnings: false ignore-paths: - migrations - tests - docs pep8: options: max-line-length: 120 mccabe: options: max-complexity: 10 # pyproject.toml [tool.pytest.ini_options] addopts = "--cov=src --cov-report=xml --cov-report=term-missing" testpaths = ["tests"] [tool.coverage.run] source = ["src"] omit = [ "src/*/migrations/*", "src/*/tests/*", "src/manage.py", "src/*/conftest.py", ] [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "if TYPE_CHECKING:", "raise NotImplementedError", "if __name__ == .__main__.:", ] # pyproject.toml [tool.pytest.ini_options] addopts = "--cov=src --cov-report=xml --cov-report=term-missing" testpaths = ["tests"] [tool.coverage.run] source = ["src"] omit = [ "src/*/migrations/*", "src/*/tests/*", "src/manage.py", "src/*/conftest.py", ] [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "if TYPE_CHECKING:", "raise NotImplementedError", "if __name__ == .__main__.:", ] # pyproject.toml [tool.pytest.ini_options] addopts = "--cov=src --cov-report=xml --cov-report=term-missing" testpaths = ["tests"] [tool.coverage.run] source = ["src"] omit = [ "src/*/migrations/*", "src/*/tests/*", "src/manage.py", "src/*/conftest.py", ] [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "if TYPE_CHECKING:", "raise NotImplementedError", "if __name__ == .__main__.:", ] name: Python Tests and Coverage on: push: branches: [main] pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install dependencies run: -weight: 500;">pip -weight: 500;">install -r requirements.txt pytest pytest-cov - name: Run tests with coverage run: pytest - name: Upload coverage to Codacy uses: codacy/codacy-coverage-reporter-action@v1 with: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} coverage-reports: coverage.xml name: Python Tests and Coverage on: push: branches: [main] pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install dependencies run: -weight: 500;">pip -weight: 500;">install -r requirements.txt pytest pytest-cov - name: Run tests with coverage run: pytest - name: Upload coverage to Codacy uses: codacy/codacy-coverage-reporter-action@v1 with: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} coverage-reports: coverage.xml name: Python Tests and Coverage on: push: branches: [main] pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install dependencies run: -weight: 500;">pip -weight: 500;">install -r requirements.txt pytest pytest-cov - name: Run tests with coverage run: pytest - name: Upload coverage to Codacy uses: codacy/codacy-coverage-reporter-action@v1 with: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} coverage-reports: coverage.xml # pyproject.toml - Django-specific coverage settings [tool.coverage.run] source = ["apps"] omit = [ "*/migrations/*", "*/admin.py", # unless you test admin customizations "*/apps.py", "manage.py", "conftest.py", "*/settings/*", ] [tool.coverage.report] fail_under = 80 # pyproject.toml - Django-specific coverage settings [tool.coverage.run] source = ["apps"] omit = [ "*/migrations/*", "*/admin.py", # unless you test admin customizations "*/apps.py", "manage.py", "conftest.py", "*/settings/*", ] [tool.coverage.report] fail_under = 80 # pyproject.toml - Django-specific coverage settings [tool.coverage.run] source = ["apps"] omit = [ "*/migrations/*", "*/admin.py", # unless you test admin customizations "*/apps.py", "manage.py", "conftest.py", "*/settings/*", ] [tool.coverage.report] fail_under = 80 - name: Run Django tests with coverage env: DJANGO_SETTINGS_MODULE: myproject.settings.test run: pytest --ds=myproject.settings.test - name: Run Django tests with coverage env: DJANGO_SETTINGS_MODULE: myproject.settings.test run: pytest --ds=myproject.settings.test - name: Run Django tests with coverage env: DJANGO_SETTINGS_MODULE: myproject.settings.test run: pytest --ds=myproject.settings.test # Bandit flags: raw SQL with potential injection User.objects.raw(f"SELECT * FROM users WHERE name = '{name}'") # Safe: parameterized query User.objects.raw("SELECT * FROM users WHERE name = %s", [name]) # Bandit flags: extra() with user input User.objects.extra(where=[f"name = '{name}'"]) # Bandit flags: raw SQL with potential injection User.objects.raw(f"SELECT * FROM users WHERE name = '{name}'") # Safe: parameterized query User.objects.raw("SELECT * FROM users WHERE name = %s", [name]) # Bandit flags: extra() with user input User.objects.extra(where=[f"name = '{name}'"]) # Bandit flags: raw SQL with potential injection User.objects.raw(f"SELECT * FROM users WHERE name = '{name}'") # Safe: parameterized query User.objects.raw("SELECT * FROM users WHERE name = %s", [name]) # Bandit flags: extra() with user input User.objects.extra(where=[f"name = '{name}'"]) from flask import Flask app = Flask(__name__) # Bandit B201: Flask debug mode in production app.run(debug=True) # Bandit B105: hardcoded secret key app.secret_key = "dev_key_replace_me" # Bandit B608: SQL injection (with Flask-SQLAlchemy bypassed) def get_user(username): query = f"SELECT * FROM users WHERE username = '{username}'" return db.engine.execute(query) from flask import Flask app = Flask(__name__) # Bandit B201: Flask debug mode in production app.run(debug=True) # Bandit B105: hardcoded secret key app.secret_key = "dev_key_replace_me" # Bandit B608: SQL injection (with Flask-SQLAlchemy bypassed) def get_user(username): query = f"SELECT * FROM users WHERE username = '{username}'" return db.engine.execute(query) from flask import Flask app = Flask(__name__) # Bandit B201: Flask debug mode in production app.run(debug=True) # Bandit B105: hardcoded secret key app.secret_key = "dev_key_replace_me" # Bandit B608: SQL injection (with Flask-SQLAlchemy bypassed) def get_user(username): query = f"SELECT * FROM users WHERE username = '{username}'" return db.engine.execute(query) # .codacy.yml --- engines: pylint: enabled: true bandit: enabled: true prospector: enabled: true radon: enabled: true exclude_paths: - "migrations/**" - "tests/**" - "docs/**" - "*.pyc" - "setup.py" # .codacy.yml --- engines: pylint: enabled: true bandit: enabled: true prospector: enabled: true radon: enabled: true exclude_paths: - "migrations/**" - "tests/**" - "docs/**" - "*.pyc" - "setup.py" # .codacy.yml --- engines: pylint: enabled: true bandit: enabled: true prospector: enabled: true radon: enabled: true exclude_paths: - "migrations/**" - "tests/**" - "docs/**" - "*.pyc" - "setup.py" - Pylint - deep static analysis covering code quality, style violations, potential bugs, and refactoring opportunities - Bandit - Python-specific security vulnerability detection - Prospector - a meta-tool that runs multiple Python analysis tools and aggregates their output - Radon - complexity metrics including cyclomatic complexity, cognitive complexity, and Maintainability Index (MI) - SQL injection via string formatting in raw queries - Shell injection via subprocess.call(shell=True) or os.system() - Command injection through user-controlled arguments to shell commands - Use of weak hash algorithms: MD5, SHA1 for security purposes - Use of insecure random number generators (random module instead of secrets) - Hardcoded cryptographic keys or seeds - eval() and exec() with user-controlled input - pickle.loads() with untrusted data (a common ML/data science vulnerability) - yaml.load() without Loader=yaml.SafeLoader - subprocess calls with shell=True - Hardcoded passwords, API keys, and tokens in source code - Database connection strings with credentials embedded - HTTPS certificate verification disabled with verify=False - Insecure use of tempfile functions - Overly permissive file permissions - pyflakes integration: catches undefined names, unused imports, and redefined unused names with fewer false positives than Pylint's equivalent checks - dodgy checks: flags obvious credential leaks - not as comprehensive as Bandit's secret detection, but catches the clearest cases - Mccabe complexity: an alternative complexity metric to Radon that measures cyclomatic complexity at the function level - Minimum coverage threshold: Block PRs where overall coverage drops below (e.g., 80%) - Coverage delta: Block PRs that reduce coverage by more than a specified percentage (e.g., 5%) - New code coverage: Require that new code introduced in the PR meets a minimum threshold (e.g., 90%) - Block if new issues introduced with severity HIGH or CRITICAL: yes (no new high-severity issues allowed) - Block if new issues introduced with severity MEDIUM: optional (warn but do not block for medium-severity) - Minimum overall coverage: 75% (adjust to your current baseline, ratchet up over time) - New code coverage: 80% (new code should be more thoroughly tested than legacy) - Coverage delta: block if coverage drops more than 5% in a single PR - Codacy does not provide a direct complexity gate at the PR level, but it tracks complexity trends in the dashboard. Use the issue system to flag HIGH complexity (F-grade Radon functions) and treat them as issues to resolve. - Block if new duplication exceeds 5% of the PR changeset - .pylintrc or pyproject.toml [tool.pylint] for Pylint - .bandit or pyproject.toml [tool.bandit] for Bandit - .prospector.yaml for Prospector - Ruff in pre-commit hooks for instant linting and formatting feedback (Codacy does not integrate Ruff, so running it separately in pre-commit gives developers fast feedback before CI runs) - mypy in CI for type checking (Codacy does not run mypy; add it as a separate CI step) - Codacy in CI for integrated quality analysis, security scanning, coverage tracking, and PR -weight: 500;">status checks - Semgrep optionally for advanced security rules, especially for Django/Flask teams that need cross-file taint analysis - Will AI Replace Code Reviewers? What the Data Actually Shows - Best AI Code Review Tools in 2026 - Expert Picks - Best AI Code Review Tools for Pull Requests in 2026 - 7 Best CodeRabbit Alternatives for AI Code Review in 2026 - CodeRabbit Pricing in 2026: Free Tier, Pro Plans, and Enterprise Costs