This is the full developer documentation for Sourcery # Sourcery docs > AI code review and security scanning for GitHub and GitLab. Sourcery reviews every pull request and scans your repos for security issues. It runs on GitHub, GitLab, GitHub Enterprise Server, and self-hosted GitLab. [Code reviews ](/reviews/)Inline comments, summaries, and a status check on every pull request. [Security scanning ](/security/)Secrets, SAST, infrastructure-as-code, dependencies, and licenses in one scan. [IDE ](/ide/)Review and apply suggestions without leaving your editor. [API ](/api/)Pull security findings into your own tools and pipelines. ## New to Sourcery [Section titled “New to Sourcery”](#new-to-sourcery) Start with the [quickstart for your platform](/start/) to install Sourcery and run your first review in a few minutes. ## How these docs are organized [Section titled “How these docs are organized”](#how-these-docs-are-organized) * [Get started](/start/) – install Sourcery and run your first review * [Code reviews](/reviews/) – what Sourcery posts on every pull request and how to tune it * [Security scanning](/security/) – the scanner stack, findings workflow, and dashboards * [IDE](/ide/) – review your code in VS Code, Cursor, Windsurf, and JetBrains * [Workspace admin](/admin/) – plans, billing, seats, and integrations like Jira * [API](/api/) – authentication and the security findings endpoints * [Trust and compliance](/trust/) – privacy posture, certifications, and data handling * [Reference](/reference/) – FAQ and glossary # Workspace admin > The account-level settings an admin manages, covering plans, billing, seats, and integrations. The admin section covers what doesn’t belong to one product, including plans, billing, seats, and integrations like Jira. Code-review and security settings live in their own sections. ## Roles [Section titled “Roles”](#roles) An admin manages the plan, billing, seats, and account settings; a member uses Sourcery but can’t change them. [Manage seats and team members](/admin/seats-and-team/) covers who counts as an admin. ## What you can change here [Section titled “What you can change here”](#what-you-can-change-here) [Plans and pricing ](/admin/plans/)Compare tiers and pick the right one for your team. [Billing ](/admin/billing/)Manage billing, whichever channel you signed up through. [Manage seats and team members ](/admin/seats-and-team/)Add or remove members and see who holds a seat. [Connect Jira ](/admin/connect-jira/)Send security findings into Jira as tasks. ## Settings that live elsewhere [Section titled “Settings that live elsewhere”](#settings-that-live-elsewhere) Code-review behavior, review rules, and path filters live in [configure code reviews](/reviews/configure/). Security scanning, findings, and triage live in the [security section](/security/). # Billing > Where to manage your Sourcery billing, whichever channel you signed up through. Where you manage billing depends on how you signed up. Most accounts bill through the Sourcery dashboard. Accounts that signed up through a marketplace manage billing there instead. ## Sourcery dashboard [Section titled “Sourcery dashboard”](#sourcery-dashboard) This is the default. Billing runs through Stripe, and you manage it from [**Billing**](https://app.sourcery.ai/dashboard/billing). From there you can: * Change your plan. * Update your payment method. * Update your billing address and tax ID. * Update your billing email. Self-hosted GitLab is billed the same way, per seat through the dashboard. ## GitHub Marketplace [Section titled “GitHub Marketplace”](#github-marketplace) If you subscribed through the GitHub Marketplace, GitHub handles the billing. The Sourcery dashboard shows your current plan but can’t change it or your seat count. To change the plan or seats, manage the subscription in GitHub. To assign the seats you have to [team members](/admin/seats-and-team/), use the Sourcery dashboard. ## Vercel Marketplace [Section titled “Vercel Marketplace”](#vercel-marketplace) If you subscribed through the Vercel Marketplace, Vercel handles the billing. Manage the subscription in Vercel. Note See [Plans and pricing](/admin/plans/) to compare tiers before you change. # Connect Jira > Connect a Jira org, map repositories to projects, and set autocreate rules so security findings flow into Jira. Connect Jira to turn security findings into Jira tasks. Once connected, you can create a task from any finding by hand, and set rules that create tasks automatically. Connecting Jira is an admin action, and you need permission to authorize an OAuth app on your Jira org. ## Connect and map repositories [Section titled “Connect and map repositories”](#connect-and-map-repositories) 1. Connect your Jira org Go to [**Integrations**](https://app.sourcery.ai/dashboard/integrations) and connect Jira. Sourcery sends you to Jira to authorize the connection, then brings you back. 2. Map repositories to projects Map each repository to a default Jira project. Tasks from that repository’s findings go to the project you choose. You don’t need to map everything: set an org-wide default project as the fallback for any repository you skip. ## Autocreate rules [Section titled “Autocreate rules”](#autocreate-rules) Autocreate opens a Jira task for a finding the moment a scan reports it. It’s available on the [Team plan](/admin/plans/). These controls decide which findings qualify: | Control | What it does | | ---------------------- | ------------------------------------------------------------------------------------------ | | Minimum severity | Findings below this severity never autocreate a task. | | Issue types | Turn autocreate on or off for SAST, IaC, secret, dependency, and license findings. | | Maximum issues per day | Limits how many tasks autocreate can open in a day, so a noisy scan can’t flood a project. | Note Autocreate runs as part of a security scan, so it needs security scanning turned on. Creating a task by hand does not. ## Create tasks by hand [Section titled “Create tasks by hand”](#create-tasks-by-hand) With Jira connected, you can open a task from any finding without turning on autocreate. Do this from the [security findings](/security/findings/) page, where each finding offers a create-task action. Use it to escalate one finding without flooding Jira with the rest. ## What’s next [Section titled “What’s next”](#whats-next) [Triage findings ](/security/triage/)Set severity and status before tasks reach Jira. [Security findings ](/security/findings/)Read what a finding contains and how to act on it. # Plans and pricing > What each Sourcery plan includes, so you can compare tiers and pick the right one. Sourcery has these plans. Pricing is per seat. | Plan | Price | For | | ----------- | -------------------------- | ---------------------------------------------------- | | Open Source | Free | Public repositories | | Pro | $15 per month ($12 annual) | Individuals and small teams | | Team | $30 per month ($24 annual) | Teams that need security scanning and admin controls | Paid plans start with a 14-day free trial, and no credit card is required. During the trial everyone in your organization has access. If you don’t add a payment method by the end of the trial, the subscription pauses until you do. ## What each plan includes [Section titled “What each plan includes”](#what-each-plan-includes) | Capability | Open Source | Pro | Team | | ----------------------------------- | --------------- | --------------- | ---- | | Code review on pull requests | Yes | Yes | Yes | | Reviewer’s guide, summary, diagrams | Yes | Yes | Yes | | IDE reviews and recipes | Yes | Yes | Yes | | Analytics | No | No | Yes | | Security scanning | Limited preview | Limited preview | Yes | | Private repositories | No | Yes | Yes | | Review rules | Yes | Yes | Yes | | User-triggered security scans | No | No | Yes | | Jira autocreate from findings | No | No | Yes | | Security REST API and API keys | No | No | Yes | | Bring your own LLM | No | No | Yes | Code review runs on every plan. Security scanning is a Team plan feature; Open Source and Pro include a limited preview, with scans on up to 3 (Open Source) or 10 (Pro) repositories, twice weekly, and up to 10 findings visible in the dashboard. The Team plan covers 200+ repositories, scans daily, shows unlimited findings, and adds on-demand scans. Every plan can [manage members and seats](/admin/seats-and-team/); you’re billed for each seat you assign. ## Rate limits [Section titled “Rate limits”](#rate-limits) Sourcery measures usage in diff characters, the size of the changed code it reads. Two limits apply. A per-pull-request cap stops Sourcery reviewing a single very large diff. A rolling seven-day budget caps total usage across all pull requests in the trailing week. The seven-day budget is multiplied by your seat count, so a larger team has a proportionally larger budget. Against the seven-day budget, a re-review costs a tenth of a normal review. | Plan | Per-pull-request cap | Rolling 7-day budget | | ----------- | -------------------- | -------------------- | | Open Source | 150,000 | 500,000 | | Pro | 300,000 | 1,500,000 | | Team | 500,000 | 2,500,000 | When a limit is reached, Sourcery skips the review. A rate limit never blocks a merge. See [re-reviews](/reviews/anatomy-of-a-review/#re-reviews) for what a skipped review looks like on the pull request, and for the separate cap of five automatic re-reviews per pull request. ## Enterprise [Section titled “Enterprise”](#enterprise) Enterprise is the Team plan on sales-negotiated terms. It covers self-hosted Sourcery, priority support, a customer success manager, and invoice billing. To set it up, [contact sales](mailto:sales@sourcery.ai). ## Changing plans [Section titled “Changing plans”](#changing-plans) You manage your plan on the [billing page](/admin/billing/). # Manage seats and team members > Add or remove team members, see who holds a seat, and learn how seats and roles work. A seat gives a person access to Sourcery’s reviews, the IDE extension, and the dashboard. ## Roles [Section titled “Roles”](#roles) Sourcery has two roles. An admin manages the plan, billing, seats, and account settings. A member uses Sourcery but can’t change those settings. You’re an admin if you’re an admin of your GitHub organization or GitLab group, or if you’re an explicit Sourcery admin. The person who first connects an account becomes a Sourcery admin. Only admins can change the settings on this page. ## Add or remove a seat [Section titled “Add or remove a seat”](#add-or-remove-a-seat) On a GitHub or GitLab account, manage seats from the [**Members**](https://app.sourcery.ai/dashboard/members) page. It lists your organization members, each with a toggle: on gives someone a seat, off takes it back. Nothing applies until you click **Save Changes**, so you can adjust several members and save once. An account with no Git organization gets a different version of that page. It lists everyone who holds a seat, with a badge on each admin. **Remove** next to a name removes the member and frees their seat. ## Seats and your plan [Section titled “Seats and your plan”](#seats-and-your-plan) Each member you assign holds a billed seat on your subscription, and your bill updates when you assign or remove seats. During a trial, every member gets access. Accounts billed through the GitHub Marketplace work differently: seats are fixed by the Marketplace subscription and managed there. ## Auto-assign new members [Section titled “Auto-assign new members”](#auto-assign-new-members) On a GitHub account’s **Members** page, turn on **Automatically assign a seat when someone joins this organization on GitHub** to seat new members as they join. Each auto-assigned member adds a billed seat. ## What’s next [Section titled “What’s next”](#whats-next) [Plans and pricing ](/admin/plans/)How per-seat pricing works and what's in each tier. [Billing ](/admin/billing/)Add seats or change your subscription. # Sourcery API > What the Sourcery API covers, the endpoints it exposes, and where to find the live OpenAPI reference. The Sourcery API is a Team-plan REST API for security findings. It is the only public API; the GraphQL endpoint stays internal. ## What you can do [Section titled “What you can do”](#what-you-can-do) Endpoints live under `/api/v1/`. They cover two resources: individual security issues and the groups Sourcery clusters related issues into. The group detail endpoint embeds its member issues, so listing a group’s issues is one request. | Endpoint | What it does | | ----------------------------------------- | ----------------------------------------------- | | `GET /api/v1/security-issues` | List issues, with filtering and pagination. | | `GET /api/v1/security-issues/{id}` | Get one issue. | | `GET /api/v1/security-issues/stats` | Aggregate counts by status and severity. | | `PATCH /api/v1/security-issues` | Bulk-update status and severity override. | | `GET /api/v1/security-issue-groups` | List groups, with filtering and pagination. | | `GET /api/v1/security-issue-groups/{id}` | Get one group, with its member issues embedded. | | `GET /api/v1/security-issue-groups/stats` | Aggregate counts by status and severity. | | `PATCH /api/v1/security-issue-groups` | Bulk-update status and severity override. | The list endpoints filter by `repository_ids`, `issue_types`, `statuses`, and `search`, and paginate with a `cursor` and `limit`. The API has no rate limits today. ## The OpenAPI reference [Section titled “The OpenAPI reference”](#the-openapi-reference) The live reference, with every parameter and response shape, is at [api.sourcery.ai/api](https://api.sourcery.ai/api). It’s always current because it’s generated from the running service. Caution The endpoints require security scanning, a Team-plan capability. Anyone with a seat on a GitHub- or GitLab-connected account can create a key, but it only works against a Team-plan account. ## What’s next [Section titled “What’s next”](#whats-next) [API keys and authentication ](/api/authentication/)Create a key, store it, and make your first request. [Findings and severity ](/security/findings/)How the findings the API returns are scored and grouped. # Authenticate to the API > Create a Sourcery API key, store it safely, and make your first authenticated request. Create an API key and make your first request. The API authenticates with a bearer token, and the endpoints it reaches need a Team-plan account. ## Create a key and call the API [Section titled “Create a key and call the API”](#create-a-key-and-call-the-api) 1. Create a key Go to [**API Keys**](https://app.sourcery.ai/dashboard/api-keys) and create a key. 2. Store it now The key is shown once, at creation. Copy it into your secret store right away. You can’t retrieve it later, so a lost key means creating a new one. 3. Make a request Pass the key in the `Authorization` header as a bearer token: ```bash curl https://api.sourcery.ai/api/v1/security-issues \ -H "Authorization: Bearer $SOURCERY_API_KEY" ``` Caution The endpoints require security scanning, a Team-plan capability. A key created on a lower plan returns a 403 until the account is on the Team plan. ## What’s next [Section titled “What’s next”](#whats-next) [Sourcery API ](/api/)The full endpoint list and the live OpenAPI reference. [Findings and severity ](/security/findings/)How the findings the API returns are scored and grouped. [Triage a finding ](/security/triage/)The status and severity actions the bulk-patch endpoint drives. # Sourcery in your IDE > What an IDE review is, how it relates to PR reviews, and which editors are supported. Sourcery reviews your code in your editor, before you open a pull request. It runs the same review engine that reviews pull requests, pointed at your working changes. You catch findings before you push. ## How it relates to PR reviews [Section titled “How it relates to PR reviews”](#how-it-relates-to-pr-reviews) A PR review runs on the pull request diff after you push. An IDE review runs in your editor on your current changes. From the Sourcery panel, you pick what to review: the current file, or your working changes against HEAD or another branch. Findings appear as inline diagnostics, and you apply fixes without leaving the editor. See [reviews in the IDE](/ide/reviews-in-the-ide/) for the full flow and the in-editor recipes. Both run the same AI review engine, and the IDE applies your organization’s review rules and path filters from [**Review Settings**](https://app.sourcery.ai/dashboard/review-settings). The editor skips the checks that need the pull request itself. Security scanning, complexity analysis, and linked-issue assessment run only on a pull request, and per-repository settings apply there too. Run a pull request review for full coverage. ## Supported editors [Section titled “Supported editors”](#supported-editors) The extension runs in VS Code, Cursor, Windsurf, and JetBrains. It signs in with a personal Sourcery token, and model access is proxied through Sourcery, so you don’t supply your own LLM keys here. ## What’s next [Section titled “What’s next”](#whats-next) [Install the extension ](/ide/install/)Set up and sign in on any supported editor. [Review your code in the IDE ](/ide/reviews-in-the-ide/)Trigger a review, read findings, and apply fixes. # Install the extension > Install the Sourcery extension and sign in, on VS Code, Cursor, Windsurf, or JetBrains. Install the Sourcery extension in your editor and sign in. ## Install [Section titled “Install”](#install) VS Code, Cursor, and Windsurf each install from a deeplink that opens the editor on the Sourcery extension. JetBrains installs from its marketplace page. | Editor | Install | | --------- | ------------------------------------------------------------------------------------------------------- | | VS Code | Open `vscode:extension/sourcery.sourcery`, then click install. | | Cursor | Open `cursor:extension/sourcery.sourcery`, then click install. | | Windsurf | Open `windsurf:extension/sourcery.sourcery`, then click install. | | JetBrains | Install [Sourcery from the JetBrains Marketplace](https://plugins.jetbrains.com/plugin/12631-sourcery). | ## Sign in [Section titled “Sign in”](#sign-in) 1. Click Log In in the editor Open the Sourcery panel and click **Log In**. The extension starts a device-authorization flow and opens your browser to confirm it. 2. Confirm and return Confirm the request in the browser. The extension picks up the token and signs you in automatically. No browser in your editor If your editor can’t open a browser, sign in by hand. Copy your token from the [**IDE**](https://app.sourcery.ai/dashboard/ide-integration) page and paste it into the extension. You can regenerate it from the same page. ## What’s next [Section titled “What’s next”](#whats-next) [Review your code in the IDE ](/ide/reviews-in-the-ide/)Trigger a review, read findings, and apply fixes. [Sourcery in your IDE ](/ide/)How IDE reviews relate to pull request reviews. # Review your code in the IDE > Trigger an IDE review on your current changes, read the inline findings, and apply fixes. Run a Sourcery review on the code you’re working on, read the findings inline, and apply a fix. ## Run a review and apply a fix [Section titled “Run a review and apply a fix”](#run-a-review-and-apply-a-fix) 1. Pick what to review In the Sourcery panel, choose a comparison mode. * **Review current file** reviews the file you’re in. * **Compare with HEAD** reviews your branch against its last commit. * **Compare with ``** reviews it against another branch. 2. Click Review My Code Click **Review My Code** and wait for Sourcery to publish its findings. 3. Read findings inline Findings appear as diagnostics on the lines they touch, the same way your linter’s warnings do. Open one to see the explanation and a suggested fix. 4. Apply a fix Where Sourcery suggests a change, use the **Apply fix** action on the finding (the code action reads **Sourcery - Apply fix**) to write it into your code. You can resolve a finding without applying it if you’d rather handle it yourself. Note [**Review Settings**](https://app.sourcery.ai/dashboard/review-settings) governs IDE reviews too, including the review rules and path filters. ## Recipes [Section titled “Recipes”](#recipes) The Sourcery panel also runs these recipes: * Generate Diagram * Generate Tests * Generate Docstrings * Explain Code Chat has its own view in the panel, for free-form questions about your code. ## What’s next [Section titled “What’s next”](#whats-next) [Configure code reviews ](/reviews/configure/)Set the review rules and path filters that apply in the editor. [Review rules ](/reviews/review-rules/)Encode your team's conventions as review rules. # Reference > FAQ and glossary for Sourcery. Lookup pages for Sourcery: common questions, and the terms these docs use. [FAQ ](/reference/faq/)Answers to the questions readers ask in support. [Glossary ](/reference/glossary/)What each Sourcery-specific term means. # FAQ > Answers to the questions Sourcery users ask most, from setup to billing to data handling. The questions that come up most often in support, grouped by topic. Each answer links to the page with the full detail. If yours isn’t here, in-app chat on any dashboard page is the fastest way to reach us. ## Getting started [Section titled “Getting started”](#getting-started) ### Sourcery stopped reviewing my pull requests. What happened? [Section titled “Sourcery stopped reviewing my pull requests. What happened?”](#sourcery-stopped-reviewing-my-pull-requests-what-happened) Most often the connection to your Git host has expired or been revoked, so Sourcery can no longer read your pull requests. An admin can restore it by reconnecting Sourcery from the dashboard; the [quickstart for your platform](/start/) covers the connection steps. If reviews still don’t run after reconnecting, reach us through in-app chat. ### I get “you do not have a seat for this account” and can’t reach the dashboard. [Section titled “I get “you do not have a seat for this account” and can’t reach the dashboard.”](#i-get-you-do-not-have-a-seat-for-this-account-and-cant-reach-the-dashboard) You need a seat to use Sourcery. An admin on your account [assigns seats](/admin/seats-and-team/) from the **Members** page, so ask them to check that you’re added and seated. If you signed in with a different identity than your teammates, for example Google instead of GitHub, you may be on a different account. ### Can I use Sourcery on more than one organization? [Section titled “Can I use Sourcery on more than one organization?”](#can-i-use-sourcery-on-more-than-one-organization) Yes. Each organization you connect is its own Sourcery account, with its own plan, seats, and billing. None of it is pooled across organizations. Your login spans all of them: click your account name at the top of the dashboard sidebar to switch accounts, or, from the same menu, **Connect GitHub organization** to add another. The [GitHub quickstart](/start/quickstart-github/) covers the connection steps. ## Code reviews [Section titled “Code reviews”](#code-reviews) ### Sourcery skipped my pull request or stopped re-reviewing it. Why? [Section titled “Sourcery skipped my pull request or stopped re-reviewing it. Why?”](#sourcery-skipped-my-pull-request-or-stopped-re-reviewing-it-why) Sourcery measures usage in diff characters and applies a per-pull-request cap and a rolling seven-day budget that scales with your seat count. Automatic re-reviews are also capped at five per pull request. When a limit is reached, the **Sourcery review** check reports Skipped. To run a full review again, comment `@sourcery-ai review`. See [re-reviews](/reviews/anatomy-of-a-review/#re-reviews). ### My review rules aren’t being applied. [Section titled “My review rules aren’t being applied.”](#my-review-rules-arent-being-applied) Check that the rule’s path patterns match the files in the pull request and that the [repository is enabled for review](/reviews/configure/). [Review rules](/reviews/review-rules/) covers how a rule is scoped and when it runs. If a rule still doesn’t fire on a pull request where you expect it, send us the link through in-app chat. ### Which programming languages does Sourcery review? [Section titled “Which programming languages does Sourcery review?”](#which-programming-languages-does-sourcery-review) Any language GitHub recognizes as a programming or markup language. Sourcery skips lockfiles and generated files. Security scanning coverage is listed per scanner on [the scanners](/security/scanners/) page. ## Plans, billing, and seats [Section titled “Plans, billing, and seats”](#plans-billing-and-seats) ### Why is my bill higher than expected? [Section titled “Why is my bill higher than expected?”](#why-is-my-bill-higher-than-expected) You’re billed per seat, and depending on your settings new organization members can be seated automatically. Review who holds a seat on [**Members**](https://app.sourcery.ai/dashboard/members) and unassign anyone who doesn’t need one, or turn off auto-assign and grant each seat yourself. [Plans and pricing](/admin/plans/) has the full tier comparison. ### How do I cancel or change my subscription? [Section titled “How do I cancel or change my subscription?”](#how-do-i-cancel-or-change-my-subscription) If you bill through the Sourcery dashboard, manage or cancel your subscription on [**Billing**](https://app.sourcery.ai/dashboard/billing). If you subscribed through the GitHub or Vercel Marketplace, that marketplace handles billing and you change or cancel it there. See [billing](/admin/billing/). ### I’m on a paid plan but the dashboard shows me on the free plan. [Section titled “I’m on a paid plan but the dashboard shows me on the free plan.”](#im-on-a-paid-plan-but-the-dashboard-shows-me-on-the-free-plan) This usually means you have two accounts from signing in with different methods, for example Google and GitHub, and the subscription lives on the other one. Sign in with the identity you subscribed under, or reach us through in-app chat and we’ll point you to the right account. ## Trust and privacy [Section titled “Trust and privacy”](#trust-and-privacy) ### Is my code used to train models, or kept after a review? [Section titled “Is my code used to train models, or kept after a review?”](#is-my-code-used-to-train-models-or-kept-after-a-review) No. None of your code is used by OpenAI, Anthropic, or Sourcery to train any model, and Sourcery retains none of your code after a review. Anthropic holds nothing under a zero-day agreement, and OpenAI retains data for no more than 30 days. See [code retention](/trust/code-retention/). ### Can Sourcery run on my own LLM? [Section titled “Can Sourcery run on my own LLM?”](#can-sourcery-run-on-my-own-llm) Yes, on the Team plan. [Bring your own LLM](/trust/bring-your-own-llm/) runs reviews through a model deployment you control, such as an existing Anthropic or Azure OpenAI contract. Sourcery still processes your code to build the review and [retains none of it](/trust/code-retention/). ### What IP addresses does Sourcery connect from, so I can allow them? [Section titled “What IP addresses does Sourcery connect from, so I can allow them?”](#what-ip-addresses-does-sourcery-connect-from-so-i-can-allow-them) Sourcery makes outbound requests from two static addresses, `34.142.124.129` and `34.147.223.148`. Add them to your Git host or firewall allowlist. This matters most for GitHub Enterprise Server, self-hosted GitLab, and IP-restricted GitHub.com organizations. See [network requirements](/trust/network-requirements/). ### How do I delete my account and all my data? [Section titled “How do I delete my account and all my data?”](#how-do-i-delete-my-account-and-all-my-data) Reach us through in-app chat or email , and we’ll delete your account and all associated data. Tell us the email and the Git host identity (GitHub, GitLab, or Google) you signed up with, so we can find the right account. # Glossary > The Sourcery-specific terms used across these docs, defined in one place. Terms Sourcery uses across these docs, defined in one place. Each entry links to the page where you can read more. | Term | Definition | | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Admin** | Manages the plan, billing, seats, and account settings. You’re an admin if you’re an admin of your GitHub organization or GitLab group, or an explicit Sourcery admin. [Roles](/admin/) | | **Autocreate** | Opens a Jira task automatically when a scan reports a security finding, gated by a severity threshold, per-type toggles, and a daily cap. [Connect Jira](/admin/connect-jira/) | | **Deeplink** | A URL that opens a supported editor straight on the Sourcery extension, used for one-click installs. [Install the extension](/ide/install/) | | **Finding** | A single security issue found by one of Sourcery’s scanners, with a severity score, a status, and membership in a group. [Security findings](/security/findings/) | | **Group** | Findings that look alike, shown as one row in the issues view. Turn grouping off to see every finding on its own. [Security findings](/security/findings/) | | **Ignore** | Permanently marks a finding as not needing action. Available for a single finding, a path prefix, or a rule. [Triage](/security/triage/) | | **Member** | Uses Sourcery (reviews, IDE, dashboard) but can’t change the plan, billing, or seats. [Roles](/admin/) | | **Review rule** | A check you write in plain language, scoped to file paths, that Sourcery applies on every review. [Review rules](/reviews/review-rules/) | | **Reviewer’s guide** | A review comment that maps the change set file by file, so human reviewers know where to focus. [Anatomy of a review](/reviews/anatomy-of-a-review/) | | **Risk profile** | A per-repository setting (network exposure and data sensitivity) that Sourcery uses to weight finding severities for that repo. [Repository risk profile](/security/repository-risk-profile/) | | **Scan** | A security scan run over a repository’s default branch. Sourcery’s scanners run and report their findings to the dashboard. [The scanners](/security/scanners/) | | **Seat** | An allocation of Sourcery access to one person, covering code reviews, the IDE extension, and the dashboard. [Seats and team](/admin/seats-and-team/) | | **Severity override** | Adjusts a finding’s severity when the scored level isn’t how you’d rank it. Remove the override to restore the original score. [Triage](/security/triage/) | | **Snooze** | Sets a finding aside until a date you choose, with an optional reason. Future scans still detect the issue. [Triage](/security/triage/) | | **Status check** | The single “Sourcery review” check on the pull request’s head commit, reporting the review’s outcome. [Anatomy of a review](/reviews/anatomy-of-a-review/) | | **Summary** | A summary of the pull request’s purpose and risk, written into the description under a “Summary by Sourcery” heading. [Anatomy of a review](/reviews/anatomy-of-a-review/) | # Refactorings > The rule catalogue for Sourcery's original refactoring tool, with a before and after for each. Browse the full catalogue by language. These are the built-in rules for Sourcery’s original refactoring tool, which suggests automatic code improvements. The tool is still available, and each page shows what a rule looks for and the change it suggests. There are 212 rules today. These are distinct from [review rules](/reviews/review-rules/), which your team writes to guide code review. ## Python [Section titled “Python”](#python) * [Assign If Expression](/reference/refactorings/python/assign-if-exp/) – Replaces conditional assignment to a variable with an if expression * [Augmented Assign](/reference/refactorings/python/aug-assign/) – Replaces assignments with augmented assignments * [Avoid Builtin Shadow](/reference/refactorings/python/avoid-builtin-shadow/) – Don’t assign to builtin variables, such as list. * [Aware datetime For UTC](/reference/refactorings/python/aware-datetime-for-utc/) – For getting the current time in UTC, use an aware datetime object with the timezone explicitly set to UTC. * [Binary Operator Identity](/reference/refactorings/python/bin-op-identity/) – Replaces binary operations between a value and itself with known identities: * [Boolean If Expression Identity](/reference/refactorings/python/boolean-if-exp-identity/) – Simplifies boolean if expressions by removing unnecessary explicit references to True or False states * [Break-Or-Continue-Outside-Loop](/reference/refactorings/python/break-or-continue-outside-loop/) – Remove break or continue statement found outside for or while loop * [Chain Compares](/reference/refactorings/python/chain-compares/) – Combine two compares on same value into a chained compare * [Class Extract Method](/reference/refactorings/python/class-extract-method/) – Extracts duplicate pieces of code in different functions in a class into their own methods * [Class Method First Parameter Should Be `cls`](/reference/refactorings/python/class-method-first-arg-name/) – Suggests that class methods should rename their first parameter to cls. * [Collection to Comprehension](/reference/refactorings/python/collection-builtin-to-comprehension/) – Use list, set or dictionary comprehensions directly instead of calling list(), dict() or set() * [Collection Into Set](/reference/refactorings/python/collection-into-set/) – Use set when checking membership of a collection of literals * [Collection to Bool](/reference/refactorings/python/collection-to-bool/) – Replace constant collection with boolean in boolean contexts * [Compare Via Equals](/reference/refactorings/python/compare-via-equals/) – Use == or != to compare str, bytes, int, and float. * [Comprehension to Generator](/reference/refactorings/python/comprehension-to-generator/) – Replace unneeded comprehension with generator * [Convert Any to In](/reference/refactorings/python/convert-any-to-in/) – Converts any() functions to simpler in statements * [Convert to Enumerate](/reference/refactorings/python/convert-to-enumerate/) – Replaces manual loop counter with call to enumerate * [DataFrame Append to Concat](/reference/refactorings/python/dataframe-append-to-concat/) – Use pandas.concat() instead of the deprecated DataFrame.append(). * [De Morgan’s Laws](/reference/refactorings/python/de-morgan/) – Simplifies conditional logic using De Morgan’s laws * [Default Get](/reference/refactorings/python/default-get/) – Simplify dictionary access by using the default get method * [Default Mutable Arguments](/reference/refactorings/python/default-mutable-arg/) – Replaces use of default mutable arguments in a function * [Del Comprehension](/reference/refactorings/python/del-comprehension/) – Replaces cases where deletions are made via for loops with comprehensions * [Dict Assign-Update to Union](/reference/refactorings/python/dict-assign-update-to-union/) – Changes dictionary assignments and updates to use the union operator. * [Dictionary Comprehension](/reference/refactorings/python/dict-comprehension/) – Replaces dictionaries created with for loops with dictionary comprehensions * [Dict-Literal](/reference/refactorings/python/dict-literal/) – Replace `dict()` with `{}` * [Do Not Use Bare Except](/reference/refactorings/python/do-not-use-bare-except/) – Use except: Exception rather than bare except * [Dont-Import-Test-Modules](/reference/refactorings/python/dont-import-test-modules/) – Don’t import test modules. * [Ensure File Closed](/reference/refactorings/python/ensure-file-closed/) – Use with when opening file to ensure closure * [Equality Identity](/reference/refactorings/python/equality-identity/) – Simplify equality comparisons that are always True or False * [Extract Duplicate Method](/reference/refactorings/python/extract-duplicate-method/) – Identifies duplicate sections of code in a function and extracts these into their own method * [Extract Method](/reference/refactorings/python/extract-method/) – Extracts complex pieces of functions into their own methods * [Flatten Nested Try](/reference/refactorings/python/flatten-nested-try/) – Merge nested try-statement into a single try * [Flip Comparison](/reference/refactorings/python/flip-comparison/) – Moves variables from the right side to the left side of comparisons * [For Append To Extend](/reference/refactorings/python/for-append-to-extend/) – Replace a for append loop with list extend * [For Index Replacement](/reference/refactorings/python/for-index-replacement/) – Replace item lookups in loops using the index with direct reference to the items * [Replace For Index with Underscore](/reference/refactorings/python/for-index-underscore/) – Replaces an unused index in a for loop or comprehension with an underscore * [Guard](/reference/refactorings/python/guard/) – Adds in guard clause to a conditional * [Hoist If from If](/reference/refactorings/python/hoist-if-from-if/) – Moves if statements that match a conditional out of that conditional * [Hoist Loop from If](/reference/refactorings/python/hoist-loop-from-if/) – Moves loops that occur in all cases of an if statement outside of the conditional * [Hoist Repeated If Condition](/reference/refactorings/python/hoist-repeated-if-condition/) – Move a repeated condition to a parent if block. * [Hoist Similar Statements from If](/reference/refactorings/python/hoist-similar-statement-from-if/) – Hoist nested repeated code outside conditional statements * [Hoist Statements from If](/reference/refactorings/python/hoist-statement-from-if/) – Moves statements that occur in all cases of an if statement outside of the conditional * [Hoist Statement from Loop](/reference/refactorings/python/hoist-statement-from-loop/) – Moves statements that are constant across all cases of a loop outside of the loop * [Identity Comprehensions](/reference/refactorings/python/identity-comprehension/) – Convert list/set/tuple comprehensions that do not change the input elements into * [Inline Immediately Returned Variables](/reference/refactorings/python/inline-immediately-returned-variable/) – Inlines a variable to a return in the case when the variable being declared is immediately returned * [Inline Immediately Yielded Variable](/reference/refactorings/python/inline-immediately-yielded-variable/) – Inline variable that is immediately yielded * [Inline Variable](/reference/refactorings/python/inline-variable/) – Inline variable that is only used once * [Instance Method First Parameter Should Be `self`](/reference/refactorings/python/instance-method-first-arg-name/) – Suggests that instance methods should rename their first parameter to self. * [Introduce Default Else](/reference/refactorings/python/introduce-default-else/) – Move default state of variable into else branch * [Invert Any/All in Body](/reference/refactorings/python/invert-any-all-body/) – Simplifies any and all statements that can be inverted * [Invert Any/All](/reference/refactorings/python/invert-any-all/) – Switches not any or not all statements to all or any statements respectively * [Last if statement guard](/reference/refactorings/python/last-if-guard/) – Convert the final conditional into a guard clause * [Lift Duplicated Conditional](/reference/refactorings/python/lift-duplicated-conditional/) – Lift repeated conditional into its own if statement * [Lift Return into If](/reference/refactorings/python/lift-return-into-if/) – Lift return into if * [List Comprehension](/reference/refactorings/python/list-comprehension/) – Converts a for loop into a list comprehension * [List Literal](/reference/refactorings/python/list-literal/) – Replaces lists created with list() with \[] * [Low Code Quality](/reference/refactorings/python/low-code-quality/) – Low code quality found in function. * [Max/min Default](/reference/refactorings/python/max-min-default/) – Use max/min default argument instead of if statement * [Merge Assignment and Augmented Assignment](/reference/refactorings/python/merge-assign-and-aug-assign/) – Replaces an assignment and an augmented assignment with a single assignment. * [Merge Comparisons](/reference/refactorings/python/merge-comparisons/) – Consolidates multiple comparisons into a single comparison * [Merge Dictionary Assignments](/reference/refactorings/python/merge-dict-assign/) – Declare the dictionary with values rather than creating an empty one and assigning to it * [Merge Duplicate Blocks](/reference/refactorings/python/merge-duplicate-blocks/) – Restructure conditional to merge duplicate branches together * [Merge Else If Into Elif](/reference/refactorings/python/merge-else-if-into-elif/) – Merge else clause’s nested if statement into elif * [Merge Exception Handlers](/reference/refactorings/python/merge-except-handler/) – Merge exception handlers with the same body into a single except handler. * [Merge isinstance](/reference/refactorings/python/merge-isinstance/) – Combines together multiple isinstance functions * [Merge List Append](/reference/refactorings/python/merge-list-append/) – Create the list with values instead of creating an empty list and appending to it * [Merge List Appends Into Extend](/reference/refactorings/python/merge-list-appends-into-extend/) – Merge consecutive list appends into a single extend. * [Merge List Extend](/reference/refactorings/python/merge-list-extend/) – Create the list with values instead of creating an empty list and extending it with another list * [Merge-Nested-Ifs](/reference/refactorings/python/merge-nested-ifs/) – Merge nested if conditions * [Merge Repeated Ifs](/reference/refactorings/python/merge-repeated-ifs/) – Merges together the interior contents of if statements with identical conditions * [Merge Set Add](/reference/refactorings/python/merge-set-add/) – Create the set with values instead of declaring an empty set and adding to it * [Method\_chaining](/reference/refactorings/python/method_chaining/) – Chaining methods improves readability * [Min/Max identity](/reference/refactorings/python/min-max-identity/) – Replaces duplicate conditionals looking for the minimum or maximum value of multiple variables with a min or max function * [Missing Dict Items](/reference/refactorings/python/missing-dict-items/) – Add missing .items() call when unpacking a dictionary * [Move Assign In Block](/reference/refactorings/python/move-assign-in-block/) – Moves assignment of variables closer to their usage within a block * [Move Assign](/reference/refactorings/python/move-assign/) – Moves assignment of variables closer to their usage * [No-Conditionals-In-Tests](/reference/refactorings/python/no-conditionals-in-tests/) – Avoid conditionals in tests. * [No-Loop-In-Tests](/reference/refactorings/python/no-loop-in-tests/) – Avoid loops in tests. * [Non Equal Comparison](/reference/refactorings/python/non-equal-comparison/) – Simplify comparison of non equal values * [None Compare](/reference/refactorings/python/none-compare/) – Replaces == with is when comparing to None * [Or-If-Exp-Identity](/reference/refactorings/python/or-if-exp-identity/) – Replace if-expression with `or` * [Pandas: Avoid inplace](/reference/refactorings/python/pandas-avoid-inplace/) – Don’t use inplace for methods that always create a copy under the hood. * [pathlib.Path Read](/reference/refactorings/python/path-read/) – Simplify basic file reads with pathlib. * [Raise From Previous Error](/reference/refactorings/python/raise-from-previous-error/) – Suggests raising from a previously-raised exception. * [Raise-Specific-Error](/reference/refactorings/python/raise-specific-error/) – Raise a specific error instead of the general `Exception` or `BaseException` * [Reintroduce Else](/reference/refactorings/python/reintroduce-else/) – Lift code into else after break in control flow * [Remove Assert True](/reference/refactorings/python/remove-assert-true/) – Remove assert True statements * [Remove Dict Items](/reference/refactorings/python/remove-dict-items/) – Remove unnecessary calls to dict.items when the values are not used. * [Remove Dictionary Keys](/reference/refactorings/python/remove-dict-keys/) – Removes unnecessary call to keys() when iterating over a dictionary * [Remove Duplicate Dict Key](/reference/refactorings/python/remove-duplicate-dict-key/) – Remove duplicate keys when instantiating dicts. * [Remove Duplicate Set Key](/reference/refactorings/python/remove-duplicate-set-key/) – Remove duplicate keys when instantiating sets. * [Remove Empty Nested Block](/reference/refactorings/python/remove-empty-nested-block/) – Remove nested block which has no effect * [Remove-None-From-Default-Get](/reference/refactorings/python/remove-none-from-default-get/) – Replace `dict.get(x, None)` with `dict.get(x)` * [Remove Pass From Body](/reference/refactorings/python/remove-pass-body/) – Removes a pass from the body of a conditional by inverting it * [Remove Pass From Elif](/reference/refactorings/python/remove-pass-elif/) – Removes a pass from the elif section of a conditional * [Remove Redundant Boolean](/reference/refactorings/python/remove-redundant-boolean/) – Removes redundant booleans from tests. * [Remove Redundant Condition](/reference/refactorings/python/remove-redundant-condition/) – Remove a redundant condition used during variable assignment * [Remove Redundant Constructor in Dict Union](/reference/refactorings/python/remove-redundant-constructor-in-dict-union/) * [Remove Redundant Continue](/reference/refactorings/python/remove-redundant-continue/) – Remove redundant continue statement * [Remove Redundant Except Handler](/reference/refactorings/python/remove-redundant-except-handler/) – Removes exception handlers that can never trigger (as the exceptions have already been caught) * [Remove Redundant Exception](/reference/refactorings/python/remove-redundant-exception/) – Remove redundant exceptions from an except clause. * [Remove Redundant f-string](/reference/refactorings/python/remove-redundant-fstring/) – If an f-string has no replacements turn it into a regular string. * [Remove Redundant If Statements](/reference/refactorings/python/remove-redundant-if/) – Removes conditional tests where the conditional is always True or False * [Remove Redundant Pass](/reference/refactorings/python/remove-redundant-pass/) – Removes unnecessary pass statements * [Remove-Redundant-Path-Exists](/reference/refactorings/python/remove-redundant-path-exists/) – Remove unnecessary `path.exists()` check. * [Remove Redundant Slice Index](/reference/refactorings/python/remove-redundant-slice-index/) – Removes unnecessary slice indices. * [Remove Str From Fstring](/reference/refactorings/python/remove-str-from-fstring/) – Remove unnecessary calls to str() within formatted values in f-strings * [Remove str() from call to print()](/reference/refactorings/python/remove-str-from-print/) – Removes unnecessary calls to str() from within print() * [Remove-Unit-Step-From-Range](/reference/refactorings/python/remove-unit-step-from-range/) – Replace range(x, y, 1) with range(x, y) * [Remove Unnecessary Cast](/reference/refactorings/python/remove-unnecessary-cast/) – Remove unnecessary casts to int, str, float and bool * [Remove Unnecessary Else](/reference/refactorings/python/remove-unnecessary-else/) – Remove unnecessary else after guard condition * [Remove Unreachable Code](/reference/refactorings/python/remove-unreachable-code/) – Removes code that will never be executed * [Remove Unused Enumerate](/reference/refactorings/python/remove-unused-enumerate/) – Remove unnecessary calls to enumerate when the index variable is not used. * [Remove-Zero-From-Range](/reference/refactorings/python/remove-zero-from-range/) – Replace range(0, x) with range(x) * [Replace `apply` With Method Call](/reference/refactorings/python/replace-apply-with-method-call/) – Replace .apply with a call to a DataFrame’s or Series’s method. * [Replace apply With NumPy Operation](/reference/refactorings/python/replace-apply-with-numpy-operation/) – Replace apply with a NumPy operation. * [Replace Dict Items with Values](/reference/refactorings/python/replace-dict-items-with-values/) – Replace calls to dict.items with dict.values when the keys are not used. * [Replace Interpolation With Fstring](/reference/refactorings/python/replace-interpolation-with-fstring/) – Replace usage of string interpolation % operator with f-strings * [Return or Yield Outside Function](/reference/refactorings/python/return-or-yield-outside-function/) – Remove return or yield statements found outside function definitions. * [Set Comprehension](/reference/refactorings/python/set-comprehension/) – Replaces sets created with for loops with set comprehensions * [Simplify Boolean Comparison](/reference/refactorings/python/simplify-boolean-comparison/) – Removes unnecessarily verbose boolean comparisons * [Simplify-Constant-Sum](/reference/refactorings/python/simplify-constant-sum/) – Simplify constant sum() call * [Simplify Dictionary Update](/reference/refactorings/python/simplify-dictionary-update/) – Add single value to dictionary directly rather than using update() * [Simplify Division](/reference/refactorings/python/simplify-division/) – Use Python’s built-in feature for succinct division syntax. * [Simplify Empty Collection Comparison](/reference/refactorings/python/simplify-empty-collection-comparison/) – Replace an empty collection comparison with a more idiomatic unary operator. * [Simplify f-string Formatting](/reference/refactorings/python/simplify-fstring-formatting/) – Simplify the formatting of replacements within an f-string. * [Simplify Generator](/reference/refactorings/python/simplify-generator/) – An identity generator (a for a in coll) can be replaced directly with the collection coll * [Simplify Length Comparison](/reference/refactorings/python/simplify-len-comparison/) – Removes unnecessarily verbose length comparisons * [Simplify Negative Index](/reference/refactorings/python/simplify-negative-index/) – Replaces a\[len(a)-1] with negative index lookup a\[-1] * [Simplify Numeric Comparison](/reference/refactorings/python/simplify-numeric-comparison/) – Consolidates any mathematical operations in a numeric comparison so there is a direct comparison of variable to number * [Simplify Single Exception Tuple](/reference/refactorings/python/simplify-single-exception-tuple/) – Replace length-one exception tuple with exception. * [Simplify String Length Comparison](/reference/refactorings/python/simplify-str-len-comparison/) – Changes an indirect comparison of a string’s length to 0 into a direct comparison of the string to the empty string. * [Simplify substring search](/reference/refactorings/python/simplify-substring-search/) – Simplify finding if substrings are present in strings by using in * [Skip Sorted List Construction](/reference/refactorings/python/skip-sorted-list-construction/) – Removes an unnecessary intermediate construction call for a sorted list, in favour of the sorted builtin. * [Split or ifs](/reference/refactorings/python/split-or-ifs/) – Splits out conditions combined with an or in an if statement into their own if statement. * [Square Identity](/reference/refactorings/python/square-identity/) – Replaces cases of a variable being multiplied by itself with squaring that variable * [Str Prefix Suffix](/reference/refactorings/python/str-prefix-suffix/) – Replace explicit str prefix/suffix check with call to startswith/endswith. * [Sum Comprehension](/reference/refactorings/python/sum-comprehension/) – Replaces summed values created with for loops with sum comprehensions * [Swap If Else Branches](/reference/refactorings/python/swap-if-else-branches/) – Swaps if and else branches of conditionals * [Swap-If-Expression](/reference/refactorings/python/swap-if-expression/) – Swap if/else branches of if expression to remove negation * [Swap Nested Ifs](/reference/refactorings/python/swap-nested-ifs/) – Swaps the order of nested if statements * [Swap Variable](/reference/refactorings/python/swap-variable/) – Swap variable values with tuple assignment * [Switch](/reference/refactorings/python/switch/) – Simplify conditionals into a form more like a switch statement * [Ternary to If Expression](/reference/refactorings/python/ternary-to-if-expression/) – Replace boolean ternary with inline if expression. * [Tuple-Literal](/reference/refactorings/python/tuple-literal/) – Replace `tuple()` with `()` * [Unwrap Iterable Construction](/reference/refactorings/python/unwrap-iterable-construction/) – Unwrap an iterable constructor into a literal iterable. * [Use Any](/reference/refactorings/python/use-any/) – Use any rather than a for loop * [Use Assigned Variable](/reference/refactorings/python/use-assigned-variable/) – Uses a variable that was previously defined in the function instead of repeating what was defined in the variable * [Use `contextlib.suppress`](/reference/refactorings/python/use-contextlib-suppress/) – Use contextlib’s suppress method to silence a specific error, instead of passing in an exception handler. This refactoring will add an import for… * [Use Count](/reference/refactorings/python/use-count/) – Replaces sum() with count() where appropriate * [Use Datetime Now Not Today](/reference/refactorings/python/use-datetime-now-not-today/) – Replace calls to datetime.datetime.today() with datetime.datetime.now(). They are functionally equivalent, but now is a more expressive name. * [Use Dictionary Items](/reference/refactorings/python/use-dict-items/) – Use dictionary.items() in for loops to access both key and value at same time * [Use Dictionary Union](/reference/refactorings/python/use-dictionary-union/) – Replace a sequence of unpacked dictionaries with a use of the dictionary union operator. * [Use File Iterator](/reference/refactorings/python/use-file-iterator/) – Use the in-built file iterator rather than calling readlines() * [Use Fstring For Concatenation](/reference/refactorings/python/use-fstring-for-concatenation/) – Use f-strings for concatenating strings instead of ’+’ * [Use FString For Formatting](/reference/refactorings/python/use-fstring-for-formatting/) – Replace calls to string.format() with f-strings. * [Use Getitem For Re Match Groups](/reference/refactorings/python/use-getitem-for-re-match-groups/) – Access groups in re.Match objects using getitem * [Use-Isna](/reference/refactorings/python/use-isna/) – Use `.isna()` or `.isnull()` instead of `== np.nan` for detecting missing values. * [Use `itertools.product`](/reference/refactorings/python/use-itertools-product/) – Replaces a nested for loop over independent iterables with itertools.product. This refactoring won’t be activated if either iterable is a literal such as… * [Use str.join()](/reference/refactorings/python/use-join/) – Use str.join() instead of for loop * [Use len()](/reference/refactorings/python/use-len/) – Replaces sum() with len where appropriate * [Use Named Expression](/reference/refactorings/python/use-named-expression/) – Merge assignment followed by conditional check using a named expression. * [Use Next](/reference/refactorings/python/use-next/) – Use the built-in function next instead of a for-loop. * [Use-Or-For-Fallback](/reference/refactorings/python/use-or-for-fallback/) – Use `or` for providing a fallback value * [Use String Remove Affix](/reference/refactorings/python/use-string-remove-affix/) – Replaces string slicing with the Python 3.9 features removesuffix and removeprefix. * [Use\_iloc](/reference/refactorings/python/use_iloc/) – Use the [`.iloc`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.iloc.html) attribute for index-based selection * [Useless-Else-On-Loop](/reference/refactorings/python/useless-else-on-loop/) – Loop’s else clause is always executed - move code to same level as loop * [While-Guard-To-Condition](/reference/refactorings/python/while-guard-to-condition/) – Move a guard clause in a while statement’s body into its test * [Replace while with for](/reference/refactorings/python/while-to-for/) – Replaces a while loop with a counter by a for loop * [Yield from](/reference/refactorings/python/yield-from/) – Replaces yield as part of for loops with yield from ## JavaScript [Section titled “JavaScript”](#javascript) * [Assignment-Operator](/reference/refactorings/javascript/assignment-operator/) – Replace assignment with assignment operator * [Avoid-Function-Declarations-In-Blocks](/reference/refactorings/javascript/avoid-function-declarations-in-blocks/) – Avoid function declarations, favouring function assignment expressions, inside blocks. * [Avoid-Infinite-Loops](/reference/refactorings/javascript/avoid-infinite-loops/) – Avoid loops with missing or constant end conditions. * [Avoid-Jumping-In-Finally](/reference/refactorings/javascript/avoid-jumping-in-finally/) – Avoid the use of jump statements in `finally` blocks. * [Avoid-Using-Var](/reference/refactorings/javascript/avoid-using-var/) – Use `const` or `let` instead of `var`. * [Binary-Operator-Identity](/reference/refactorings/javascript/binary-operator-identity/) – Simplify binary operation * [Combine-Object-Destructuring](/reference/refactorings/javascript/combine-object-destructuring/) – Combine destructure assignments. * [Dont-Concatenate-String-Literals](/reference/refactorings/javascript/dont-concatenate-string-literals/) – Do not concatenate string literals * [Dont-Negate-Is-Instanceof-Operands](/reference/refactorings/javascript/dont-negate-is-instanceof-operands/) – `in` and `instanceof` have lower precedence than negation operators. * [Dont-Reassign-Caught-Exceptions](/reference/refactorings/javascript/dont-reassign-caught-exceptions/) – Don’t reassign the bound exception ${exc} * [Dont-Reassign-Foreach-Variables](/reference/refactorings/javascript/dont-reassign-foreach-variables/) – Don’t reassign the for-each variable ${var} * [Dont-Reassign-Parameters](/reference/refactorings/javascript/dont-reassign-parameters/) – Don’t reassign parameter - ${param} * [Dont-Self-Assign-Variables](/reference/refactorings/javascript/dont-self-assign-variables/) – Assigning a variable to itself has no effect. * [Dont-Shadow-Arguments](/reference/refactorings/javascript/dont-shadow-arguments/) – Don’t shadow `arguments`. * [Dont-Use-With](/reference/refactorings/javascript/dont-use-with/) – Avoid using `with` statements. * [Dont-Use-Wrappers-For-Builtins](/reference/refactorings/javascript/dont-use-wrappers-for-builtins/) – Don’t use `new` syntax with `String`, `Number` and `Boolean` objects * [Flatten-Nested-Try](/reference/refactorings/javascript/flatten-nested-try/) – Merge nested try-statement into a single try * [Flip-Comparison](/reference/refactorings/javascript/flip-comparison/) – Moves variables from the right side to the left side of comparisons * [Generators-Should-Yield](/reference/refactorings/javascript/generators-should-yield/) – Avoid writing generators that don’t `yield` any values. * [Inline-Immediately-Returned-Variable](/reference/refactorings/javascript/inline-immediately-returned-variable/) – Inline variable that is immediately returned * [Invert-Ternary](/reference/refactorings/javascript/invert-ternary/) – Invert ternary operator to remove negation * [Max-Min-Identity](/reference/refactorings/javascript/max-min-identity/) – Use max/min instead of if statement * [Merge-Else-If](/reference/refactorings/javascript/merge-else-if/) – Merge else clause’s nested if statement into `else if` * [Merge-Nested-Ifs](/reference/refactorings/javascript/merge-nested-ifs/) – Merge nested if conditions * [Misplaced-Break-Or-Continue](/reference/refactorings/javascript/misplaced-break-or-continue/) – This `break` or `continue` does not refer to any valid loop or statement. * [No-Eval](/reference/refactorings/javascript/no-eval/) – Never use `eval()` * [No-New-Function](/reference/refactorings/javascript/no-new-function/) – Never use the `Function` constructor. * [No-New-Symbol](/reference/refactorings/javascript/no-new-symbol/) – An instance of `Symbol` can only be created using `Symbol` as a function * [Only-Delete-Object-Properties](/reference/refactorings/javascript/only-delete-object-properties/) – Delete should only be used for object properties. * [Possible-Incorrect-Bitwise-Operator](/reference/refactorings/javascript/possible-incorrect-bitwise-operator/) – Flags possibly incorrect use of bitwise operators `|` and `&`. * [Remove-Redundant-Boolean](/reference/refactorings/javascript/remove-redundant-boolean/) – Remove unnecessary boolean value * [Remove-Redundant-If-Statement](/reference/refactorings/javascript/remove-redundant-if-statement/) – Remove an `if` statement where the condition is `true`. * [Remove-Redundant-Slice-Index](/reference/refactorings/javascript/remove-redundant-slice-index/) – Remove a redundant index from the array `.slice()` method. * [Remove-Unreachable-Code](/reference/refactorings/javascript/remove-unreachable-code/) – Remove unreachable code. * [Return-Outside-Function](/reference/refactorings/javascript/return-outside-function/) – Cannot use `return` outside a function definition * [Simplify-Ternary](/reference/refactorings/javascript/simplify-ternary/) – Avoid unneeded ternary statements * [Throw-New-Errors](/reference/refactorings/javascript/throw-new-errors/) – New errors should not be created without being thrown. * [Use-Array-Literal](/reference/refactorings/javascript/use-array-literal/) – Use literal syntax for array creation. * [Use-Object-Destructuring](/reference/refactorings/javascript/use-object-destructuring/) – Prefer object destructuring when accessing and using properties. * [Use-Ternary-Operator](/reference/refactorings/javascript/use-ternary-operator/) – Replace if statement with ternary operator * [While-Guard-To-Condition](/reference/refactorings/javascript/while-guard-to-condition/) – Move a guard clause in a while statement’s body into its test * [Yield-Outside-Generator](/reference/refactorings/javascript/yield-outside-generator/) – Cannot use `yield` outside a generator function # Assignment-Operator > Replace assignment with assignment operator #### Sourcery rule id: `assignment-operator` [Section titled “Sourcery rule id: assignment-operator”](#sourcery-rule-id-assignment-operator) #### Description [Section titled “Description”](#description) Replace assignment with assignment operator #### Before [Section titled “Before”](#before) ```javascript x = x + 1 ``` #### After [Section titled “After”](#after) ```javascript x += 1 ``` #### Explanation [Section titled “Explanation”](#explanation) The augmented assignment operators, such as `+=` are a more streamlined and easy to understand way to add values. Using them removes a little bit of repetition and makes the code slightly easier to understand. # Avoid-Function-Declarations-In-Blocks > Avoid function declarations, favouring function assignment expressions, inside blocks. #### Sourcery rule id: `avoid-function-declarations-in-blocks` [Section titled “Sourcery rule id: avoid-function-declarations-in-blocks”](#sourcery-rule-id-avoid-function-declarations-in-blocks) #### Description [Section titled “Description”](#description) Avoid function declarations, favouring function assignment expressions, inside blocks. #### Match [Section titled “Match”](#match) ```javascript if (x) { function foo() {} } ``` #### Explanation [Section titled “Explanation”](#explanation) Function declarations may be hoisted in Javascript, but the behaviour is inconsistent between browsers. Hoisting is generally confusing and should be avoided. Rather than using function declarations inside blocks, you should use function expressions, which create functions in-scope. # Avoid-Infinite-Loops > Avoid loops with missing or constant end conditions. #### Sourcery rule id: `avoid-infinite-loops` [Section titled “Sourcery rule id: avoid-infinite-loops”](#sourcery-rule-id-avoid-infinite-loops) #### Description [Section titled “Description”](#description) Avoid loops with missing or constant end conditions. #### Match [Section titled “Match”](#match) ```javascript for (;;) {console.log("forever")} ``` #### Explanation [Section titled “Explanation”](#explanation) Loops with a constant condition, or no terminator, can be useful in certain contexts, but it’s easy to forget to break out of them. This rule highlights loops which may never end. # Avoid-Jumping-In-Finally > Avoid the use of jump statements in `finally` blocks. #### Sourcery rule id: `avoid-jumping-in-finally` [Section titled “Sourcery rule id: avoid-jumping-in-finally”](#sourcery-rule-id-avoid-jumping-in-finally) #### Description [Section titled “Description”](#description) Avoid the use of jump statements in `finally` blocks. #### Match [Section titled “Match”](#match) ```javascript try { stuff(); } finally { for (thing of things) { return; } } ``` #### Explanation [Section titled “Explanation”](#explanation) Returning results or throwing exceptions in `finally` blocks (of `try..catch..finally`) suppresses propagation of unhandled exceptions. The `return`, `break`, `throw`, and `continue` statements should therefore not be used within a finally block. # Avoid-Using-Var > Use `const` or `let` instead of `var`. #### Sourcery rule id: `avoid-using-var` [Section titled “Sourcery rule id: avoid-using-var”](#sourcery-rule-id-avoid-using-var) #### Description [Section titled “Description”](#description) Use `const` or `let` instead of `var`. #### Match [Section titled “Match”](#match) ```javascript var a = 1 ``` #### Explanation [Section titled “Explanation”](#explanation) `const` is preferred as it ensures you cannot reassign references (which can lead to buggy and confusing code). `let` may be used if you need to reassign references - it’s preferred to `var` because it is block- rather than function-scoped. From the [Airbnb JavaScript Style Guide](https://airbnb.io/javascript/#references--prefer-const) # Binary-Operator-Identity > Simplify binary operation #### Sourcery rule id: `binary-operator-identity` [Section titled “Sourcery rule id: binary-operator-identity”](#sourcery-rule-id-binary-operator-identity) #### Description [Section titled “Description”](#description) Simplify binary operation #### Before [Section titled “Before”](#before) ```javascript 2 - 2 ``` #### After [Section titled “After”](#after) ```javascript 0 ``` #### Explanation [Section titled “Explanation”](#explanation) Some identities can be applied to simplify certain binary operations. For example writing `x - x` is a complicated way of saying `0`. # Combine-Object-Destructuring > Combine destructure assignments. #### Sourcery rule id: `combine-object-destructuring` [Section titled “Sourcery rule id: combine-object-destructuring”](#sourcery-rule-id-combine-object-destructuring) #### Description [Section titled “Description”](#description) Combine destructure assignments. #### Before [Section titled “Before”](#before) ```javascript const {firstName} = user; const {lastName} = user; ``` #### After [Section titled “After”](#after) ```javascript const {firstName, lastName} = user; ``` #### Explanation [Section titled “Explanation”](#explanation) Object destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct. From the [Airbnb Javascript Style Guide](https://airbnb.io/javascript/#destructuring--object) # Dont-Concatenate-String-Literals > Do not concatenate string literals #### Sourcery rule id: `dont-concatenate-string-literals` [Section titled “Sourcery rule id: dont-concatenate-string-literals”](#sourcery-rule-id-dont-concatenate-string-literals) #### Description [Section titled “Description”](#description) Do not concatenate string literals #### Match [Section titled “Match”](#match) ```javascript const errorMessage = 'This is a super long error that was thrown because \ of Batman. When you stop to think about how Batman had anything to do \ with this, you would get nowhere \ fast.'; ``` #### Explanation [Section titled “Explanation”](#explanation) String literals should not be written across multiple lines because this makes them harder to read, maintain and search for. Instead, let them be longer than the maximum line length. From the [Airbnb JavaScript Style Guide](https://airbnb.io/javascript/#strings--line-length) # Dont-Negate-Is-Instanceof-Operands > `in` and `instanceof` have lower precedence than negation operators. #### Sourcery rule id: `dont-negate-is-instanceof-operands` [Section titled “Sourcery rule id: dont-negate-is-instanceof-operands”](#sourcery-rule-id-dont-negate-is-instanceof-operands) #### Description [Section titled “Description”](#description) `in` and `instanceof` have lower precedence than negation operators. #### Match [Section titled “Match”](#match) ```javascript if (!"prop" in myObj) { // Noncompliant; "in" operator is checking property "false" doTheThing(); // this block will be never executed } ``` #### Explanation [Section titled “Explanation”](#explanation) Since negation is applied first, checking `!key in dict` checks whether `!key` is in dict rather than whether the `key` is not in the `dict`, and similarly with `instanceof`. This rule warns when the left operand of `is` or `instanceof` is negated. # Dont-Reassign-Caught-Exceptions > Don't reassign the bound exception ${exc} #### Sourcery rule id: `dont-reassign-caught-exceptions` [Section titled “Sourcery rule id: dont-reassign-caught-exceptions”](#sourcery-rule-id-dont-reassign-caught-exceptions) #### Description [Section titled “Description”](#description) Don’t reassign the bound exception ${exc} #### Match [Section titled “Match”](#match) ```javascript try { whatever(); } catch (e) { e = new Error('something'); } ``` #### Explanation [Section titled “Explanation”](#explanation) Reassigning exceptions may lead to unexpected behaviour, and the variable should be treated as if it were constant. # Dont-Reassign-Foreach-Variables > Don't reassign the for-each variable ${var} #### Sourcery rule id: `dont-reassign-foreach-variables` [Section titled “Sourcery rule id: dont-reassign-foreach-variables”](#sourcery-rule-id-dont-reassign-foreach-variables) #### Description [Section titled “Description”](#description) Don’t reassign the for-each variable ${var} #### Match [Section titled “Match”](#match) ```javascript for (var str of strings) { str = ""; } ``` #### Explanation [Section titled “Explanation”](#explanation) Reassigning the iteratee in a for-of loop may lead to unexpected behaviour, and the variable should be treated as if it were constant. # Dont-Reassign-Parameters > Don't reassign parameter - ${param} #### Sourcery rule id: `dont-reassign-parameters` [Section titled “Sourcery rule id: dont-reassign-parameters”](#sourcery-rule-id-dont-reassign-parameters) #### Description [Section titled “Description”](#description) Don’t reassign parameter - ${param} #### Match [Section titled “Match”](#match) ```javascript function foo(name, options, arguments) { name = 1; } ``` #### Explanation [Section titled “Explanation”](#explanation) Reassigning parameters can lead to unexpected behavior, especially when accessing the arguments object. It can also cause optimization issues, especially in V8. From the [Airbnb JavaScript Style Guide](https://airbnb.io/javascript/#functions--reassign-params) # Dont-Self-Assign-Variables > Assigning a variable to itself has no effect. #### Sourcery rule id: `dont-self-assign-variables` [Section titled “Sourcery rule id: dont-self-assign-variables”](#sourcery-rule-id-dont-self-assign-variables) #### Description [Section titled “Description”](#description) Assigning a variable to itself has no effect. #### Match [Section titled “Match”](#match) ```javascript a = a ``` #### Explanation [Section titled “Explanation”](#explanation) Assigning a variable to itself has no effect, and is therefore either redundant or a mistake. # Dont-Shadow-Arguments > Don't shadow `arguments`. #### Sourcery rule id: `dont-shadow-arguments` [Section titled “Sourcery rule id: dont-shadow-arguments”](#sourcery-rule-id-dont-shadow-arguments) #### Description [Section titled “Description”](#description) Don’t shadow `arguments`. #### Match [Section titled “Match”](#match) ```javascript function foo(name, options, arguments) { something(); } ``` #### Explanation [Section titled “Explanation”](#explanation) Never name a parameter `arguments`. This will take precedence over the arguments object that is given to every function scope. From the [Airbnb JavaScript Style Guide](https://airbnb.io/javascript/#functions--arguments-shadow) # Dont-Use-With > Avoid using `with` statements. #### Sourcery rule id: `dont-use-with` [Section titled “Sourcery rule id: dont-use-with”](#sourcery-rule-id-dont-use-with) #### Description [Section titled “Description”](#description) Avoid using `with` statements. #### Match [Section titled “Match”](#match) ```javascript with (thing) { y = 5; x = 2; } ``` #### Explanation [Section titled “Explanation”](#explanation) The `with` statement is deprecated, and may cease to work in browsers at any time. `with` gives short-hand access to an object’s properties, but the scope is inconsistent and it’s better just to use normal attribute setting. # Dont-Use-Wrappers-For-Builtins > Don't use `new` syntax with `String`, `Number` and `Boolean` objects #### Sourcery rule id: `dont-use-wrappers-for-builtins` [Section titled “Sourcery rule id: dont-use-wrappers-for-builtins”](#sourcery-rule-id-dont-use-wrappers-for-builtins) #### Description [Section titled “Description”](#description) Don’t use `new` syntax with `String`, `Number` and `Boolean` objects #### Before [Section titled “Before”](#before) ```javascript const items = new Boolean(true); ``` #### After [Section titled “After”](#after) ```javascript const items = true; ``` #### Explanation [Section titled “Explanation”](#explanation) Although possible, there aren’t any good reasons to use these primitive wrappers as constructors. They tend to confuse other developers more than anything else because they seem like they should act as primitives, but they do not. If you wish to convert a value to another type, use these without the `new` syntax. From [ESLint](https://eslint.org/docs/latest/rules/no-new-wrappers) # Flatten-Nested-Try > Merge nested try-statement into a single try #### Sourcery rule id: `flatten-nested-try` [Section titled “Sourcery rule id: flatten-nested-try”](#sourcery-rule-id-flatten-nested-try) #### Description [Section titled “Description”](#description) Merge nested try-statement into a single try #### Before [Section titled “Before”](#before) ```javascript try { try { something() } catch (e) { catching() } } finally { finalling() } ``` #### After [Section titled “After”](#after) ```javascript try { something() } catch (e) { catching() } finally { finalling() } ``` #### Explanation [Section titled “Explanation”](#explanation) Flattening try…catch statements nested within a try…finally generates equivalent code that is easier to read and expand upon. # Flip-Comparison > Moves variables from the right side to the left side of comparisons #### Sourcery rule id: `flip-comparison` [Section titled “Sourcery rule id: flip-comparison”](#sourcery-rule-id-flip-comparison) #### Description [Section titled “Description”](#description) Moves variables from the right side to the left side of comparisons #### Before [Section titled “Before”](#before) ```javascript 1 == a ``` #### After [Section titled “After”](#after) ```javascript a == 1 ``` #### Explanation [Section titled “Explanation”](#explanation) It is a coding convention that when doing comparisons to a numeric constant, the number should be on the right and the variable on the left. Having things be consistent in this way makes the code easier to read. # Generators-Should-Yield > Avoid writing generators that don't `yield` any values. #### Sourcery rule id: `generators-should-yield` [Section titled “Sourcery rule id: generators-should-yield”](#sourcery-rule-id-generators-should-yield) #### Description [Section titled “Description”](#description) Avoid writing generators that don’t `yield` any values. #### Match [Section titled “Match”](#match) ```javascript function* wrongGen(x) { let val = 0; val += x; } ``` #### Explanation [Section titled “Explanation”](#explanation) Generators without `yield` statements are confusing, since they defy a reader’s expectations. # Inline-Immediately-Returned-Variable > Inline variable that is immediately returned #### Sourcery rule id: `inline-immediately-returned-variable` [Section titled “Sourcery rule id: inline-immediately-returned-variable”](#sourcery-rule-id-inline-immediately-returned-variable) #### Description [Section titled “Description”](#description) Inline variable that is immediately returned #### Before [Section titled “Before”](#before) ```javascript const var = 1; return var; ``` #### After [Section titled “After”](#after) ```javascript return 1; ``` #### Explanation [Section titled “Explanation”](#explanation) Something that we often see in people’s code is assigning to a result variable and then immediately returning it. Returning the result directly shortens the code and removes an unnecessary variable, reducing the mental load of reading the function. Where intermediate variables can be useful is if they then get used as a parameter or a condition, and the name can act like a comment on what the variable represents. In the case where you’re returning it from a function, the function name is there to tell you what the result is, so the variable name is unnecessary. # Invert-Ternary > Invert ternary operator to remove negation #### Sourcery rule id: `invert-ternary` [Section titled “Sourcery rule id: invert-ternary”](#sourcery-rule-id-invert-ternary) #### Description [Section titled “Description”](#description) Invert ternary operator to remove negation #### Before [Section titled “Before”](#before) ```javascript const beverage = !too_young ? 'Beer' : 'Juice'; ``` #### After [Section titled “After”](#after) ```javascript const beverage = too_young ? 'Juice': 'Beer'; ``` #### Explanation [Section titled “Explanation”](#explanation) Negated conditions are more difficult to read than positive ones, so it is best to avoid them where we can. By inverting the ternary condition and swapping the expressions we can simplify the code. # Max-Min-Identity > Use max/min instead of if statement #### Sourcery rule id: `max-min-identity` [Section titled “Sourcery rule id: max-min-identity”](#sourcery-rule-id-max-min-identity) #### Description [Section titled “Description”](#description) Use max/min instead of if statement #### Before [Section titled “Before”](#before) ```javascript if (a < b) { c = a } else { c = b } ``` #### After [Section titled “After”](#after) ```javascript c = Math.min(a, b) ``` #### Explanation [Section titled “Explanation”](#explanation) We often need to work out the smallest or largest of two values, and the most readable way to do this is to use the built-in `min` and `max` functions. This results in a shorter and clearer way to achieve the same result. # Merge-Else-If > Merge else clause's nested if statement into `else if` #### Sourcery rule id: `merge-else-if` [Section titled “Sourcery rule id: merge-else-if”](#sourcery-rule-id-merge-else-if) #### Description [Section titled “Description”](#description) Merge else clause’s nested if statement into `else if` #### Before [Section titled “Before”](#before) ```javascript if (response.status == 200) { return response.data } else { if (response.status == 404) { return "Not Found" } else if (response.status == 403) { return "Forbidden" } else { return "Error" } } ``` #### After [Section titled “After”](#after) ```javascript if (response.status == 200) { return response.data } else if (response.status == 404) { return "Not Found" } else if (response.status == 403) { return "Forbidden" } else { return "Error" } ``` #### Explanation [Section titled “Explanation”](#explanation) Flattening if statements nested within else clauses generates code that is easier to read and expand upon. # Merge-Nested-Ifs > Merge nested if conditions #### Sourcery rule id: `merge-nested-ifs` [Section titled “Sourcery rule id: merge-nested-ifs”](#sourcery-rule-id-merge-nested-ifs) #### Description [Section titled “Description”](#description) Merge nested if conditions #### Before [Section titled “Before”](#before) ```javascript if (a && b) { if (c) { something(); d = other_thing; } } ``` #### After [Section titled “After”](#after) ```javascript if (a && b && c) { something(); d = other_thing; } ``` #### Explanation [Section titled “Explanation”](#explanation) Reading deeply nested conditional code is confusing, since you have to keep track of which conditions relate to which levels. We therefore strive to reduce nesting where possible, and the situation where two `if` conditions can be combined using `and` is an easy win. # Misplaced-Break-Or-Continue > This `break` or `continue` does not refer to any valid loop or statement. #### Sourcery rule id: `misplaced-break-or-continue` [Section titled “Sourcery rule id: misplaced-break-or-continue”](#sourcery-rule-id-misplaced-break-or-continue) #### Description [Section titled “Description”](#description) This `break` or `continue` does not refer to any valid loop or statement. #### Match [Section titled “Match”](#match) ```javascript break; ``` #### Explanation [Section titled “Explanation”](#explanation) A `break` or `continue` cannot be outside a suitable loop or block statement. # No-Eval > Never use `eval()` #### Sourcery rule id: `no-eval` [Section titled “Sourcery rule id: no-eval”](#sourcery-rule-id-no-eval) #### Description [Section titled “Description”](#description) Never use `eval()` #### Match [Section titled “Match”](#match) ```javascript eval("throw 1") ``` #### Explanation [Section titled “Explanation”](#explanation) Using `eval` opens your code up to serious security risks - it executes code with the privileges of the caller, leading to all sorts of possible attacks. It can also cause performance issues, preventing javascript minifiers and interpreters from making many efficiency gains. # No-New-Function > Never use the `Function` constructor. #### Sourcery rule id: `no-new-function` [Section titled “Sourcery rule id: no-new-function”](#sourcery-rule-id-no-new-function) #### Description [Section titled “Description”](#description) Never use the `Function` constructor. #### Match [Section titled “Match”](#match) ```javascript var subtract = Function('a', 'b', 'return a - b'); ``` #### Explanation [Section titled “Explanation”](#explanation) Creating a function using the `Function` constructor evaluates a string similarly to `eval()` which may open up vulnerabilities. From the [Airbnb JavaScript Style Guide](https://airbnb.io/javascript/#functions--constructor) # No-New-Symbol > An instance of `Symbol` can only be created using `Symbol` as a function #### Sourcery rule id: `no-new-symbol` [Section titled “Sourcery rule id: no-new-symbol”](#sourcery-rule-id-no-new-symbol) #### Description [Section titled “Description”](#description) An instance of `Symbol` can only be created using `Symbol` as a function #### Before [Section titled “Before”](#before) ```javascript new Symbol("abc") ``` #### After [Section titled “After”](#after) ```javascript Symbol("abc") ``` #### Explanation [Section titled “Explanation”](#explanation) `Symbol` is not intended to be used with the `new` operator, but called as a function. # Only-Delete-Object-Properties > Delete should only be used for object properties. #### Sourcery rule id: `only-delete-object-properties` [Section titled “Sourcery rule id: only-delete-object-properties”](#sourcery-rule-id-only-delete-object-properties) #### Description [Section titled “Description”](#description) Delete should only be used for object properties. #### Match [Section titled “Match”](#match) ```javascript var q = 1; delete q; ``` #### Explanation [Section titled “Explanation”](#explanation) The `delete` operator is intended for use in deleting the properties of objects, and does not have meaningful behaviour otherwise. See [the MDN docs for delete.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete) # Possible-Incorrect-Bitwise-Operator > Flags possibly incorrect use of bitwise operators `|` and `&`. #### Sourcery rule id: `possible-incorrect-bitwise-operator` [Section titled “Sourcery rule id: possible-incorrect-bitwise-operator”](#sourcery-rule-id-possible-incorrect-bitwise-operator) #### Description [Section titled “Description”](#description) Flags possibly incorrect use of bitwise operators `|` and `&`. #### Match [Section titled “Match”](#match) ```javascript if (x & y) {} ``` #### Explanation [Section titled “Explanation”](#explanation) Although bitwise operators are valid in boolean contexts, it’s more likely to be a programming error, as it’s easy to mix up `&` with `&&` and `|` with `||`. # Remove-Redundant-Boolean > Remove unnecessary boolean value #### Sourcery rule id: `remove-redundant-boolean` [Section titled “Sourcery rule id: remove-redundant-boolean”](#sourcery-rule-id-remove-redundant-boolean) #### Description [Section titled “Description”](#description) Remove unnecessary boolean value #### Before [Section titled “Before”](#before) ```javascript while (first_condition && true && second_condition) { do_something(); } ``` #### After [Section titled “After”](#after) ```javascript while (first_condition && second_condition) { do_something(); } ``` #### Explanation [Section titled “Explanation”](#explanation) This rule removes unnecessary `true` values from `&&` boolean operators, since `something && true` has exactly the same meaning as `something` # Remove-Redundant-If-Statement > Remove an `if` statement where the condition is `true`. #### Sourcery rule id: `remove-redundant-if-statement` [Section titled “Sourcery rule id: remove-redundant-if-statement”](#sourcery-rule-id-remove-redundant-if-statement) #### Description [Section titled “Description”](#description) Remove an `if` statement where the condition is `true`. #### Before [Section titled “Before”](#before) ```javascript if (true) { console.log("great!") } ``` #### After [Section titled “After”](#after) ```javascript console.log("great!") ``` #### Explanation [Section titled “Explanation”](#explanation) If the condition of an `if` statement is `true`, its body will always be executed. Conversely, if the condition is always `false`, then its body will never be executed. In the first case, we can replace the `if` statement with its body, and in the second case we can remove it altogether. # Remove-Redundant-Slice-Index > Remove a redundant index from the array `.slice()` method. #### Sourcery rule id: `remove-redundant-slice-index` [Section titled “Sourcery rule id: remove-redundant-slice-index”](#sourcery-rule-id-remove-redundant-slice-index) #### Description [Section titled “Description”](#description) Remove a redundant index from the array `.slice()` method. #### Before [Section titled “Before”](#before) ```javascript const toEnd = numbers.slice(3, numbers.length) ``` #### After [Section titled “After”](#after) ```javascript const toEnd = numbers.slice(3) ``` #### Explanation [Section titled “Explanation”](#explanation) Parameters in the array prototype `.slice(start, end)` method are optional, and by convention you should only use the necessary ones, to simplify readability. The `end` is not necessary if you want to slice to the end of the array. The `start` is additionally not necessary if you want to slice from the start of the array. # Remove-Unreachable-Code > Remove unreachable code. #### Sourcery rule id: `remove-unreachable-code` [Section titled “Sourcery rule id: remove-unreachable-code”](#sourcery-rule-id-remove-unreachable-code) #### Description [Section titled “Description”](#description) Remove unreachable code. #### Before [Section titled “Before”](#before) ```javascript if (true) { return; do_something(); } ``` #### After [Section titled “After”](#after) ```javascript if (true) { return; } ``` #### Explanation [Section titled “Explanation”](#explanation) Statements after a `return`, `break`, `continue` or `throw` will never be executed. Leaving them in the code confuses the reader, who may believe that these statements have some effect. They should therefore be removed. # Return-Outside-Function > Cannot use `return` outside a function definition #### Sourcery rule id: `return-outside-function` [Section titled “Sourcery rule id: return-outside-function”](#sourcery-rule-id-return-outside-function) #### Description [Section titled “Description”](#description) Cannot use `return` outside a function definition #### Match [Section titled “Match”](#match) ```javascript return ``` #### Explanation [Section titled “Explanation”](#explanation) The `return` statement ends function execution and specifies a value to be returned to the function caller. If `return` is used outside a function definition it will throw a `SyntaxError`. However in a node.js script a `return` statement is allowed outside a function as node.js scripts are wrapped in a module function - # Simplify-Ternary > Avoid unneeded ternary statements #### Sourcery rule id: `simplify-ternary` [Section titled “Sourcery rule id: simplify-ternary”](#sourcery-rule-id-simplify-ternary) #### Description [Section titled “Description”](#description) Avoid unneeded ternary statements #### Before [Section titled “Before”](#before) ```javascript const foo = a ? a : b; ``` #### After [Section titled “After”](#after) ```javascript const foo = a || b; ``` #### Explanation [Section titled “Explanation”](#explanation) It is possible to simplify certain ternary statements into either use of an `||` or `!`. This makes the code easier to read, since there is no conditional logic. # Throw-New-Errors > New errors should not be created without being thrown. #### Sourcery rule id: `throw-new-errors` [Section titled “Sourcery rule id: throw-new-errors”](#sourcery-rule-id-throw-new-errors) #### Description [Section titled “Description”](#description) New errors should not be created without being thrown. #### Match [Section titled “Match”](#match) ```javascript new Error("whatever"); ``` #### Explanation [Section titled “Explanation”](#explanation) Although it is valid to construct an error without throwing it, this behaviour is likely unintended: errors must be explicitly thrown. # Use-Array-Literal > Use literal syntax for array creation. #### Sourcery rule id: `use-array-literal` [Section titled “Sourcery rule id: use-array-literal”](#sourcery-rule-id-use-array-literal) #### Description [Section titled “Description”](#description) Use literal syntax for array creation. #### Before [Section titled “Before”](#before) ```javascript const items = new Array(); ``` #### After [Section titled “After”](#after) ```javascript const items = []; ``` #### Explanation [Section titled “Explanation”](#explanation) Use of the Array constructor to construct a new array is generally discouraged in favor of array literal notation because of the single-argument pitfall and because the Array global may be redefined. The exception is when the Array constructor is used to intentionally create sparse arrays of a specified size by giving the constructor a single numeric argument. From [ESLint](https://eslint.org/docs/latest/rules/no-array-constructor.html) From the [Airbnb JavaScript Style Guide](https://airbnb.io/javascript/#arrays--literals) # Use-Object-Destructuring > Prefer object destructuring when accessing and using properties. #### Sourcery rule id: `use-object-destructuring` [Section titled “Sourcery rule id: use-object-destructuring”](#sourcery-rule-id-use-object-destructuring) #### Description [Section titled “Description”](#description) Prefer object destructuring when accessing and using properties. #### Before [Section titled “Before”](#before) ```javascript const firstName = user.firstName; ``` #### After [Section titled “After”](#after) ```javascript const {firstName} = user; ``` #### Explanation [Section titled “Explanation”](#explanation) Object destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct. From the [Airbnb Javascript Style Guide](https://airbnb.io/javascript/#destructuring--object) # Use-Ternary-Operator > Replace if statement with ternary operator #### Sourcery rule id: `use-ternary-operator` [Section titled “Sourcery rule id: use-ternary-operator”](#sourcery-rule-id-use-ternary-operator) #### Description [Section titled “Description”](#description) Replace if statement with ternary operator #### Before [Section titled “Before”](#before) ```javascript if (cond) { var x = 1 } else { var x = 2 } ``` #### After [Section titled “After”](#after) ```javascript var x = cond ? 1 : 2; ``` #### Explanation [Section titled “Explanation”](#explanation) The ternary operator is a simpler and more elegant way to write conditional assignments to a variable than the long form. # While-Guard-To-Condition > Move a guard clause in a while statement's body into its test #### Sourcery rule id: `while-guard-to-condition` [Section titled “Sourcery rule id: while-guard-to-condition”](#sourcery-rule-id-while-guard-to-condition) #### Description [Section titled “Description”](#description) Move a guard clause in a while statement’s body into its test #### Before [Section titled “Before”](#before) ```javascript while (true) { if (condition) { break; } do_some_stuff(); } ``` #### After [Section titled “After”](#after) ```javascript while (true && !condition) { do_some_stuff(); } ``` #### Explanation [Section titled “Explanation”](#explanation) A guard clause with a `break` that is the first thing in a `while` body is equivalent to having the condition contained in the `while` itself. # Yield-Outside-Generator > Cannot use `yield` outside a generator function #### Sourcery rule id: `yield-outside-generator` [Section titled “Sourcery rule id: yield-outside-generator”](#sourcery-rule-id-yield-outside-generator) #### Description [Section titled “Description”](#description) Cannot use `yield` outside a generator function #### Match [Section titled “Match”](#match) ```javascript yield ``` #### Explanation [Section titled “Explanation”](#explanation) The `yield` keyword is used to pause and resume a generator function. If `yield` is used outside a generator function or method it will throw a `SyntaxError` # Assign If Expression > Replaces conditional assignment to a variable with an if expression #### Sourcery refactoring id: `assign-if-exp` [Section titled “Sourcery refactoring id: assign-if-exp”](#sourcery-refactoring-id-assign-if-exp) #### Description: [Section titled “Description:”](#description) Replaces conditional assignment to a variable with an if expression #### Before: [Section titled “Before:”](#before) ```python if condition: x = 1 else: x = 2 ``` #### After: [Section titled “After:”](#after) ```python x = 1 if condition else 2 ``` #### Explanation: [Section titled “Explanation:”](#explanation) Python’s conditional expression syntax is its version of the ternary operator. Using this syntax is definitely more concise, but it is one of the more controversial refactorings (along with list comprehensions). Some coders dislike these expressions and find them slightly harder to parse than writing them out fully. Our view is that this is a definite improvement where the conditional expression is: * short and fits on one line * is not complex (no nested expressions or long chains of booleans) Once the change is made there’s only one statement where `x` is defined as opposed to having to read two statements plus the if-else lines. Similarly to the comprehension example, when we’re scanning the code we usually won’t need to know the details of how `x` gets assigned, and can just see that it’s being assigned and move on. # Augmented Assign > Replaces assignments with augmented assignments #### Sourcery refactoring id: `aug-assign` [Section titled “Sourcery refactoring id: aug-assign”](#sourcery-refactoring-id-aug-assign) #### Description: [Section titled “Description:”](#description) Replaces assignments with augmented assignments #### Before: [Section titled “Before:”](#before) ```python count = count + other_value ``` #### After: [Section titled “After:”](#after) ```python count += other_value ``` #### Explanation: [Section titled “Explanation:”](#explanation) Reading the changed code is a bit shorter and clearer - we don’t need to think about the `count` variable twice. Other operators that can be used include `-=`, `*=`, `/=` and `**=`. The type you’re assigning to has to have the appropriate operator defined, so Sourcery will only suggestion this change where it can determine the type and it is suitable. For instance `numpy` arrays do not support the `/=` operation. # Avoid Builtin Shadow > Don't assign to builtin variables, such as list. #### Sourcery comment id: `avoid-builtin-shadow` [Section titled “Sourcery comment id: avoid-builtin-shadow”](#sourcery-comment-id-avoid-builtin-shadow) #### Description [Section titled “Description”](#description) Don’t assign to builtin variables, such as `list`. #### Before [Section titled “Before”](#before) ```python list = [1, 1, 2, 3, 5, 8] ``` #### Possible Fix [Section titled “Possible Fix”](#possible-fix) ```python fibonacci = [1, 1, 2, 3, 5, 8] ``` #### Explanation [Section titled “Explanation”](#explanation) Python has a number of `builtin` variables: functions and constants that form a part of the language, such as `list`, `getattr`, and `type` (See ). It is valid, in the language, to re-bind such variables: ```python list = [1, 2, 3] ``` However, this is considered poor practice. * It will confuse other developers. * It will confuse syntax highlighters and linters. * It means you can no longer use that builtin for its original purpose. How can you solve this? Rename the variable something more specific, such as `integers`. In a pinch, `my_list` and similar names are colloquially-recognized placeholders. # Aware datetime For UTC > For getting the current time in UTC, use an aware datetime object with the timezone explicitly set to UTC. #### Sourcery suggestion id: `aware-datetime-for-utc` [Section titled “Sourcery suggestion id: aware-datetime-for-utc”](#sourcery-suggestion-id-aware-datetime-for-utc) #### Description [Section titled “Description”](#description) For getting the current time in UTC, use an aware datetime object with the timezone explicitly set to UTC. #### Before [Section titled “Before”](#before) ```python from datetime import datetime this_moment_utc = datetime.utcnow() ``` #### After [Section titled “After”](#after) ```python from datetime import datetime, timezone this_moment_utc = datetime.now(timezone.utc) ``` #### Explanation [Section titled “Explanation”](#explanation) The [documentation](https://docs.python.org/3/library/datetime.html#datetime.datetime.utcnow) for the `datetime.datetime.utcnow()` function provides an excellent explanation why we should prefer to use aware datetime objects: > Warning: Because naive datetime objects are treated by many datetime methods as local times, it is preferred to use aware datetimes to represent times in UTC. As such, the recommended way to create an object representing the current time in UTC is by calling datetime.now(timezone.utc). # Binary Operator Identity > Replaces binary operations between a value and itself with known identities: #### Sourcery refactoring id: `bin-op-identity` [Section titled “Sourcery refactoring id: bin-op-identity”](#sourcery-refactoring-id-bin-op-identity) #### Description: [Section titled “Description:”](#description) Replaces binary operations between a value and itself with known identities: #### Changes: [Section titled “Changes:”](#changes) * `x | x` => `x` * `x & x` => `x` * `x ^ x` => `0` * `x - x` => `0` * `x / x` => `1` * `x // x` => `1` * `x % x` => `0` #### Explanation: [Section titled “Explanation:”](#explanation) These changes simplify the code, making it easier to understand what is going on. # Boolean If Expression Identity > Simplifies boolean if expressions by removing unnecessary explicit references to True or False states #### Sourcery refactoring id: `boolean-if-exp-identity` [Section titled “Sourcery refactoring id: boolean-if-exp-identity”](#sourcery-refactoring-id-boolean-if-exp-identity) #### Description: [Section titled “Description:”](#description) Simplifies boolean if expressions by removing unnecessary explicit references to `True` or `False` states #### Before: [Section titled “Before:”](#before) ```python some_var = True if some_boolean_expression else False ``` #### After: [Section titled “After:”](#after) ```python some_var = some_boolean_expression ``` #### Explanation: [Section titled “Explanation:”](#explanation) Rather than using an if-expression to evaluate a boolean, you can just use it directly. This is shorter and clearer - if expressions of this form take a while to mentally parse. Where Sourcery cannot determine if the expression is a boolean it will wrap it in a call to`bool()`. # Break-Or-Continue-Outside-Loop > Remove break or continue statement found outside for or while loop #### Sourcery rule id: `break-or-continue-outside-loop` [Section titled “Sourcery rule id: break-or-continue-outside-loop”](#sourcery-rule-id-break-or-continue-outside-loop) #### Description [Section titled “Description”](#description) Remove break or continue statement found outside for or while loop #### Before [Section titled “Before”](#before) ```python def handle_invalid_number(numbers): for number in numbers: if is_valid(number): continue break handle(number) ``` #### After [Section titled “After”](#after) ```python def handle_invalid_number(numbers): for number in numbers: if is_valid(number): continue handle(number) ``` #### Explanation [Section titled “Explanation”](#explanation) The [`break`](https://docs.python.org/3/reference/simple_stmts.html#break) and [`continue`](https://docs.python.org/3/reference/simple_stmts.html#the-continue-statement) are used to control the behaviour of [`for`](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement) and [`while`](https://docs.python.org/3/reference/compound_stmts.html#the-while-statement) loops. Using them outside those loops is a [`SyntaxError`](https://docs.python.org/3/library/exceptions.html#SyntaxError). This error may be very easy to pass through since sometimes it is caused by a wrong indentation. # Chain Compares > Combine two compares on same value into a chained compare #### Sourcery refactoring id: `chain-compares` [Section titled “Sourcery refactoring id: chain-compares”](#sourcery-refactoring-id-chain-compares) #### Description [Section titled “Description”](#description) Combine two compares on same value into a chained compare #### Before [Section titled “Before”](#before) ```python if 1 < b and b < 3: print("b is between 1 and 3") ``` #### After [Section titled “After”](#after) ```python if 1 < b < 3: print("b is between 1 and 3") ``` #### Explanation [Section titled “Explanation”](#explanation) Two compare operations on the same value can be chained together. This is described in detail in the [Python comparison docs](https://docs.python.org/3/reference/expressions.html#comparisons). Chaining compares is how they are written in mathematics and makes them easier to read. # Class Extract Method > Extracts duplicate pieces of code in different functions in a class into their own methods #### Sourcery refactoring id: `class-extract-method` [Section titled “Sourcery refactoring id: class-extract-method”](#sourcery-refactoring-id-class-extract-method) #### Description: [Section titled “Description:”](#description) Extracts duplicate pieces of code in different functions in a class into their own methods **Requires Sourcery Pro** #### Before: [Section titled “Before:”](#before) ```python class ExtractionExample: def method_one(self): self.speed_slider = Scale( self.parent, from_=1, to=10, orient=HORIZONTAL, label="Speed" ) self.speed_slider.pack() self.speed_slider.set(DEFAULT_SPEED) self.speed_slider.configure(background="white") def method_two(self): self.force_slider = Scale( self.parent, from_=1, to=10, orient=HORIZONTAL, label="Force" ) self.force_slider.pack() self.force_slider.set(DEFAULT_FORCE) self.force_slider.configure(background="white") ``` #### After: [Section titled “After:”](#after) ```python class ExtractionExample: def method_one(self): self.speed_slider = self._extracted_from_method_two_2("Speed", DEFAULT_SPEED) def method_two(self): self.force_slider = self._extracted_from_method_two_2("Force", DEFAULT_FORCE) # TODO Rename this here and in `method_one` and `method_two` def _extracted_from_method_two_2(self, label, arg1): result = Scale(self.parent, from_=1, to=10, orient=HORIZONTAL, label=label) result.pack() result.set(arg1) result.configure(background="white") return result ``` #### Explanation: [Section titled “Explanation:”](#explanation) Do not Repeat Yourself (DRY) is an important tenet of writing clean, maintainable code. Duplicated code bloats the code base, making it harder to read and understand. It often also leads to bugs. Where changes are made in only some of the duplicated areas unintended behaviour will often arise. One of the main ways to remove duplication is to extract the common areas into another method and call that. Sourcery can detect areas of duplicate code that are in different functions in the same class and extract them. It is recommended that you then rename the extracted function and any arguments that have not been automatically named. In the above example a suitable method name would be `create_slider`, and `arg1` would be `default_value`. Note that this refactoring only runs when a file is opened or saved - it does not get triggered whenever you change the code in a file. # Class Method First Parameter Should Be `cls` > Suggests that class methods should rename their first parameter to cls. #### Sourcery refactoring id: `class-method-first-arg-name` [Section titled “Sourcery refactoring id: class-method-first-arg-name”](#sourcery-refactoring-id-class-method-first-arg-name) #### Description: [Section titled “Description:”](#description) Suggests that class methods should rename their first parameter to `cls`. #### Before: [Section titled “Before:”](#before) ```python class Chicken: @classmethod def from_egg(new, egg): with egg.hatch() as chick: return new(chick) ``` #### After: [Section titled “After:”](#after) ```python class Chicken: @classmethod def from_egg(cls, egg): with egg.hatch() as chick: return cls(chick) ``` #### Explanation: [Section titled “Explanation:”](#explanation) This is both a very strong convention in Python and an explicit entry in [PEP-8](https://peps.python.org/pep-0008/#function-and-method-arguments). Adhering to it improves code consistency and makes it easier for developers to understand the intention of your code. See also [`instance-method-first-arg-name`](/reference/refactorings/python/instance-method-first-arg-name/) # Collection to Comprehension > Use list, set or dictionary comprehensions directly instead of calling list(), dict() or set() #### Sourcery refactoring id: `collection-builtin-to-comprehension` [Section titled “Sourcery refactoring id: collection-builtin-to-comprehension”](#sourcery-refactoring-id-collection-builtin-to-comprehension) #### Description: [Section titled “Description:”](#description) Use list, set or dictionary comprehensions directly instead of calling list(), dict() or set() #### Before: [Section titled “Before:”](#before) ```python squares = list(x * x for x in y) ``` ```python squares = set(x * x for x in y) ``` ```python squares = dict((x, x * x) for x in xs) ``` #### After: [Section titled “After:”](#after) ```python squares = [x * x for x in y] ``` ```python squares = {x * x for x in y} ``` ```python squares = {x: x * x for x in xs} ``` #### Explanation: [Section titled “Explanation:”](#explanation) The Pythonic way to create a list, set or dictionary from a generator is to use comprehensions. Using the comprehensions rather than the methods is slightly shorter, and the dictionary comprehension in particular is easier to read in comprehension form. # Collection Into Set > Use set when checking membership of a collection of literals #### Sourcery refactoring id: `collection-into-set` [Section titled “Sourcery refactoring id: collection-into-set”](#sourcery-refactoring-id-collection-into-set) #### Description: [Section titled “Description:”](#description) Use set when checking membership of a collection of literals #### Before: [Section titled “Before:”](#before) ```python if currency in ["EUR", "USD"]: take_payment() ``` #### After: [Section titled “After:”](#after) ```python if currency in {"EUR", "USD"}: take_payment() ``` #### Explanation: [Section titled “Explanation:”](#explanation) When checking if a variable is one of a collection of literals it is both more natural and more performant to use a `set` rather than a `list` or `tuple` to define that collection. Note that this can only be done where Sourcery can ascertain that the value being checked is of a hashable type. # Collection to Bool > Replace constant collection with boolean in boolean contexts #### Sourcery refactoring id: `collection-to-bool` [Section titled “Sourcery refactoring id: collection-to-bool”](#sourcery-refactoring-id-collection-to-bool) #### Description: [Section titled “Description:”](#description) Replace constant collection with boolean in boolean contexts #### Before: [Section titled “Before:”](#before) ```python if ("foo", "bar"): baz() ``` #### After: [Section titled “After:”](#after) ```python if True: baz() ``` #### Explanation: [Section titled “Explanation:”](#explanation) In boolean contexts, dicts, lists, sets and tuples are true if they contain at least one element, and false if they are empty. Because of this, constant collections can be converted to bools based on their length. Some examples are: * `[]`: empty list -> `False` * `{}`: empty dictionary -> `False` * `{0, 1, 2}`: non-empty set -> `True` * `(a, b)`: non-empty tuple -> `True` # Compare Via Equals > Use == or != to compare str, bytes, int, and float. #### Sourcery refactoring id: `compare-via-equals` [Section titled “Sourcery refactoring id: compare-via-equals”](#sourcery-refactoring-id-compare-via-equals) #### Description [Section titled “Description”](#description) Use == or != to compare str, bytes, int, and float. #### Before [Section titled “Before”](#before) ```python nr = 100 if nr is calculate_sum(): do_x() ``` #### After [Section titled “After”](#after) ```python nr = 100 if nr == calculate_sum(): do_x() ``` #### Before [Section titled “Before”](#before-1) ```python if calculate_sum() is not 100: do_x() ``` #### After [Section titled “After”](#after-1) ```python if calculate_sum() != 100: do_x() ``` #### Explanation [Section titled “Explanation”](#explanation) While `is` compares whether two objects are the same, `==` compares whether their values are the same. For objects without an identity (aka value objects in domain-driven design), only the comparison of their values make sense. Prefer `==` to `is` both for the built-in value object types, but also for the value objects defined in your code. Comparing the built-in number types via `is` can also lead to some surprising behaviour and bugs. For small numbers, `is` returns the same as `==`: ```plaintext nr = 42 other_nr = 42 nr == other_nr => True nr is other_nr => True ``` For huge numbers, the result is different: ```plaintext nr = 42000 other_nr = 42000 nr == other_nr => True nr is other_nr => False ``` The same applies for `is not` vs `!=`. # Comprehension to Generator > Replace unneeded comprehension with generator #### Sourcery refactoring id: `comprehension-to-generator` [Section titled “Sourcery refactoring id: comprehension-to-generator”](#sourcery-refactoring-id-comprehension-to-generator) #### Description: [Section titled “Description:”](#description) Replace unneeded comprehension with generator #### Before: [Section titled “Before:”](#before) ```python hat_found = any([is_hat(item) for item in wardrobe]) ``` #### After: [Section titled “After:”](#after) ```python hat_found = any(is_hat(item) for item in wardrobe) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Functions like `any`, `all` and `sum` allow you to pass in a generator rather than a collection. Doing so removes a pair of brackets, making the intent slightly clearer. It will also return immediately if a hat is found, rather than having to build the whole list. This lazy evaluation can lead to performance improvements. Note that we are actually passing a generator into `any()` so strictly speaking the code would look like this: ```python hat_found = any((is_hat(item) for item in wardrobe)) ``` but Python allows you to omit this pair of brackets. The standard library functions that accept generators are: ```python "all", "any", "enumerate", "frozenset", "list", "max", "min", "set", "sum", "tuple" ``` # Convert Any to In > Converts any() functions to simpler in statements #### Sourcery refactoring id: `convert-any-to-in` [Section titled “Sourcery refactoring id: convert-any-to-in”](#sourcery-refactoring-id-convert-any-to-in) #### Description: [Section titled “Description:”](#description) Converts `any()` functions to simpler `in` statements #### Before: [Section titled “Before:”](#before) ```python def shout_about_bowlers(hats: list[str]) -> None: if any(hat == "bowler" for hat in hats): shout("I have a bowler hat!") ``` #### After: [Section titled “After:”](#after) ```python def shout_about_bowlers(hats: list[str]) -> None: if "bowler" in hats: shout("I have a bowler hat!") ``` #### Explanation: [Section titled “Explanation:”](#explanation) Using Python’s `in` operator simplifies the code and makes it much easier to tell at a glance that you are checking if something is present in the sequence. Note that this only triggers if we can detect that the collection being iterated over is not a string type - in this case the semantics of the before/after code would be different. # Convert to Enumerate > Replaces manual loop counter with call to enumerate #### Sourcery refactoring id: `convert-to-enumerate` [Section titled “Sourcery refactoring id: convert-to-enumerate”](#sourcery-refactoring-id-convert-to-enumerate) #### Description: [Section titled “Description:”](#description) Replaces manual loop counter with call to `enumerate` #### Before: [Section titled “Before:”](#before) ```python i = 0 for currency in currencies: print(i, currency) i += 1 ``` #### After: [Section titled “After:”](#after) ```python for i, currency in enumerate(currencies): print(i, currency) ``` #### Explanation: [Section titled “Explanation:”](#explanation) When iterating over a list you sometimes need access to a loop counter that will let you know the index of the element you are utilising. Using the built-in Python function, `enumerate`, lets you generate an index directly, removing two unneeded lines of code. When reading this we don’t have to worry about the book-keeping of the `i` variable, letting us focus in on the code that really matters. # DataFrame Append to Concat > Use pandas.concat() instead of the deprecated DataFrame.append(). #### Sourcery suggestion id: `dataframe-append-to-concat` [Section titled “Sourcery suggestion id: dataframe-append-to-concat”](#sourcery-suggestion-id-dataframe-append-to-concat) Available starting with version 1.1.0 #### Description [Section titled “Description”](#description) Use `pandas.concat()` instead of the deprecated `DataFrame.append()`. #### Before [Section titled “Before”](#before) ```python import pandas as pd df = pd.DataFrame( [ ["Python", 190], ["JavaScript", 33], ], columns=["Language", "Number of rules"], ) other = pd.DataFrame([{"Language": "Java", "Number of rules": 5}]) df.append(other) ``` #### After [Section titled “After”](#after) ```python import pandas as pd df = pd.DataFrame( [ ["Python", 190], ["JavaScript", 33], ], columns=["Language", "Number of rules"], ) other = pd.DataFrame([{"Language": "Java", "Number of rules": 5}]) pd.concat([df, other]) ``` #### Explanation [Section titled “Explanation”](#explanation) `pandas.DataFrame.append()` has been deprecated. Use [`pandas.concat()`](https://pandas.pydata.org/docs/reference/api/pandas.concat.html) instead. See the [DataFrame docs](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.append.html) # De Morgan's Laws > Simplifies conditional logic using De Morgan's laws #### Sourcery refactoring id: `de-morgan` [Section titled “Sourcery refactoring id: de-morgan”](#sourcery-refactoring-id-de-morgan) #### Description: [Section titled “Description:”](#description) Simplifies conditional logic using De Morgan’s laws #### Before: [Section titled “Before:”](#before) ```python if not not p: a = 1 ``` ```python if not a is b: a = 1 ``` ```python if not (p == 1 and q == 2): a = 1 ``` #### After: [Section titled “After:”](#after) ```python if p: a = 1 ``` ```python if a is not b: a = 1 ``` ```python if p != 1 or q != 2: a = 1 ``` #### Explanation: [Section titled “Explanation:”](#explanation) When reading logical statements it is important for them to be as simple as possible. This proposal applies De Morgan’s laws to simplify code by removing double negatives or pushing negations deeper into statements. # Default Get > Simplify dictionary access by using the default get method #### Sourcery refactoring id: `default-get` [Section titled “Sourcery refactoring id: default-get”](#sourcery-refactoring-id-default-get) #### Description: [Section titled “Description:”](#description) Simplify dictionary access by using the default `get` method #### Before: [Section titled “Before:”](#before) ```python def pick_hat(available_hats: Dict[Label, Hat]): if self.favourite_hat in available_hats: hat_to_wear = available_hats[self.favourite_hat] else: hat_to_wear = NO_HAT return hat_to_wear ``` #### After: [Section titled “After:”](#after) ```python def pick_hat(available_hats: Dict[Label, Hat]): hat_to_wear = available_hats.get(self.favourite_hat, NO_HAT) return hat_to_wear ``` #### Explanation: [Section titled “Explanation:”](#explanation) We often want to pick something from a dictionary if the key is present, or use a default value if it isn’t. A useful shortcut is that Python dictionaries have a `get()` method which lets you set a default value using the second parameter. This has slimmed the code down and removed some duplication. A point to note is that if you don’t pass in a default value to `get()` it will use `None`. # Default Mutable Arguments > Replaces use of default mutable arguments in a function #### Sourcery refactoring id: `default-mutable-arg` [Section titled “Sourcery refactoring id: default-mutable-arg”](#sourcery-refactoring-id-default-mutable-arg) #### Description: [Section titled “Description:”](#description) Replaces use of default mutable arguments in a function #### Before: [Section titled “Before:”](#before) ```python def func(hats: list = []): change(hats) ``` #### After: [Section titled “After:”](#after) ```python def func(hats: list = None): if hats is None: hats = [] change(hats) ``` #### Explanation: [Section titled “Explanation:”](#explanation) A common gotcha in Python involves default argument values which are mutable. These are evaluated only once at function definition time, so only one list, set or dictionary instance will be created. This means that if this list is mutated in one call to a function, those changes will show up in subsequent calls of that function. This is usually unintended behaviour, though it can be useful in limited circumstances for writing caches. # Del Comprehension > Replaces cases where deletions are made via for loops with comprehensions #### Sourcery refactoring id: `del-comprehension` [Section titled “Sourcery refactoring id: del-comprehension”](#sourcery-refactoring-id-del-comprehension) #### Description: [Section titled “Description:”](#description) Replaces cases where deletions are made via `for` loops with comprehensions #### Before: [Section titled “Before:”](#before) ```python x1 = {"a": 1, "b": 2, "c": 3} for key in x1.copy(): # can't iterate over a variable that changes size if key not in x0: del x1[key] ``` #### After: [Section titled “After:”](#after) ```python x1 = {"a": 1, "b": 2, "c": 3} x1 = {key: value for key, value in x1.items() if key in x0} ``` #### Explanation: [Section titled “Explanation:”](#explanation) When creating a filtered list in Python it is shorter and more readable to use a comprehension than to copy a list and delete the unneeded keys. # Dict Assign-Update to Union > Changes dictionary assignments and updates to use the union operator. #### Sourcery refactoring id: `dict-assign-update-to-union` [Section titled “Sourcery refactoring id: dict-assign-update-to-union”](#sourcery-refactoring-id-dict-assign-update-to-union) #### Description [Section titled “Description”](#description) Changes dictionary assignments and updates to use the union operator. #### Before [Section titled “Before”](#before) ```python def resolve_configs( default_config: Dict[str, Any], user_config: Dict[str, Any], project_config: Dict[str, Any], ): config = default_config.copy() config.update(user_config) config.update(project_config) return config ``` #### After [Section titled “After”](#after) ```python def resolve_configs( default_config: Dict[str, Any], user_config: Dict[str, Any], project_config: Dict[str, Any], ): return default_config | user_config | project_config ``` #### Explanation [Section titled “Explanation”](#explanation) The union operator was implemented for dictionaries in Python 3.9 (see [PEP 584](https://peps.python.org/pep-0584/)) and introduces a concise way to merge dictionaries without resorting to copy/update statements. See also: [`use-dictionary-union`](/reference/refactorings/python/use-dictionary-union/). # Dictionary Comprehension > Replaces dictionaries created with for loops with dictionary comprehensions #### Sourcery refactoring id: `dict-comprehension` [Section titled “Sourcery refactoring id: dict-comprehension”](#sourcery-refactoring-id-dict-comprehension) #### Description: [Section titled “Description:”](#description) Replaces dictionaries created with `for` loops with dictionary comprehensions #### Before: [Section titled “Before:”](#before) ```python cubes = {} for i in range(100): cubes[i] = i**3 ``` #### After: [Section titled “After:”](#after) ```python cubes = {x: x**3 for x in range(100)} ``` #### Explanation: [Section titled “Explanation:”](#explanation) A dictionary comprehension can create the dictionary on one line, cutting out the clutter of declaring an empty dict and then adding items. Turning three lines of code into one is a definite win - it means less scrolling back and forth when reading methods and helps keep things manageable. Squeezing code onto one line can make it more difficult to read, but for comprehensions this isn’t the case. All of the elements that you need are nicely presented, and once you are used to the syntax it is actually more readable than the for loop version. Another point is that the assignment is now more of an atomic operation - we’re declaring what `cubes` is rather than giving instructions on how to build it. This makes the code read like more of a narrative, since going forward we will care more about what `cubes` is than the details of its construction. Finally comprehensions will usually execute more quickly than building the collection in a loop, which is another factor if performance is a consideration. # Dict-Literal > Replace `dict()` with `{}` #### Sourcery rule id: `dict-literal` [Section titled “Sourcery rule id: dict-literal”](#sourcery-rule-id-dict-literal) #### Description [Section titled “Description”](#description) Replace `dict()` with `{}` #### Before [Section titled “Before”](#before) ```python x = dict() ``` #### After [Section titled “After”](#after) ```python x = {} ``` #### Explanation [Section titled “Explanation”](#explanation) The most concise and Pythonic way to create a dictionary is to use the `{}` notation. This fits in with the way we create dictionaries with items, saving a bit of mental energy that might be taken up with thinking about two different ways of creating dicts. ```python x = {"first": "thing"} ``` Doing things this way has the added advantage of being a nice little performance improvement. Here are the timings before and after the change: ```plaintext $ python3 -m timeit "x = dict()" 5000000 loops, best of 5: 69.8 nsec per loop ``` ```plaintext $ python3 -m timeit "x = {}" 20000000 loops, best of 5: 29.4 nsec per loop ``` Similar reasoning and performance results hold for replacing `list()` with `[]`. # Do Not Use Bare Except > Use except: Exception rather than bare except #### Sourcery suggestion id: `do-not-use-bare-except` [Section titled “Sourcery suggestion id: do-not-use-bare-except”](#sourcery-suggestion-id-do-not-use-bare-except) #### Description [Section titled “Description”](#description) Use `except: Exception` rather than bare `except` #### Before [Section titled “Before”](#before) ```python try: some_important_code() except: exception_handling_code() ``` #### After [Section titled “After”](#after) ```python try: some_important_code() except Exception: exception_handling_code() ``` #### Explanation [Section titled “Explanation”](#explanation) PEP 8 [recommends](https://peps.python.org/pep-0008/#programming-recommendations) that we should avoid catching exceptions using a bare `except` clause. The problem with these is that they catch `SystemExit` and `KeyboardInterrupt` exceptions, which makes it harder to interrupt a program using `CTRL`+`C`, and can also disguise other problems. The PEP recommends catching `Exception`, which will catch all program errors, so that is what Sourcery suggests. Note that this suggestion does not trigger if the exception handler ends by raising the original exception. # Dont-Import-Test-Modules > Don't import test modules. #### Sourcery rule id: `dont-import-test-modules` [Section titled “Sourcery rule id: dont-import-test-modules”](#sourcery-rule-id-dont-import-test-modules) #### Description [Section titled “Description”](#description) Don’t import test modules. #### Match [Section titled “Match”](#match) ```python import test.unit.test_something ``` #### Explanation [Section titled “Explanation”](#explanation) Don’t import test modules. Tests should be self-contained and don’t depend on each other. If a helper function is used by multiple tests, define it in a helper module, instead of importing one test from the other. # Ensure File Closed > Use with when opening file to ensure closure #### Sourcery refactoring id: `ensure-file-closed` [Section titled “Sourcery refactoring id: ensure-file-closed”](#sourcery-refactoring-id-ensure-file-closed) #### Description: [Section titled “Description:”](#description) Use `with` when opening file to ensure closure #### Before: [Section titled “Before:”](#before) ```python file = open("welcome.txt") data = file.read() print(data) file.close() ``` #### After: [Section titled “After:”](#after) ```python with open("welcome.txt") as file: data = file.read() print(data) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Opening files in this way is more concise, and also ensures that the call to `file.close()` is not skipped if there is an exception. It makes use of Python’s `with` context manager - under the hood the changed code behaves like this: ```python file = open("welcome.txt") try: data = file.read() print(data) finally: file.close() ``` The file is closed for you as soon as the block is exited, even where an exception has been thrown. # Equality Identity > Simplify equality comparisons that are always True or False #### Sourcery refactoring id: `equality-identity` [Section titled “Sourcery refactoring id: equality-identity”](#sourcery-refactoring-id-equality-identity) #### Description: [Section titled “Description:”](#description) Simplify equality comparisons that are always `True` or `False` #### Before: [Section titled “Before:”](#before) ```python if 1 == 1: always_do_this() if 1 != 1: never_do_this() ``` #### After: [Section titled “After:”](#after) ```python if True: always_do_this() if False: never_do_this() ``` #### Explanation: [Section titled “Explanation:”](#explanation) When comparing a value to itself, the outcome will always be `True`, as long as the equality operator has not been overridden, and this also holds for the opposite comparison and `False`. It is more readable to use a direct comparison to the boolean value. # Extract Duplicate Method > Identifies duplicate sections of code in a function and extracts these into their own method #### Sourcery refactoring id: `extract-duplicate-method` [Section titled “Sourcery refactoring id: extract-duplicate-method”](#sourcery-refactoring-id-extract-duplicate-method) #### Description: [Section titled “Description:”](#description) Identifies duplicate sections of code in a function and extracts these into their own method **Requires Sourcery Pro** #### Before: [Section titled “Before:”](#before) ```python def extraction_example(): self.speed_slider = Scale( self.parent, from_=1, to=10, orient=HORIZONTAL, label="Speed" ) self.speed_slider.pack() self.speed_slider.set(DEFAULT_SPEED) self.speed_slider.configure(background="white") self.force_slider = Scale( self.parent, from_=1, to=10, orient=HORIZONTAL, label="Force" ) self.force_slider.pack() self.force_slider.set(DEFAULT_FORCE) self.force_slider.configure(background="white") ``` #### After: [Section titled “After:”](#after) ```python def extraction_example(): self.speed_slider = _extracted_from_extraction_example_2( self, "Speed", DEFAULT_SPEED ) self.force_slider = _extracted_from_extraction_example_2( self, "Force", DEFAULT_FORCE ) def _extracted_from_extraction_example_2(self, label, arg2): result = Scale(self.parent, from_=1, to=10, orient=HORIZONTAL, label=label) result.pack() result.set(arg2) result.configure(background="white") return result ``` #### Explanation: [Section titled “Explanation:”](#explanation) Do not Repeat Yourself (DRY) is an important tenet of writing clean, maintainable code. Duplicated code bloats the code base, making it harder to read and understand. It often also leads to bugs. Where changes are made in only some of the duplicated areas unintended behaviour will often arise. One of the main ways to remove duplication is to extract the common areas into another method and call that. Sourcery can detect areas of duplicate code that are in the same function and extract them. It is recommended that you then rename the extracted function and any arguments that have not been automatically named. In the above example a suitable method name would be `create_slider`, and `arg2` would be `default_value`. # Extract Method > Extracts complex pieces of functions into their own methods #### Sourcery refactoring id: `extract-method` [Section titled “Sourcery refactoring id: extract-method”](#sourcery-refactoring-id-extract-method) #### Description: [Section titled “Description:”](#description) Extracts complex pieces of functions into their own methods **Requires Sourcery Pro** #### Before: [Section titled “Before:”](#before) ```python def func(self): if typed and self.current_char == "(": pos = self.pos try: pass except DefinitionError as exParamQual: self.pos = pos try: pass except DefinitionError as exNoPtrParen: self.pos = pos msg = "If parenthesis in noptr-declarator" if paramMode == "function": msg += " (e.g., 'void (*f(int arg))(double)')" prevErrors.append((exNoPtrParen, msg)) header = "Error in declarator" raise self._make_multi_error(prevErrors, header) ``` #### After: [Section titled “After:”](#after) ```python def func(self): if typed and self.current_char == "(": pos = self.pos try: pass except DefinitionError as exParamQual: self.pos = pos try: pass except DefinitionError as exNoPtrParen: self._extracted_from_func_11(pos, exNoPtrParen) def _extracted_from_func_11(self, pos, exNoPtrParen): self.pos = pos msg = "If parenthesis in noptr-declarator" if paramMode == "function": msg += " (e.g., 'void (*f(int arg))(double)')" prevErrors.append((exNoPtrParen, msg)) header = "Error in declarator" raise self._make_multi_error(prevErrors, header) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Methods are much more readable if they are short, and if every line of code is at the same rough level of abstraction. This refactoring takes long blocks from functions and extracts them into their own method. It will only trigger for large blocks that are possible to extract, and where the remainder of the method is sufficiently long. It is recommended that you then rename the extracted function and any arguments that have not been automatically named. Here a suitable method name would be `handle_noptr_exception`. # Flatten Nested Try > Merge nested try-statement into a single try #### Sourcery refactoring id: `flatten-nested-try` [Section titled “Sourcery refactoring id: flatten-nested-try”](#sourcery-refactoring-id-flatten-nested-try) #### Description: [Section titled “Description:”](#description) Merge nested try-statement into a single try #### Before: [Section titled “Before:”](#before) ```python def testConnection(db, credentials): try: try: db.connect(credentials) except InvalidCredentials: return "Check your credentials" except ConnectionError: return "Error while trying to connect" finally: print("Connection attempt finished") return "Connection Successful" ``` #### After: [Section titled “After:”](#after) ```python def testConnection(db, credentials): try: db.connect(credentials) except InvalidCredentials: return "Check your credentials" except ConnectionError: return "Error while trying to connect" finally: print("Connection attempt finished") return "Connection Successful" ``` #### Explanation: [Section titled “Explanation:”](#explanation) Flattening try-except statements nested within a try-finally generates equivalent code that is easier to read and expand upon. # Flip Comparison > Moves variables from the right side to the left side of comparisons #### Sourcery refactoring id: `flip-comparison` [Section titled “Sourcery refactoring id: flip-comparison”](#sourcery-refactoring-id-flip-comparison) #### Description: [Section titled “Description:”](#description) Moves variables from the right side to the left side of comparisons #### Before: [Section titled “Before:”](#before) ```python if 1 == x: do_x() ``` #### After: [Section titled “After:”](#after) ```python if x == 1: do_x() ``` #### Explanation: [Section titled “Explanation:”](#explanation) It is a coding convention that when doing comparisons to a numeric constant, the number should be on the right and the variable on the left. Having things be consistent in this way makes the code easier to read. # For Append To Extend > Replace a for append loop with list extend #### Sourcery refactoring id: `for-append-to-extend` [Section titled “Sourcery refactoring id: for-append-to-extend”](#sourcery-refactoring-id-for-append-to-extend) #### Description [Section titled “Description”](#description) Replace a for append loop with list extend #### Before [Section titled “Before”](#before) ```python games = [] for i in range(10): for j in range(10): if i != j: games.append(play(i, j)) ``` #### After [Section titled “After”](#after) ```python games = [] for i in range(10): games.extend(play(i, j) for j in range(10) if i != j) ``` #### Explanation [Section titled “Explanation”](#explanation) Instead of looping through an iterator and appending to the list, we can make the intent clearer by explicitly stating that we are extending this list. # For Index Replacement > Replace item lookups in loops using the index with direct reference to the items #### Sourcery refactoring id: `for-index-replacement` [Section titled “Sourcery refactoring id: for-index-replacement”](#sourcery-refactoring-id-for-index-replacement) #### Description: [Section titled “Description:”](#description) Replace item lookups in loops using the index with direct reference to the items #### Before: [Section titled “Before:”](#before) ```python for i in range(len(currencies)): print(currencies[i]) ``` #### After: [Section titled “After:”](#after) ```python for currency in currencies: print(currency) ``` #### Explanation: [Section titled “Explanation:”](#explanation) A pattern that is often used in Python for loops is to use `range(len(list))` to generate a range of numbers that can be iterated over. If the index `i` is only used to do list access this code can be improved by iterating over the list directly, as in the above example. This code is easier to understand, and a lot less cluttered. In particular being able to use a meaningful name for `currency` greatly improves readability. Note that this refactoring will only trigger where Sourcery can determine that the collection (`currencies` in this case) is a list. # Replace For Index with Underscore > Replaces an unused index in a for loop or comprehension with an underscore #### Sourcery refactoring id: `for-index-underscore` [Section titled “Sourcery refactoring id: for-index-underscore”](#sourcery-refactoring-id-for-index-underscore) #### Description: [Section titled “Description:”](#description) Replaces an unused index in a `for` loop or comprehension with an underscore #### Before: [Section titled “Before:”](#before) ```python for hat in my_wardrobe.hats: shout("Hurrah!") ``` ```python shouts = [shout("Hurrah!") for hat in my_wardrobe.hats] ``` #### After: [Section titled “After:”](#after) ```python for _ in my_wardrobe.hats: shout("Hurrah!") ``` ```python shouts = [shout("Hurrah!") for _ in my_wardrobe.hats] ``` #### Explanation: [Section titled “Explanation:”](#explanation) Sometimes in a for loop or comprehension we just want some code to run a certain number of times, and don’t actually make use of the index variable. In the above example we have introduced a new variable, `hat`, which we have to note when reading the code, but actually we don’t need it and could replace it with `_`: It is a convention in Python to use `_` as a throwaway name for unused variables. This means your brain can learn to safely ignore these, reducing the overhead to understand the code. Where you see this in a `for` loop or comprehension it is immediately clear that the loop is just used to repeat a block of code and we don’t care about the value being iterated over. # Guard > Adds in guard clause to a conditional #### Sourcery refactoring id: `guard` [Section titled “Sourcery refactoring id: guard”](#sourcery-refactoring-id-guard) #### Description: [Section titled “Description:”](#description) Adds in guard clause to a conditional #### Before: [Section titled “Before:”](#before) ```python def set_hat_details(hat): if hat.stylish and is_summer_hat(hat): hat.tag = "SUMMER" elif not hat: pass elif hat.stylish and is_winter_hat(hat): hat.tag = "WINTER" ``` #### After: [Section titled “After:”](#after) ```python def set_hat_details(hat): if not hat: return if hat.stylish and is_summer_hat(hat): hat.tag = "SUMMER" elif hat.stylish and is_winter_hat(hat): hat.tag = "WINTER" ``` #### Explanation: [Section titled “Explanation:”](#explanation) Often one option within an `if..elif` conditional will have no effect, and hence have only a `pass` within it. Where there is no further code within the block afterwards it is possible to extract this branch out as a guard condition. This can then sometimes allow further refinements to be made to the `if..elif` statements. Sourcery only suggests this refactoring if it unlocks such further changes - for example in the above case it would be combined with `lift-duplicated-conditional` to give the following output: ```python def set_hat_details(hat): if not hat: return if hat.stylish: if is_summer_hat(hat): hat.tag = "SUMMER" elif is_winter_hat(hat): hat.tag = "WINTER" ``` # Hoist If from If > Moves if statements that match a conditional out of that conditional #### Sourcery refactoring id: `hoist-if-from-if` [Section titled “Sourcery refactoring id: hoist-if-from-if”](#sourcery-refactoring-id-hoist-if-from-if) #### Description: [Section titled “Description:”](#description) Moves `if` statements that match a conditional out of that conditional #### Before: [Section titled “Before:”](#before) ```python if hat.quality < DESIRED_QUALITY: happiness -= 1 if not hat.is_stylish() and hat.quality < DESIRED_QUALITY: happiness -= 1 ``` #### After: [Section titled “After:”](#after) ```python if hat.quality < DESIRED_QUALITY: happiness -= 1 if not hat.is_stylish() and hat.quality < DESIRED_QUALITY: happiness -= 1 ``` #### Explanation: [Section titled “Explanation:”](#explanation) Where a nested conditional repeats the conditions of the outer one, it is logically equivalent to reduce the level of nesting as shown above. Doing this can make the meaning of the code slightly clearer. # Hoist Loop from If > Moves loops that occur in all cases of an if statement outside of the conditional #### Sourcery refactoring id: `hoist-loop-from-if` [Section titled “Sourcery refactoring id: hoist-loop-from-if”](#sourcery-refactoring-id-hoist-loop-from-if) #### Description: [Section titled “Description:”](#description) Moves loops that occur in all cases of an `if` statement outside of the conditional #### Before: [Section titled “Before:”](#before) ```python def sing_song(self): if style == 1: while thing_true(): do_x() elif style == 2: while thing_true(): do_y() elif style == 3: while thing_true(): do_z() ``` #### After: [Section titled “After:”](#after) ```python def sing_song(self): while thing_true(): if style == 1: do_x() elif style == 2: do_y() elif style == 3: do_z() ``` #### Explanation: [Section titled “Explanation:”](#explanation) Where the same `for` or `while` loop is defined in all branches of a conditional, the code can be considerably shortened and clarified by hoisting. By moving the loop outside, duplication is removed and the code becomes much clearer. # Hoist Repeated If Condition > Move a repeated condition to a parent if block. #### Sourcery refactoring id: `hoist-repeated-if-condition` [Section titled “Sourcery refactoring id: hoist-repeated-if-condition”](#sourcery-refactoring-id-hoist-repeated-if-condition) #### Description [Section titled “Description”](#description) Move a repeated condition to a parent `if` block. #### Before [Section titled “Before”](#before) ```python if x < 5 and y < 10: grid = range(10) if x < 5 and y < 100: grid = range(100) ``` #### After [Section titled “After”](#after) ```python if x < 5: if y < 10: grid = range(10) if y < 100: grid = range(100) ``` #### Explanation [Section titled “Explanation”](#explanation) Don’t repeat yourself! Although nested code is normally discouraged, in this case it is easier to see the relationships between conditions. See also: [`lift-duplicated-conditional`](/reference/refactorings/python/lift-duplicated-conditional/), the related refactoring for `if..elif` blocks. # Hoist Similar Statements from If > Hoist nested repeated code outside conditional statements #### Sourcery refactoring id: `hoist-similar-statement-from-if` [Section titled “Sourcery refactoring id: hoist-similar-statement-from-if”](#sourcery-refactoring-id-hoist-similar-statement-from-if) #### Description: [Section titled “Description:”](#description) Hoist nested repeated code outside conditional statements #### Before: [Section titled “Before:”](#before) ```python if condition: args = {"some": "args"} with db: make_db_change(args) else: args = {"some": "args"} with db: make_other_db_change(args) ``` #### After: [Section titled “After:”](#after) ```python args = {"some": "args"} with db: if condition: make_db_change(args) else: make_other_db_change(args) ``` #### Explanation: [Section titled “Explanation:”](#explanation) We should always be on the lookout for ways to remove duplicated code. An opportunity for code hoisting is a nice way of doing so. Sometimes code is repeated on both branches of a conditional, and the difference between the branches occurs within a nested structure like a `with`. In this case we can refactor the conditional so that the condition is moved into the nested structure, hoisting the rest of the code and resulting in something simpler. # Hoist Statements from If > Moves statements that occur in all cases of an if statement outside of the conditional #### Sourcery refactoring id: `hoist-statement-from-if` [Section titled “Sourcery refactoring id: hoist-statement-from-if”](#sourcery-refactoring-id-hoist-statement-from-if) #### Description: [Section titled “Description:”](#description) Moves statements that occur in all cases of an `if` statement outside of the conditional #### Before: [Section titled “Before:”](#before) ```python if sold > DISCOUNT_AMOUNT: total = sold * DISCOUNT_PRICE label = f"Total: {total}" else: total = sold * PRICE label = f"Total: {total}" ``` #### After: [Section titled “After:”](#after) ```python if sold > DISCOUNT_AMOUNT: total = sold * DISCOUNT_PRICE else: total = sold * PRICE label = f"Total: {total}" ``` #### Explanation: [Section titled “Explanation:”](#explanation) We should always be on the lookout for ways to remove duplicated code. An opportunity for code hoisting is a nice way of doing so. Sometimes code is repeated on both branches of a conditional. This means that the code will always execute. The duplicate lines can be hoisted out of the conditional and replaced with a single line. By taking the assignment to `label` outside of the conditional we have removed a duplicate line of code, and made it clearer what the conditional is actually controlling, which is the `total`. # Hoist Statement from Loop > Moves statements that are constant across all cases of a loop outside of the loop #### Sourcery refactoring id: `hoist-statement-from-loop` [Section titled “Sourcery refactoring id: hoist-statement-from-loop”](#sourcery-refactoring-id-hoist-statement-from-loop) #### Description: [Section titled “Description:”](#description) Moves statements that are constant across all cases of a loop outside of the loop #### Before: [Section titled “Before:”](#before) ```python for building in buildings: city = "London" addresses.append(building.street_address, city) ``` #### After: [Section titled “After:”](#after) ```python city = "London" for building in buildings: addresses.append(building.street_address, city) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Another type of hoisting is pulling invariant statements out of loops. If a statement just sets up some variables for use in the loop, it doesn’t need to be inside it. Loops are inherently complex, so making them shorter and easier to understand should be on your mind while writing them. In this example the `city` variable gets assigned inside the loop, but it is only read and not altered. It’s therefore safe to hoist it out, and this makes it clearer that the same `city` value will apply to every `building`. This also improves performance - any statement in a loop is going to be executed every time the loop runs. The time spent on these multiple executions is being wasted, since it only needs to be executed once. This saving can be significant if the statements involve calls to databases or other time-consuming tasks. # Identity Comprehensions > Convert list/set/tuple comprehensions that do not change the input elements into #### Sourcery refactoring id: `identity-comprehension` [Section titled “Sourcery refactoring id: identity-comprehension”](#sourcery-refactoring-id-identity-comprehension) #### Description [Section titled “Description”](#description) Convert list/set/tuple comprehensions that do not change the input elements into #### Before [Section titled “Before”](#before) ```python # List comprehensions [item for item in coll] [item for item in friends.names()] # Dict comprehensions {k: v for k, v in coll} {k: v for k, v in coll.items()} # Only if we know coll is a `dict` # Unneeded call to `.items()` dict(coll.items()) # Only if we know coll is a `dict` # Set comprehensions {item for item in coll} ``` #### After [Section titled “After”](#after) ```python # List comprehensions list(iter(coll)) list(iter(friends.names())) # Dict comprehensions dict(coll) dict(coll) # Unneeded call to `.items()` dict(coll) # Set comprehensions set(coll) ``` #### Explanation [Section titled “Explanation”](#explanation) All these comprehensions are just creating a copy of the original collection. They can all be simplified by simply constructing a new collection directly. The resulting code is easier to read and shows the intent more clearly. # Inline Immediately Returned Variables > Inlines a variable to a return in the case when the variable being declared is immediately returned #### Sourcery refactoring id: `inline-immediately-returned-variable` [Section titled “Sourcery refactoring id: inline-immediately-returned-variable”](#sourcery-refactoring-id-inline-immediately-returned-variable) #### Description [Section titled “Description”](#description) Inlines a variable to a `return` in the case when the variable being declared is immediately returned #### Before [Section titled “Before”](#before) ```python def state_attributes(self): """Return the state attributes.""" state_attr = { ATTR_CODE_FORMAT: self.code_format, ATTR_CHANGED_BY: self.changed_by, } return state_attr ``` #### After [Section titled “After”](#after) ```python def state_attributes(self): """Return the state attributes.""" return { ATTR_CODE_FORMAT: self.code_format, ATTR_CHANGED_BY: self.changed_by, } ``` #### Explanation [Section titled “Explanation”](#explanation) Something that we often see in people’s code is assigning to a result variable and then immediately returning it. Returning the result directly shortens the code and removes an unnecessary variable, reducing the mental load of reading the function. Where intermediate variables can be useful is if they then get used as a parameter or a condition, and the name can act like a comment on what the variable represents. In the case where you’re returning it from a function, the function name is there to tell you what the result is - in the example above it’s the state attributes, and the `state_attr` name wasn’t providing any extra information. One point to note is that if the variable has a type annotation, we will not suggest inlining it, since this would lose that information, possibly leading to type checking errors. #### Related refactorings [Section titled “Related refactorings”](#related-refactorings) * [inline-immediately-yielded-variable](/reference/refactorings/python/inline-immediately-yielded-variable/) # Inline Immediately Yielded Variable > Inline variable that is immediately yielded #### Sourcery refactoring id: `inline-immediately-yielded-variable` [Section titled “Sourcery refactoring id: inline-immediately-yielded-variable”](#sourcery-refactoring-id-inline-immediately-yielded-variable) #### Description [Section titled “Description”](#description) Inline variable that is immediately yielded #### Before [Section titled “Before”](#before) ```python def f(a): b = a + 1 yield b def g(a): b = [1, 2, 3] yield from b ``` #### After [Section titled “After”](#after) ```python def f(a): yield a + 1 def g(a): yield from [1, 2, 3] ``` #### Explanation [Section titled “Explanation”](#explanation) Yielding the result directly shortens the code and makes it more readable. #### Related refactorings [Section titled “Related refactorings”](#related-refactorings) * [inline-immediately-returned-variable](/reference/refactorings/python/inline-immediately-returned-variable/) # Inline Variable > Inline variable that is only used once #### Sourcery refactoring id: `inline-variable` [Section titled “Sourcery refactoring id: inline-variable”](#sourcery-refactoring-id-inline-variable) #### Description: [Section titled “Description:”](#description) Inline variable that is only used once #### Before: [Section titled “Before:”](#before) ```python thelist = [] for i in range(10): k = i**2 thelist.append(k) ``` #### After: [Section titled “After:”](#after) ```python thelist = [] for i in range(10): thelist.append(i**2) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Inlining variable can help to streamline the code, but can also make it less readable. Sourcery will only inline variables where doing so allows further readability changes to be made, such as converting a loop into a list comprehension. # Instance Method First Parameter Should Be `self` > Suggests that instance methods should rename their first parameter to self. #### Sourcery refactoring id: `instance-method-first-arg-name` [Section titled “Sourcery refactoring id: instance-method-first-arg-name”](#sourcery-refactoring-id-instance-method-first-arg-name) #### Description: [Section titled “Description:”](#description) Suggests that instance methods should rename their first parameter to `self`. #### Before: [Section titled “Before:”](#before) ```python class Chicken: def lay_egg(this, coop): with this.enter_coop(coop): coop.eggs.add(Egg(this.dna)) ``` #### After: [Section titled “After:”](#after) ```python class Chicken: def lay_egg(self, coop): with self.enter_coop(coop): coop.eggs.add(Egg(self.dna)) ``` #### Explanation: [Section titled “Explanation:”](#explanation) This is both a very strong convention in Python and an explicit entry in [PEP-8](https://peps.python.org/pep-0008/#function-and-method-arguments). Adhering to it improves code consistency and makes it easier for developers to understand the intention of your code. Note that we do not suggest this where there are decorators present, since these can override the behaviour, meaning that `self` is not the correct name. See also: [`class-method-first-arg-name`](/reference/refactorings/python/class-method-first-arg-name/) # Introduce Default Else > Move default state of variable into else branch #### Sourcery refactoring id: `introduce-default-else` [Section titled “Sourcery refactoring id: introduce-default-else”](#sourcery-refactoring-id-introduce-default-else) #### Description: [Section titled “Description:”](#description) Move default state of variable into `else` branch #### Before: [Section titled “Before:”](#before) ```python data = "" if condition: data = dictionary["message"] ``` #### After: [Section titled “After:”](#after) ```python if condition: data = dictionary["message"] else: data = "" ``` #### Explanation: [Section titled “Explanation:”](#explanation) A common pattern when coding is to set the state of a variable based on a condition. Often we set a default state first, then might set it to something different if a condition holds. In these cases Sourcery will move the default setting into the `else` branch, but only if this unlocks further changes. # Invert Any/All > Switches not any or not all statements to all or any statements respectively #### Sourcery refactoring id: `invert-any-all` [Section titled “Sourcery refactoring id: invert-any-all”](#sourcery-refactoring-id-invert-any-all) #### Description: [Section titled “Description:”](#description) Switches `not any` or `not all` statements to `all` or `any` statements respectively #### Before: [Section titled “Before:”](#before) ```python b = not all(a > b for a in things) ``` #### After: [Section titled “After:”](#after) ```python b = any(a <= b for a in things) ``` #### Explanation: [Section titled “Explanation:”](#explanation) A negation is more difficult to read than a straightforward statement. This refactoring inverts the any or all to remove the negation. # Invert Any/All in Body > Simplifies any and all statements that can be inverted #### Sourcery refactoring id: `invert-any-all-body` [Section titled “Sourcery refactoring id: invert-any-all-body”](#sourcery-refactoring-id-invert-any-all-body) #### Description: [Section titled “Description:”](#description) Simplifies `any` and `all` statements that can be inverted #### Before: [Section titled “Before:”](#before) ```python b = any(not a for a in things) ``` #### After: [Section titled “After:”](#after) ```python b = all(things) ``` #### Explanation: [Section titled “Explanation:”](#explanation) A negation is more difficult to read than a straightforward statement. This refactoring inverts the any or all to remove the negation. # Last if statement guard > Convert the final conditional into a guard clause #### Sourcery refactoring id: `last-if-guard` [Section titled “Sourcery refactoring id: last-if-guard”](#sourcery-refactoring-id-last-if-guard) #### Description: [Section titled “Description:”](#description) Convert the final conditional into a guard clause #### Before: [Section titled “Before:”](#before) ```python def f(a=None): if a is None: return 42 else: # some long calculations var = (i % 2 for i in range(a)) return sum(var) ``` #### After: [Section titled “After:”](#after) ```python def f(a=None): if a is None: return 42 # some long calculations var = (i % 2 for i in range(a)) return sum(var) ``` #### Explanation: [Section titled “Explanation:”](#explanation) A common code pattern is to have some clauses at the start of a function, to check whether certain conditions have been fulfilled and return early or raise an exception if not. While this is perfectly valid code, it can run into problems with excessive nesting, particularly if the rest of the function is fairly long. Here we can take advantage of the fact that we don’t need the `else` if the main body of the `if` breaks the control flow by ending with `return` or `raise`. Rewriting the function as shown here is logically equivalent. Using a guard condition, or multiple guard conditions, in this way now doesn’t cause the rest of the function to be indented. In general the less we have to deal with indents the easier the code is to understand. # Lift Duplicated Conditional > Lift repeated conditional into its own if statement #### Sourcery refactoring id: `lift-duplicated-conditional` [Section titled “Sourcery refactoring id: lift-duplicated-conditional”](#sourcery-refactoring-id-lift-duplicated-conditional) #### Description: [Section titled “Description:”](#description) Lift repeated conditional into its own `if` statement #### Before: [Section titled “Before:”](#before) ```python if isinstance(hat, Sombrero) and hat.colour == "green": wear(hat) elif isinstance(hat, Sombrero) and hat.colour == "red": destroy(hat) ``` #### After: [Section titled “After:”](#after) ```python if isinstance(hat, Sombrero): if hat.colour == "green": wear(hat) elif hat.colour == "red": destroy(hat) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Duplicating conditions in an `if` statement makes things more difficult to read, and carries with it all the usual problems of code duplication. While normally we try to avoid adding nesting to the code, in this case it makes sense to lift the duplicated conditional into its own `if` statement. It is now clearer at a glance that the whole `if..elif` chain relates only to sombreros and not other types of hat. See also: [`hoist-repeated-if-condition`](/reference/refactorings/python/hoist-repeated-if-condition/), the related refactoring for repeated `if` blocks. # Lift Return into If > Lift return into if #### Sourcery refactoring id: `lift-return-into-if` [Section titled “Sourcery refactoring id: lift-return-into-if”](#sourcery-refactoring-id-lift-return-into-if) #### Description: [Section titled “Description:”](#description) Lift `return` into `if` #### Before: [Section titled “Before:”](#before) ```python def f(): if condition: val = 42 else: val = 0 return val ``` #### After: [Section titled “After:”](#after) ```python def f(): if condition: return 42 else: return 0 ``` #### Explanation: [Section titled “Explanation:”](#explanation) This is a quick way to streamline code slightly. Where a value is set on each branch of an if and then immediately returned, instead return it directly from each branch. This has removed an unnecessary intermediate variable which we had to mentally track. # List Comprehension > Converts a for loop into a list comprehension #### Sourcery refactoring id: `list-comprehension` [Section titled “Sourcery refactoring id: list-comprehension”](#sourcery-refactoring-id-list-comprehension) #### Description: [Section titled “Description:”](#description) Converts a `for` loop into a list comprehension #### Before: [Section titled “Before:”](#before) ```python cubes = [] for i in range(20): cubes.append(i**3) ``` #### After: [Section titled “After:”](#after) ```python cubes = [i**3 for i in range(20)] ``` #### Explanation: [Section titled “Explanation:”](#explanation) A list comprehension can create the list on one line, cutting out the clutter of declaring an empty list and then appending values. Turning three lines of code into one is a definite win - it means less scrolling back and forth when reading methods and helps keep things manageable. Squeezing code onto one line can make it more difficult to read, but for comprehensions this isn’t the case. All of the elements that you need are nicely presented, and once you are used to the syntax it is actually more readable than the for loop version. Another point is that the assignment is now more of an atomic operation - we’re declaring what `cubes` is rather than giving instructions on how to build it. This makes the code read like more of a narrative, since going forward we will care more about what `cubes` is than the details of its construction. Finally comprehensions will usually execute more quickly than building the collection in a loop, which is another factor if performance is a consideration. # List Literal > Replaces lists created with list() with [] #### Sourcery refactoring id: `list-literal` [Section titled “Sourcery refactoring id: list-literal”](#sourcery-refactoring-id-list-literal) #### Description: [Section titled “Description:”](#description) Replaces lists created with `list()` with `[]` #### Before: [Section titled “Before:”](#before) ```python x = list() ``` #### After: [Section titled “After:”](#after) ```python x = [] ``` #### Explanation: [Section titled “Explanation:”](#explanation) The most concise and Pythonic way to create a list is to use the `[]` notation. This fits in with the way we create lists with elements, saving a bit of mental energy that might be taken up with thinking about two different ways of creating lists. ```python x = ["first", "second"] ``` Doing things this way has the added advantage of being a nice little performance improvement. Here are the timings before and after the change: ```plaintext $ python3 -m timeit "x = list()" 5000000 loops, best of 5: 63.3 nsec per loop ``` ```plaintext $ python3 -m timeit "x = []" 20000000 loops, best of 5: 15.8 nsec per loop ``` Similar reasoning and performance results hold for replacing `dict()` with `{}`. # Low Code Quality > Low code quality found in function. #### Sourcery comment id: `low-code-quality` [Section titled “Sourcery comment id: low-code-quality”](#sourcery-comment-id-low-code-quality) #### Description [Section titled “Description”](#description) Low code quality found in function. #### Explanation [Section titled “Explanation”](#explanation) This appears when the quality score for your function dips below the configured quality threshold (the default is 25%). This score is a combination of the method length, cognitive complexity and working memory of the function. How can you solve this? * Reduce the function length by extracting pieces of functionality out into their own functions. This is the most important thing you can do - ideally a function should be less than 10 lines. * Reduce nesting, perhaps by introducing guard clauses to return early * Ensure that variables are tightly scoped, so that code using related concepts sits together within the function rather than being scattered. # Max/min Default > Use max/min default argument instead of if statement #### Sourcery refactoring id: `max-min-default` [Section titled “Sourcery refactoring id: max-min-default”](#sourcery-refactoring-id-max-min-default) #### Description [Section titled “Description”](#description) Use max/min default argument instead of if statement #### Before [Section titled “Before”](#before) ```python a = [1, 2, 3] if a: b = max(a) else: b = 0 ``` #### After [Section titled “After”](#after) ```python a = [1, 2, 3] b = max(a, default=0) ``` #### Explanation [Section titled “Explanation”](#explanation) When using `max`/`min` over an iterable we often want to provide a default value. Instead of setting this using an `if` statement we can use the `default` keyword argument of [max](https://docs.python.org/3/library/functions.html#max)/[min](https://docs.python.org/3/library/functions.html#min) directly. This makes the intent of the code much clearer and makes it much easier to read. # Merge Assignment and Augmented Assignment > Replaces an assignment and an augmented assignment with a single assignment. #### Sourcery refactoring id: `merge-assign-and-aug-assign` [Section titled “Sourcery refactoring id: merge-assign-and-aug-assign”](#sourcery-refactoring-id-merge-assign-and-aug-assign) #### Description: [Section titled “Description:”](#description) Replaces an assignment and an augmented assignment with a single assignment. #### Before: [Section titled “Before:”](#before) ```python other_value = 33 number = 42 number += other_value ``` #### After: [Section titled “After:”](#after) ```python other_value = 33 number = 42 + other_value ``` #### Explanation: [Section titled “Explanation:”](#explanation) When we mutate a variable multiple times without reading or writing its value in between, it’s more readable and more efficient to change its value only once. This way, it’s clearer which values this variable can have at various points. This refactoring works with all 4 augmented assignment operators: * `+=` * `-=` * `*=` * `/=` # Merge Comparisons > Consolidates multiple comparisons into a single comparison #### Sourcery refactoring id: `merge-comparisons` [Section titled “Sourcery refactoring id: merge-comparisons”](#sourcery-refactoring-id-merge-comparisons) #### Description: [Section titled “Description:”](#description) Consolidates multiple comparisons into a single comparison #### Before: [Section titled “Before:”](#before) ```python def process_payment(payment): if payment.currency == "USD" or payment.currency == "EUR": process_standard_payment(payment) else: process_international_payment(payment) ``` #### After: [Section titled “After:”](#after) ```python def process_payment(payment): if payment.currency in ["USD", "EUR"]: process_standard_payment(payment) else: process_international_payment(payment) ``` #### Explanation: [Section titled “Explanation:”](#explanation) We often have to compare a value to one of several possible others. When written out like the ‘Before’ example we have to look through each comparison to understand it, as well as mentally processing the boolean operator. By using the `in` operator and moving the values we are comparing to into a collection we can simplify things. This has avoided a little bit of duplication, and the conditional can now be taken in and understood with one glance. # Merge Dictionary Assignments > Declare the dictionary with values rather than creating an empty one and assigning to it #### Sourcery refactoring id: `merge-dict-assign` [Section titled “Sourcery refactoring id: merge-dict-assign”](#sourcery-refactoring-id-merge-dict-assign) #### Description: [Section titled “Description:”](#description) Declare the dictionary with values rather than creating an empty one and assigning to it #### Before: [Section titled “Before:”](#before) ```python hats_i_own = {} hats_i_own["panama"] = 1 hats_i_own["baseball_cap"] = 2 hats_i_own["bowler"] = 23 ``` #### After: [Section titled “After:”](#after) ```python hats_i_own = {"panama": 1, "baseball_cap": 2, "bowler": 23} ``` #### Explanation: [Section titled “Explanation:”](#explanation) When declaring a dictionary and filling it up with values one way that can come naturally is to declare it as empty and then add entries to it. This can be done in place, shortening the code and making the intent more explicit. Now I just need to glance at one line to see that I’m filling a variable with hats, rather than four. The same holds true for filling up other collection types like sets and lists. # Merge Duplicate Blocks > Restructure conditional to merge duplicate branches together #### Sourcery refactoring id: `merge-duplicate-blocks` [Section titled “Sourcery refactoring id: merge-duplicate-blocks”](#sourcery-refactoring-id-merge-duplicate-blocks) #### Description: [Section titled “Description:”](#description) Restructure conditional to merge duplicate branches together #### Before: [Section titled “Before:”](#before) ```python def process_payment(payment): if payment.currency == "USD": process_standard_payment(payment) elif payment.currency == "EUR": process_standard_payment(payment) else: process_international_payment(payment) ``` #### After: [Section titled “After:”](#after) ```python def process_payment(payment): if payment.currency == "USD" or payment.currency == "EUR": process_standard_payment(payment) else: process_international_payment(payment) ``` #### Explanation: [Section titled “Explanation:”](#explanation) We should always be searching out opportunities to remove duplicated code. A good place to do so is where there are multiple identical blocks inside an `if..elif` chain. This refactoring combines such blocks. Now if we need to change the `process_standard_payment(payment)` line we can do it in one place instead of two. This becomes even more important if these blocks involve multiple lines. # Merge Else If Into Elif > Merge else clause's nested if statement into elif #### Sourcery refactoring id: `merge-else-if-into-elif` [Section titled “Sourcery refactoring id: merge-else-if-into-elif”](#sourcery-refactoring-id-merge-else-if-into-elif) #### Description: [Section titled “Description:”](#description) Merge else clause’s nested if statement into elif #### Before: [Section titled “Before:”](#before) ```python def interpret_response(response): if response.status == "200": return response.data else: if response.status == "404": return "Not Found" else: return "Error" ``` #### After: [Section titled “After:”](#after) ```python def interpret_response(response): if response.status == "200": return response.data elif response.status == "404": return "Not Found" else: return "Error" ``` #### Explanation: [Section titled “Explanation:”](#explanation) Flattening if statements nested within else clauses generates code that is easier to read and expand upon. # Merge Exception Handlers > Merge exception handlers with the same body into a single except handler. #### Sourcery refactoring id: `merge-except-handler` [Section titled “Sourcery refactoring id: merge-except-handler”](#sourcery-refactoring-id-merge-except-handler) #### Description [Section titled “Description”](#description) Merge exception handlers with the same body into a single except handler. #### Before [Section titled “Before”](#before) ```python try: f = open("myfile.txt") s = f.readline() i = int(s.strip()) except OSError as err: logger.exception("Error while reading myfile.txt") raise except ValueError as err: logger.exception("Error while reading myfile.txt") raise ``` #### After [Section titled “After”](#after) ```python try: f = open("myfile.txt") s = f.readline() i = int(s.strip()) except (OSError, ValueError) as err: logger.exception("Error while reading myfile.txt") raise ``` #### Explanation [Section titled “Explanation”](#explanation) By merging except handlers with the same body we remove duplicate code. This makes it easier to read, and when changing the logic we won’t accidentally change it in only one place instead of both. #### Related Rules [Section titled “Related Rules”](#related-rules) * [remove-redundant-except-handler](/reference/refactorings/python/remove-redundant-except-handler/) to remove unreachable `except` blocks * [remove-redundant-exception](/reference/refactorings/python/remove-redundant-exception/) to clean up the tuple of an except clause * [use-contextlib-suppress](/reference/refactorings/python/use-contextlib-suppress/) instead of empty `except` blocks * [do-not-use-bare-except](/reference/refactorings/python/do-not-use-bare-except/) to always specify which errors an `except` block is handling # Merge isinstance > Combines together multiple isinstance functions #### Sourcery refactoring id: `merge-isinstance` [Section titled “Sourcery refactoring id: merge-isinstance”](#sourcery-refactoring-id-merge-isinstance) #### Description: [Section titled “Description:”](#description) Combines together multiple `isinstance` functions #### Before: [Section titled “Before:”](#before) ```python if isinstance(hat, Bowler) or isinstance(hat, Fedora): wear(hat) ``` #### After: [Section titled “After:”](#after) ```python if isinstance(hat, (Bowler, Fedora)): wear(hat) ``` #### Explanation: [Section titled “Explanation:”](#explanation) When you want to check whether something is one of multiple different types, you can merge the two `isinstance` checks into a single call. This is shorter while staying nice and easy to read. # Merge List Append > Create the list with values instead of creating an empty list and appending to it #### Sourcery refactoring id: `merge-list-append` [Section titled “Sourcery refactoring id: merge-list-append”](#sourcery-refactoring-id-merge-list-append) #### Description: [Section titled “Description:”](#description) Create the list with values instead of creating an empty list and appending to it #### Before: [Section titled “Before:”](#before) ```python hats_i_own = [] hats_i_own.append("panama") hats_i_own.append("baseball_cap") hats_i_own.append("bowler") ``` #### After: [Section titled “After:”](#after) ```python hats_i_own = ["panama", "baseball_cap", "bowler"] ``` #### Explanation: [Section titled “Explanation:”](#explanation) When declaring a list and filling it up with values one way that can come naturally is to declare it as empty and then append to it. This can be done in place, shortening the code and making the intent more explicit. Now I just need to glance at one line to see that I’m filling a variable with hats, rather than four. Doing it this way is also slightly more performant since it avoids the function calls to `append`. The same holds true for filling up other collection types like sets and dictionaries. # Merge List Appends Into Extend > Merge consecutive list appends into a single extend. #### Sourcery refactoring id: `merge-list-appends-into-extend` [Section titled “Sourcery refactoring id: merge-list-appends-into-extend”](#sourcery-refactoring-id-merge-list-appends-into-extend) #### Description: [Section titled “Description:”](#description) Merge consecutive list appends into a single extend. #### Before: [Section titled “Before:”](#before) ```python my_list.extend([1, 2]) my_list.append(3) my_list.append(4) my_list.extend((5, 6, 7)) my_list.append(8) my_list.append(9) ``` #### After: [Section titled “After:”](#after) ```python my_list.extend([1, 2, 3, 4, 5, 6, 7, 8, 9]) ``` #### Explanation: [Section titled “Explanation:”](#explanation) When adding multiple items to a list, it is cleaner to combine consecutive calls to `list.append` or `list.extend` into a single `list.extend` when possible. # Merge List Extend > Create the list with values instead of creating an empty list and extending it with another list #### Sourcery refactoring id: `merge-list-extend` [Section titled “Sourcery refactoring id: merge-list-extend”](#sourcery-refactoring-id-merge-list-extend) #### Description: [Section titled “Description:”](#description) Create the list with values instead of creating an empty list and extending it with another list #### Before: [Section titled “Before:”](#before) ```python hats_i_own = [] hats_i_own.extend(["panama, baseball_cap, bowler"]) ``` #### After: [Section titled “After:”](#after) ```python hats_i_own = ["panama", "baseball_cap", "bowler"] ``` #### Explanation: [Section titled “Explanation:”](#explanation) When declaring a list with values it is much clearer to do so on one line rather than declaring an empty list and then extending it with values. # Merge-Nested-Ifs > Merge nested if conditions #### Sourcery rule id: `merge-nested-ifs` [Section titled “Sourcery rule id: merge-nested-ifs”](#sourcery-rule-id-merge-nested-ifs) #### Description [Section titled “Description”](#description) Merge nested if conditions #### Before [Section titled “Before”](#before) ```python if a: if b: return c ``` #### After [Section titled “After”](#after) ```python if a and b: return c ``` #### Explanation [Section titled “Explanation”](#explanation) Too much nesting can make code difficult to understand, and this is especially true in Python, where there are no brackets to help out with the delineation of different nesting levels. Reading deeply nested code is confusing, since you have to keep track of which conditions relate to which levels. We therefore strive to reduce nesting where possible, and the situation where two `if` conditions can be combined using `and` is an easy win. # Merge Repeated Ifs > Merges together the interior contents of if statements with identical conditions #### Sourcery refactoring id: `merge-repeated-ifs` [Section titled “Sourcery refactoring id: merge-repeated-ifs”](#sourcery-refactoring-id-merge-repeated-ifs) #### Description: [Section titled “Description:”](#description) Merges together the interior contents of `if` statements with identical conditions #### Before: [Section titled “Before:”](#before) ```python if wardrobe.hats: self.happiness += 1 else: self.happiness -= 1 if wardrobe.hats: self.stylishness += 1 else: self.stylishness -= 1 ``` #### After: [Section titled “After:”](#after) ```python if wardrobe.hats: self.happiness += 1 self.stylishness += 1 else: self.happiness -= 1 self.stylishness -= 1 ``` #### Explanation: [Section titled “Explanation:”](#explanation) Where subsequent `if` statements have identical conditions it is logically equivalent to merge them together. This shortens the code and makes it easier to read and understand. Note that this can only be done if the statements inside the first `if` condition have no effect on the condition itself. # Merge Set Add > Create the set with values instead of declaring an empty set and adding to it #### Sourcery refactoring id: `merge-set-add` [Section titled “Sourcery refactoring id: merge-set-add”](#sourcery-refactoring-id-merge-set-add) #### Description: [Section titled “Description:”](#description) Create the set with values instead of declaring an empty set and adding to it #### Before: [Section titled “Before:”](#before) ```python hats_i_own = set() hats_i_own.add("panama") hats_i_own.add("baseball_cap") hats_i_own.add("bowler") ``` #### After: [Section titled “After:”](#after) ```python hats_i_own = {"panama", "baseball_cap", "bowler"} ``` #### Explanation: [Section titled “Explanation:”](#explanation) When declaring a set and filling it up with values one way that can come naturally is to declare it as empty and then add to it. This can be done in place, shortening the code and making the intent more explicit. Now I just need to glance at one line to see that I’m filling a variable with hats, rather than four. Doing it this way is also slightly more performant since it avoids the function calls to `add`. The same holds true for filling up other collection types like lists and dictionaries. # Method_chaining > Chaining methods improves readability #### Sourcery rule id: `method_chaining` [Section titled “Sourcery rule id: method\_chaining”](#sourcery-rule-id-method_chaining) #### Description [Section titled “Description”](#description) Chaining methods improves readability #### Before [Section titled “Before”](#before) ```python df = df.drop_duplicates() df = df.reset_index(drop=True) ``` #### After [Section titled “After”](#after) ```python df = df.drop_duplicates().reset_index(drop=True) ``` #### Explanation [Section titled “Explanation”](#explanation) Chaining methods together can improve readability and performance in Pandas. Instead of applying methods one after another, try chaining them together to create a single expression. # Min/Max identity > Replaces duplicate conditionals looking for the minimum or maximum value of multiple variables with a min or max function #### Sourcery refactoring id: `min-max-identity` [Section titled “Sourcery refactoring id: min-max-identity”](#sourcery-refactoring-id-min-max-identity) #### Description: [Section titled “Description:”](#description) Replaces duplicate conditionals looking for the minimum or maximum value of multiple variables with a `min` or `max` function #### Before: [Section titled “Before:”](#before) ```python if first_hat.price < second_hat.price: cheapest_hat_price = first_hat.price else: cheapest_hat_price = second_hat.price ``` ```python if sale_price >= 10: sale_price = 10 ``` #### After: [Section titled “After:”](#after) ```python cheapest_hat_price = min(first_hat.price, second_hat.price) ``` ```python sale_price = min(sale_price, 10) ``` #### Explanation: [Section titled “Explanation:”](#explanation) We often need to work out the smallest or largest of two values, and the quickest way to do this in Python is to use the built-in `min` and `max` functions. This results in a shorter and clearer way to achieve the same result. The same functions also offer a shortcut for when we want to put a cap or a floor on the value of a variable. # Missing Dict Items > Add missing .items() call when unpacking a dictionary #### Sourcery refactoring id: `missing-dict-items` [Section titled “Sourcery refactoring id: missing-dict-items”](#sourcery-refactoring-id-missing-dict-items) #### Description [Section titled “Description”](#description) Add missing `.items()` call when unpacking a dictionary #### Before [Section titled “Before”](#before) ```python data = {"a": 1, "b": 2} for key, value in data: process(key, value) ``` ```python data = {"x": 1, "y": 2, "z": 3} filtered = {k: v for k, v in data if k == "x"} ``` #### After [Section titled “After”](#after) ```python data = {"a": 1, "b": 2} for key, value in data.items(): process(key, value) ``` ```python data = {"x": 1, "y": 2, "z": 3} filtered = {k: v for k, v in data.items() if k == "x"} ``` #### Explanation [Section titled “Explanation”](#explanation) When writing a loop to unpack a dictionary it is easy to forget to call `.items()`. This suggestion adds in the missing call. # Move Assign > Moves assignment of variables closer to their usage #### Sourcery refactoring id: `move-assign` [Section titled “Sourcery refactoring id: move-assign”](#sourcery-refactoring-id-move-assign) #### Description: [Section titled “Description:”](#description) Moves assignment of variables closer to their usage #### Before: [Section titled “Before:”](#before) ```python def should_i_wear_this_hat(self, hat): if not isinstance(hat, Hat): return False weather_outside = self.look_out_of_window() is_stylish = isinstance(hat, StylishHat) if weather_outside.is_raining: print("Damn.") return True else: print("Great.") return is_stylish ``` #### After: [Section titled “After:”](#after) ```python def should_i_wear_this_hat(self, hat): if not isinstance(hat, Hat): return False weather_outside = self.look_out_of_window() if weather_outside.is_raining: print("Damn.") return True else: print("Great.") is_stylish = isinstance(hat, StylishHat) return is_stylish ``` #### Explanation: [Section titled “Explanation:”](#explanation) The scope of local variables should always be as tightly defined as possible and practicable. This means that: * You don’t have to keep the variable in your working memory through the parts of the function where it’s not needed. This cuts down on the cognitive load of understanding your code. * If code is in coherent blocks where variables are declared and used together, it makes it easier to split functions apart, which can lead to shorter, easier to understand methods. * If variables are declared far from their usage, they can become stranded. If the code where they are used is later changed or removed unused variables can be left sitting around, complicating the code unnecessarily. # Move Assign In Block > Moves assignment of variables closer to their usage within a block #### Sourcery refactoring id: `move-assign-in-block` [Section titled “Sourcery refactoring id: move-assign-in-block”](#sourcery-refactoring-id-move-assign-in-block) #### Description: [Section titled “Description:”](#description) Moves assignment of variables closer to their usage within a block #### Before: [Section titled “Before:”](#before) ```python cubes = [] function_unrelated_to_cubes() if another_unrelated_condition(): more_unrelated_logic() for i in range(20): cubes.append(i**3) ``` #### After: [Section titled “After:”](#after) ```python function_unrelated_to_cubes() if another_unrelated_condition(): more_unrelated_logic() cubes = [] for i in range(20): cubes.append(i**3) ``` #### Explanation: [Section titled “Explanation:”](#explanation) The scope of local variables should always be as tightly defined as possible and practicable. This means that: * You don’t have to keep the variable in your working memory through the parts of the function where it’s not needed. This cuts down on the cognitive load of understanding your code. * If code is in coherent blocks where variables are declared and used together, it makes it easier to split functions apart, which can lead to shorter, easier to understand methods. * If variables are declared far from their usage, they can become stranded. If the code where they are used is later changed or removed unused variables can be left sitting around, complicating the code unnecessarily. # No-Conditionals-In-Tests > Avoid conditionals in tests. #### Sourcery rule id: `no-conditionals-in-tests` [Section titled “Sourcery rule id: no-conditionals-in-tests”](#sourcery-rule-id-no-conditionals-in-tests) #### Description [Section titled “Description”](#description) Avoid conditionals in tests. #### Match [Section titled “Match”](#match) ```python def test_something(): some_code() if nr < 42: verify_small_number(nr) else: verify_big_number(nr) ``` #### Explanation [Section titled “Explanation”](#explanation) Avoid complex code, like conditionals, in test functions. Google’s software engineering guidelines says: “Clear tests are trivially correct upon inspection” To reach that avoid complex code in tests: * loops * conditionals Some ways to fix this: * Use parametrized tests to get rid of the loop. * Move the complex logic into helpers. * Move the complex part into pytest fixtures. > Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn’t take much logic to make a test more difficult to reason about. Software Engineering at Google / [Don’t Put Logic in Tests](https://abseil.io/resources/swe-book/html/ch12.html#donapostrophet_put_logic_in_tests) # No-Loop-In-Tests > Avoid loops in tests. #### Sourcery rule id: `no-loop-in-tests` [Section titled “Sourcery rule id: no-loop-in-tests”](#sourcery-rule-id-no-loop-in-tests) #### Description [Section titled “Description”](#description) Avoid loops in tests. #### Match [Section titled “Match”](#match) ```python def test_something(): some_code() for i in range(len(currencies)): verify(currencies[i]) ``` #### Explanation [Section titled “Explanation”](#explanation) Avoid complex code, like loops, in test functions. Google’s software engineering guidelines says: “Clear tests are trivially correct upon inspection” To reach that avoid complex code in tests: * loops * conditionals Some ways to fix this: * Use parametrized tests to get rid of the loop. * Move the complex logic into helpers. * Move the complex part into pytest fixtures. > Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn’t take much logic to make a test more difficult to reason about. Software Engineering at Google / [Don’t Put Logic in Tests](https://abseil.io/resources/swe-book/html/ch12.html#donapostrophet_put_logic_in_tests) # Non Equal Comparison > Simplify comparison of non equal values #### Sourcery refactoring id: `non-equal-comparison` [Section titled “Sourcery refactoring id: non-equal-comparison”](#sourcery-refactoring-id-non-equal-comparison) #### Description [Section titled “Description”](#description) Simplify comparison of non equal values #### Before [Section titled “Before”](#before) ```python def compare(a: int, b: int): if a < b: return True elif a > b: return False ``` #### After [Section titled “After”](#after) ```python def compare(a: int, b: int): if a != b: return a < b ``` #### Explanation [Section titled “Explanation”](#explanation) If we are comparing two numbers and returning only if they are non equal we can simplifiy the form of this expression. Removing an if statement and merging two comparisons makes the code shorter and easier to read. # None Compare > Replaces == with is when comparing to None #### Sourcery refactoring id: `none-compare` [Section titled “Sourcery refactoring id: none-compare”](#sourcery-refactoring-id-none-compare) #### Description: [Section titled “Description:”](#description) Replaces `==` with `is` when comparing to `None` #### Before: [Section titled “Before:”](#before) ```python if hat == None: raise NoHatException ``` #### After: [Section titled “After:”](#after) ```python if hat is None: raise NoHatException ``` #### Explanation: [Section titled “Explanation:”](#explanation) In Python `is` refers to reference equality - where you want to check if something is the same object. Equals `==` checks value equality, to check that the objects are equal to each other. In Python the `None` object is a singleton, so it is correct to use `is` when comparing to it. # Or-If-Exp-Identity > Replace if-expression with `or` #### Sourcery rule id: `or-if-exp-identity` [Section titled “Sourcery rule id: or-if-exp-identity”](#sourcery-rule-id-or-if-exp-identity) #### Description [Section titled “Description”](#description) Replace if-expression with `or` #### Before [Section titled “Before”](#before) ```python currency = input_currency if input_currency else DEFAULT_CURRENCY ``` #### After [Section titled “After”](#after) ```python currency = input_currency or DEFAULT_CURRENCY ``` #### Explanation [Section titled “Explanation”](#explanation) Here we find ourselves setting a value if it evaluates to `True`, and otherwise using a default. The ‘After’ case is a bit easier to read and avoids the duplication of `input_currency`. It works because the left-hand side is evaluated first. If it evaluates to true then `currency` will be set to this and the right-hand side will not be evaluated. If it evaluates to false the right-hand side will be evaluated and `currency` will be set to `DEFAULT_CURRENCY`. # Pandas: Avoid inplace > Don't use inplace for methods that always create a copy under the hood. #### Sourcery suggestion id: `pandas-avoid-inplace` [Section titled “Sourcery suggestion id: pandas-avoid-inplace”](#sourcery-suggestion-id-pandas-avoid-inplace) Available starting with version 1.1.0 #### Description [Section titled “Description”](#description) Don’t use `inplace` for methods that always create a copy under the hood. #### Before [Section titled “Before”](#before) ```python import pandas as pd df = pd.DataFrame( [ ["Python", 190], ["JavaScript", 33], ], columns=["Language", "Number of rules"], ) df.sort_values("Language", inplace=True) ``` #### After [Section titled “After”](#after) ```python import pandas as pd df = pd.DataFrame( [ ["Python", 190], ["JavaScript", 33], ], columns=["Language", "Number of rules"], ) df = df.sort_values("Language") ``` #### Before [Section titled “Before”](#before-1) ```python import pandas as pd df = pd.DataFrame( [ ["Python", 190], ["JavaScript", 33], ], columns=["Language", "Number of rules"], ) df.copy().sort_values("Language", inplace=True) ``` #### After [Section titled “After”](#after-1) ```python import pandas as pd df = pd.DataFrame( [ ["Python", 190], ["JavaScript", 33], ], columns=["Language", "Number of rules"], ) df.copy().sort_values("Language") ``` #### Explanation [Section titled “Explanation”](#explanation) Some `DataFrame` methods can never operate inplace. Their operation (like reordering rows) requires copying, so they create a copy even if you provide `inplace=True`. For these methods, `inplace` doesn’t bring a performance gain. It’s only a “syntactic sugar for reassigning the new result to the calling DataFrame/Series.” Drawbacks of using `inplace`: > * You can’t use method chaining with `inplace=True` > * The `inplace` keyword complicates type annotations (because the return value depends on the value of `inplace`) > * Using `inplace=True` gives code that mutates the state of an object and thus has side-effects. That can introduce subtle bugs and is harder to debug. [PDEP-8](https://github.com/pandas-dev/pandas/pull/51466) This PDEP suggests to deprecate the `inplace` option for methods that can never operate inplace. Best practice: Explicitly reassign the result to the caller `DataFrame`. E.g. ```python df = df.sort_values("language") ``` In cases, where the caller isn’t a variable but an expression, `inplace` doesn’t have an effect anyway. ```python df.copy().sort_values("Language", inplace=True) ``` `copy` creates a new `DataFrame` object, which isn’t assigned to any variable. `inplace` doesn’t change the `df` object, but this copy result object instead. In this case, the only effect of `inplace` is that the expression returns `None` instead of a new `DataFrame`. Thus, it should be omitted for clarity. ```python df.copy().sort_values("Language") ``` #### DataFrame Methods Affected [Section titled “DataFrame Methods Affected”](#dataframe-methods-affected) These `DataFrame` methods always create a copy under the hood even if you provide the `inplace` keyword. In [PDEP-8](https://github.com/pandas-dev/pandas/pull/51466), they are mentioned as “Group 4” methods. * `dropna` * `drop_duplicates` * `sort_values` * `sort_index` * `eval` * `query` # pathlib.Path Read > Simplify basic file reads with pathlib. #### Sourcery refactoring id: `path-read` [Section titled “Sourcery refactoring id: path-read”](#sourcery-refactoring-id-path-read) #### Description: [Section titled “Description:”](#description) Simplify basic file reads with `pathlib`. #### Before: [Section titled “Before:”](#before) ```python with open("file.txt") as f: file_contents = f.read() ``` #### After: [Section titled “After:”](#after) ```python import pathlib file_contents = pathlib.Path("file.txt").read_text() ``` #### Explanation: [Section titled “Explanation:”](#explanation) `pathlib` is Python’s built-in object-oriented file system path API. `Path`s are more versatile than strings and have cross-platform support. The [`Path.read_text`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.read_text) method opens the file, reads it in text-mode and returns its contents making sure it gets closed. By using this method, we save an indentation level and remove the need for manually using a context manager. This refactoring also ensures that `pathlib` gets imported in case it is not already available. # Raise From Previous Error > Suggests raising from a previously-raised exception. #### Sourcery refactoring id: `raise-from-previous-error` [Section titled “Sourcery refactoring id: raise-from-previous-error”](#sourcery-refactoring-id-raise-from-previous-error) #### Description: [Section titled “Description:”](#description) Suggests raising from a previously-raised exception. #### Before: [Section titled “Before:”](#before) ```python x = int(input()) try: print(1 / x) except ZeroDivisionError: raise ValueError("Can't divide by zero") ``` #### After: [Section titled “After:”](#after) ```python x = int(input()) try: print(1 / x) except ZeroDivisionError as e: raise ValueError("Can't divide by zero") from e ``` #### Explanation: [Section titled “Explanation:”](#explanation) Exception chaining was introduced in Python 3 [PEP 3134](https://www.python.org/dev/peps/pep-3134/), and is an implicit part of exception handling. This suggestion converts the implicit exception chain into an explicit one, as suggested by Pylint’s `raise-missing-from` error. # Raise-Specific-Error > Raise a specific error instead of the general `Exception` or `BaseException` #### Sourcery rule id: `raise-specific-error` [Section titled “Sourcery rule id: raise-specific-error”](#sourcery-rule-id-raise-specific-error) #### Description [Section titled “Description”](#description) Raise a specific error instead of the general `Exception` or `BaseException` #### Match [Section titled “Match”](#match) ```python raise Exception ``` #### Explanation [Section titled “Explanation”](#explanation) If a piece of code raises a specific exception type rather than the generic [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) or [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), the calling code can: * get more information about what type of error it is * define specific exception handling for it This way, callers of the code can handle the error appropriately. How can you solve this? * Use one of the [built-in exceptions](https://docs.python.org/3/library/exceptions.html) of the standard library. * [Define your own error class](https://docs.python.org/3/tutorial/errors.html#tut-userexceptions) that subclasses `Exception`. So instead of having code raising `Exception` or `BaseException` like ```python if incorrect_input(value): raise Exception("The input is incorrect") ``` you can have code raising a specific error like ```python if incorrect_input(value): raise ValueError("The input is incorrect") ``` or ```python class IncorrectInputError(Exception): pass if incorrect_input(value): raise IncorrectInputError("The input is incorrect") ``` # Reintroduce Else > Lift code into else after break in control flow #### Sourcery refactoring id: `reintroduce-else` [Section titled “Sourcery refactoring id: reintroduce-else”](#sourcery-refactoring-id-reintroduce-else) #### Description: [Section titled “Description:”](#description) Lift code into `else` after break in control flow #### Before: [Section titled “Before:”](#before) ```python summer_hats = [] for hat in hats: if hat in WINTER_HATS: continue summer_hats.append(hat) ``` #### After: [Section titled “After:”](#after) ```python summer_hats = [] for hat in hats: if hat in WINTER_HATS: continue else: summer_hats.append(hat) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Where the body of an `if` statement ends with a break in the control flow, such as a `continue`, `return` or `raise`, the subsequent statements can be lifted into the `else` clause. On its own this change does not improve the code, so Sourcery will only suggest it where it unlocks furher improvements. In the example above once the code has been lifted into the `else` the conditional can be inverted and the `continue` removed, which then lets the `for` loop be converted into a list comprehension. # Remove Assert True > Remove assert True statements #### Sourcery refactoring id: `remove-assert-true` [Section titled “Sourcery refactoring id: remove-assert-true”](#sourcery-refactoring-id-remove-assert-true) #### Description [Section titled “Description”](#description) Remove `assert True` statements #### Before [Section titled “Before”](#before) ```python print("Checking things") assert True assert True, "Always true" assert len([0, 1, 2]) >= 0, "This is also always true" print("Done") ``` #### After [Section titled “After”](#after) ```python print("Checking things") print("Done") ``` #### Explanation [Section titled “Explanation”](#explanation) Assertions are useful to make sure that some conditions hold during the execution of a program. However, when the expression being evaluated is always true, no assertion is necessary, and removing it makes code simpler and easier to understand. # Remove Dict Items > Remove unnecessary calls to dict.items when the values are not used. #### Sourcery refactoring id: `remove-dict-items` [Section titled “Sourcery refactoring id: remove-dict-items”](#sourcery-refactoring-id-remove-dict-items) #### Description: [Section titled “Description:”](#description) Remove unnecessary calls to `dict.items` when the values are not used. #### Before: [Section titled “Before:”](#before) ```python for name, age in people.items(): print("Hi, my name is", name) ``` #### After: [Section titled “After:”](#after) ```python for name in people: print("Hi, my name is", name) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Calling [`dict.items`](https://docs.python.org/3/library/stdtypes.html#dict.items) is only necessary when both the dictionary keys and values are used. When the values are not used, it is sufficient to iterate over the dictionary keys only. The code is now easier to read, avoiding an unnecessary call to `dict.items`, and removing an unused variable - the dictionary values. # Remove Dictionary Keys > Removes unnecessary call to keys() when iterating over a dictionary #### Sourcery refactoring id: `remove-dict-keys` [Section titled “Sourcery refactoring id: remove-dict-keys”](#sourcery-refactoring-id-remove-dict-keys) #### Description: [Section titled “Description:”](#description) Removes unnecessary call to `keys()` when iterating over a dictionary #### Before: [Section titled “Before:”](#before) ```python for currency in currencies.keys(): process(currency) ``` #### After: [Section titled “After:”](#after) ```python for currency in currencies: process(currency) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Sometimes when iterating over a dictionary you only need to use the dictionary keys. In this case the call to `keys()` is unnecessary, since the default behaviour when iterating over a dictionary is to iterate over the keys. The code is now slightly cleaner and easier to read, and avoiding a function call will yield a (small) performance improvement. # Remove Duplicate Dict Key > Remove duplicate keys when instantiating dicts. #### Sourcery suggestion id: `remove-duplicate-dict-key` [Section titled “Sourcery suggestion id: remove-duplicate-dict-key”](#sourcery-suggestion-id-remove-duplicate-dict-key) #### Description: [Section titled “Description:”](#description) Remove duplicate keys when instantiating `dict`s. #### Before: [Section titled “Before:”](#before) ```python my_dict = {a: 1, b: 2, a: 3, **d1, **d2, **d1} ``` #### After: [Section titled “After:”](#after) ```python my_dict = {b: 2, a: 3, **d2, **d1} ``` #### Explanation: [Section titled “Explanation:”](#explanation) Dictionary keys must be unique. Hence, repeated keys are redundant and can be removed from its inialization to increase the conciseness and clarity of the code. !!! note “Note” This is a breaking change in Python 3.7+ since `dict` keys are kept in their insertion order. By applying this *suggestion*, the order of insertion is changed. If your application does not rely on this ordering, then there won’t be any changes in behaviour. # Remove Duplicate Set Key > Remove duplicate keys when instantiating sets. #### Sourcery refactoring id: `remove-duplicate-set-key` [Section titled “Sourcery refactoring id: remove-duplicate-set-key”](#sourcery-refactoring-id-remove-duplicate-set-key) #### Description: [Section titled “Description:”](#description) Remove duplicate keys when instantiating `set`s. #### Before: [Section titled “Before:”](#before) ```python addresses = {"here", *address_list1, *address_list2, *address_list1, "there", "here"} ``` #### After: [Section titled “After:”](#after) ```python addresses = {*address_list2, *address_list1, "there", "here"} ``` #### Explanation: [Section titled “Explanation:”](#explanation) Set keys must be unique. Hence, repeated keys are redundant and can be removed from its inialization to increase the conciseness and clarity of the code. # Remove Empty Nested Block > Remove nested block which has no effect #### Sourcery refactoring id: `remove-empty-nested-block` [Section titled “Sourcery refactoring id: remove-empty-nested-block”](#sourcery-refactoring-id-remove-empty-nested-block) #### Description: [Section titled “Description:”](#description) Remove nested block which has no effect #### Before: [Section titled “Before:”](#before) ```python for i in range(3): pass ``` #### After: [Section titled “After:”](#after) ```python ``` #### Explanation: [Section titled “Explanation:”](#explanation) An `if` or `for` which is empty and has no effect should not be present in the code. It clutters things up and makes it harder to see what the code is really doing. # Remove-None-From-Default-Get > Replace `dict.get(x, None)` with `dict.get(x)` #### Sourcery rule id: `remove-none-from-default-get` [Section titled “Sourcery rule id: remove-none-from-default-get”](#sourcery-rule-id-remove-none-from-default-get) #### Description [Section titled “Description”](#description) Replace `dict.get(x, None)` with `dict.get(x)` #### Before [Section titled “Before”](#before) ```python hats = {"bowler": Bowler(), "sombrero": Sombrero()} fedora = hats.get("fedora", None) ``` #### After [Section titled “After”](#after) ```python hats = {"bowler": Bowler(), "sombrero": Sombrero()} fedora = hats.get("fedora") ``` #### Explanation [Section titled “Explanation”](#explanation) When using a dictionary’s `get` method you can specify a default to return if the key is not found. This defaults to `None`, so it is unnecessary to specify `None` if this is the required behaviour. Removing the unnecessary argument makes the code slightly shorter and clearer. # Remove Pass From Body > Removes a pass from the body of a conditional by inverting it #### Sourcery refactoring id: `remove-pass-body` [Section titled “Sourcery refactoring id: remove-pass-body”](#sourcery-refactoring-id-remove-pass-body) #### Description: [Section titled “Description:”](#description) Removes a `pass` from the body of a conditional by inverting it #### Before: [Section titled “Before:”](#before) ```python if not a: pass else: return c ``` #### After: [Section titled “After:”](#after) ```python if a: return c ``` #### Explanation: [Section titled “Explanation:”](#explanation) It is much clearer for a conditional to do something in its main body than solely in its `else` clause. In the latter case anyone reading the code has to mentally invert the condition in order to determine the meaning. # Remove Pass From Elif > Removes a pass from the elif section of a conditional #### Sourcery refactoring id: `remove-pass-elif` [Section titled “Sourcery refactoring id: remove-pass-elif”](#sourcery-refactoring-id-remove-pass-elif) #### Description: [Section titled “Description:”](#description) Removes a `pass` from the elif section of a conditional #### Before: [Section titled “Before:”](#before) ```python if a: x() elif b: pass else: return c ``` #### After: [Section titled “After:”](#after) ```python if a: x() elif not b: return c ``` #### Explanation: [Section titled “Explanation:”](#explanation) Reducing the number of branches in a conditional reduces the mental load of reading and understanding it considerably. This simplification can be made where an empty `elif` branch is immediately followed by the `else`. # Remove Redundant Boolean > Removes redundant booleans from tests. #### Sourcery refactoring id: `remove-redundant-boolean` [Section titled “Sourcery refactoring id: remove-redundant-boolean”](#sourcery-refactoring-id-remove-redundant-boolean) #### Description [Section titled “Description”](#description) Removes redundant booleans from tests. #### Before [Section titled “Before”](#before) ```python hat_prices = [] while True and hat in hats: hat_prices.append(hat.price) hats.remove(hat) assert len(hat_prices) > 10 and False ``` #### After [Section titled “After”](#after) ```python while hat in hats: hat_prices.append(hat.price) hats.remove(hat) assert False ``` #### Explanation [Section titled “Explanation”](#explanation) Boolean values like `True` or `False` in boolean operations like `and` or `or` may be redundant, and can be removed to simplify the code without affecting its functionality. This refactoring will often compose with other refactorings where boolean operations have been combined. See also: [`remove-redundant-if`](/reference/refactorings/python/remove-redundant-if/) # Remove Redundant Condition > Remove a redundant condition used during variable assignment #### Sourcery refactoring id: `remove-redundant-condition` [Section titled “Sourcery refactoring id: remove-redundant-condition”](#sourcery-refactoring-id-remove-redundant-condition) #### Description: [Section titled “Description:”](#description) Remove a redundant condition used during variable assignment #### Before: [Section titled “Before:”](#before) ```python x = y if z else y ``` #### After: [Section titled “After:”](#after) ```python x = y ``` #### Explanation: [Section titled “Explanation:”](#explanation) Code may contain redundant statements where a condition is unnecessarily evaluated, and the result will be the same either way. This refactoring detects such redundancy and removes it. # Remove Redundant Constructor in Dict Union #### Sourcery refactoring id: `remove-redundant-constructor-in-dict-union` [Section titled “Sourcery refactoring id: remove-redundant-constructor-in-dict-union”](#sourcery-refactoring-id-remove-redundant-constructor-in-dict-union) #### Description [Section titled “Description”](#description) #### Before [Section titled “Before”](#before) ```python x = dict(y) | z ``` #### After [Section titled “After”](#after) ```python x = y | z ``` #### Explanation [Section titled “Explanation”](#explanation) The Python dictionary union operator returns a new dictionary, without modifying any of its operands. As a result, dictionary copies or reconstructions are unnecessary, and clutter the code. See also: [`unwrap-iterable-construction`](/reference/refactorings/python/unwrap-iterable-construction/), [`dict-assign-update-to-union`](/reference/refactorings/python/dict-assign-update-to-union/) # Remove Redundant Continue > Remove redundant continue statement #### Sourcery refactoring id: `remove-redundant-continue` [Section titled “Sourcery refactoring id: remove-redundant-continue”](#sourcery-refactoring-id-remove-redundant-continue) #### Description: [Section titled “Description:”](#description) Remove redundant `continue` statement #### Before: [Section titled “Before:”](#before) ```python mylist2 = [] for i in mylist: if i != 2: mylist2.append(i) else: continue ``` #### After: [Section titled “After:”](#after) ```python mylist2 = [] for i in mylist: if i != 2: mylist2.append(i) ``` #### Explanation: [Section titled “Explanation:”](#explanation) If a `continue` is not followed by any other statements in a `for` or `while` loop then it is not necessary, so can be removed. Removing unnecessary lines declutters the code and makes it easier to understand. This refactoring will only be triggered if it unlocks further improvements. # Remove Redundant Except Handler > Removes exception handlers that can never trigger (as the exceptions have already been caught) #### Sourcery refactoring id: `remove-redundant-except-handler` [Section titled “Sourcery refactoring id: remove-redundant-except-handler”](#sourcery-refactoring-id-remove-redundant-except-handler) #### Description: [Section titled “Description:”](#description) Removes exception handlers that can never trigger (as the exceptions have already been caught) #### Before: [Section titled “Before:”](#before) ```python try: pass except Exception: do_x() except ValueError: do_y() finally: do_t() ``` #### After: [Section titled “After:”](#after) ```python try: pass except Exception: do_x() finally: do_t() ``` #### Explanation: [Section titled “Explanation:”](#explanation) Currently only one except handler can be triggered for any exception raised in Python. This means that if an exception is already caught, subsequent handlers can never have an effect on it. Having them present is confusing, since the reader may think that this exception handling code is behaving as expected, when in fact it is redundant. #### Related Rules [Section titled “Related Rules”](#related-rules) * [merge-except-handler](/reference/refactorings/python/merge-except-handler/) to merge except handlers with the same content * [remove-redundant-exception](/reference/refactorings/python/remove-redundant-exception/) to clean up the tuple of an except clause * [use-contextlib-suppress](/reference/refactorings/python/use-contextlib-suppress/) instead of empty `except` blocks * [do-not-use-bare-except](/reference/refactorings/python/do-not-use-bare-except/) to always specify which errors an `except` block is handling # Remove Redundant Exception > Remove redundant exceptions from an except clause. #### Sourcery refactoring id: `remove-redundant-exception` [Section titled “Sourcery refactoring id: remove-redundant-exception”](#sourcery-refactoring-id-remove-redundant-exception) #### Description [Section titled “Description”](#description) Remove redundant exceptions from an except clause. #### Before [Section titled “Before”](#before) ```python try: int("not an int") except (ValueError, Exception): logger.log("error") ``` #### After [Section titled “After”](#after) ```python try: int("not an int") except Exception: logger.log("error") ``` #### Explanation [Section titled “Explanation”](#explanation) An `except` clause handles all instances of the defined exception, incl. its subclasses. If an exception is a subclass of another exception, it’s redundant to mention both explicitly in the except clause. Note that: * All built-in, non-system-exiting exceptions are derived from the `Exception` class. * All user-defined exceptions should also be derived from the `Exception` class. * It’s a good practice to have a base exception class for your application (deriving from `Exception`) and have your more specific custom exceptions derive from it. #### Related Rules [Section titled “Related Rules”](#related-rules) * [merge-except-handler](/reference/refactorings/python/merge-except-handler/) to merge except handlers with the same content * [remove-redundant-except-handler](/reference/refactorings/python/remove-redundant-except-handler/) to remove unreachable `except` blocks * [use-contextlib-suppress](/reference/refactorings/python/use-contextlib-suppress/) instead of empty `except` blocks * [do-not-use-bare-except](/reference/refactorings/python/do-not-use-bare-except/) to always specify which errors an `except` block is handling # Remove Redundant f-string > If an f-string has no replacements turn it into a regular string. #### Sourcery refactoring id: `remove-redundant-fstring` [Section titled “Sourcery refactoring id: remove-redundant-fstring”](#sourcery-refactoring-id-remove-redundant-fstring) #### Description [Section titled “Description”](#description) If an f-string has no replacements turn it into a regular string. #### Before [Section titled “Before”](#before) ```python print(f"Why, oh why am I an f-string") ``` #### After [Section titled “After”](#after) ```python print("Why, oh why am I an f-string") ``` #### Explanation [Section titled “Explanation”](#explanation) Is not necessary to use an f-string that has no replacements so let’s keep it simple and just use a constant string. # Remove Redundant If Statements > Removes conditional tests where the conditional is always True or False #### Sourcery refactoring id: `remove-redundant-if` [Section titled “Sourcery refactoring id: remove-redundant-if”](#sourcery-refactoring-id-remove-redundant-if) #### Description: [Section titled “Description:”](#description) Removes conditional tests where the conditional is always `True` or `False` #### Before: [Section titled “Before:”](#before) ```python def hello(name: str): if name.startswith("L"): self.sing("Hip Hip Horray! For " + name) elif not name.startswith("L"): self.sing("Hello " + name + ", it's nice to meet you.") ``` #### After: [Section titled “After:”](#after) ```python def hello(name: str): if name.startswith("L"): self.sing("Hip Hip Horray! For " + name) else: self.sing("Hello " + name + ", it's nice to meet you.") ``` #### Explanation: [Section titled “Explanation:”](#explanation) This refactoring will re-structure conditionals where Sourcery determines that one of the tests is always `True` or `False`. This reveals the actual logic of the conditional to the reader, making the code easier to understand. See also: [`remove-redundant-boolean`](/reference/refactorings/python/remove-redundant-boolean/) # Remove Redundant Pass > Removes unnecessary pass statements #### Sourcery refactoring id: `remove-redundant-pass` [Section titled “Sourcery refactoring id: remove-redundant-pass”](#sourcery-refactoring-id-remove-redundant-pass) #### Description: [Section titled “Description:”](#description) Removes unnecessary `pass` statements #### Before: [Section titled “Before:”](#before) ```python if a: x() else: pass ``` ```python do_something() pass ``` #### After: [Section titled “After:”](#after) ```python if a: x() ``` ```python do_something() ``` #### Explanation: [Section titled “Explanation:”](#explanation) A `pass` statement is unnecessary if a block has other statements in it, and an `else` which only contains `pass` can be safely removed. Making these changes shortens the code, and the reader doesn’t have to consider and deal with the unnecessary `pass` statements. # Remove-Redundant-Path-Exists > Remove unnecessary `path.exists()` check. #### Sourcery rule id: `remove-redundant-path-exists` [Section titled “Sourcery rule id: remove-redundant-path-exists”](#sourcery-rule-id-remove-redundant-path-exists) #### Description [Section titled “Description”](#description) Remove unnecessary `path.exists()` check. #### Before [Section titled “Before”](#before) ```python from pathlib import Path p = Path("/home/user/mydir") if p.exists() and p.is_dir(): do_sth() ``` #### After [Section titled “After”](#after) ```python from pathlib import Path p = Path("/home/user/mydir") if p.is_dir(): do_sth() ``` #### Explanation [Section titled “Explanation”](#explanation) The following functions already contain a check whether a path exists: * `pathlib.Path.is_dir()` * `pathlib.Path.is_file()` * `pathlib.Path.is_symlink()` There’s no need to explicitly call `pathlib.Path.exists()` before. # Remove Redundant Slice Index > Removes unnecessary slice indices. #### Sourcery refactoring id: `remove-redundant-slice-index` [Section titled “Sourcery refactoring id: remove-redundant-slice-index”](#sourcery-refactoring-id-remove-redundant-slice-index) #### Description: [Section titled “Description:”](#description) Removes unnecessary slice indices. #### Before: [Section titled “Before:”](#before) ```python numbers[0 : len(numbers)] ``` #### After: [Section titled “After:”](#after) ```python numbers[:] ``` #### Explanation: [Section titled “Explanation:”](#explanation) The default starting value for a slice is 0, so it is unnecessary to explicitly define it. The default ending value is the end of the collection, so it is unnecessary to explicitly define this as well. This refactoring removes unnecessary slice indices, slightly shortening the code. # Remove Str From Fstring > Remove unnecessary calls to str() within formatted values in f-strings #### Sourcery refactoring id: `remove-str-from-fstring` [Section titled “Sourcery refactoring id: remove-str-from-fstring”](#sourcery-refactoring-id-remove-str-from-fstring) #### Description [Section titled “Description”](#description) Remove unnecessary calls to `str()` within formatted values in f-strings #### Before [Section titled “Before”](#before) ```python def description(name: str, age: int): return f"Name: {str(name)}, Age: {str(age)}" ``` #### After [Section titled “After”](#after) ```python def description(name: str, age: int): return f"Name: {name}, Age: {age}" ``` #### Explanation [Section titled “Explanation”](#explanation) Calls to `str()` are normally unnecessary within the formatted values of f-strings, since this conversion is already implicitly made. Removing them tidies up the code slightly and will yield a small performance improvement. You do need to call `str()` on some types with custom `__repr__()` methods, so this refactoring only triggers where Sourcery has inferred the type to be suitable. # Remove str() from call to print() > Removes unnecessary calls to str() from within print() #### Sourcery refactoring id: `remove-str-from-print` [Section titled “Sourcery refactoring id: remove-str-from-print”](#sourcery-refactoring-id-remove-str-from-print) #### Description: [Section titled “Description:”](#description) Removes unnecessary calls to `str()` from within `print()` #### Before: [Section titled “Before:”](#before) ```python print(str(1)) ``` #### After: [Section titled “After:”](#after) ```python print(1) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Objects passed into calls to the `print()` function already have `str()` called on them, so it is not required to do so yourself. # Remove-Unit-Step-From-Range > Replace range(x, y, 1) with range(x, y) #### Sourcery rule id: `remove-unit-step-from-range` [Section titled “Sourcery rule id: remove-unit-step-from-range”](#sourcery-rule-id-remove-unit-step-from-range) #### Description [Section titled “Description”](#description) Replace range(x, y, 1) with range(x, y) #### Before [Section titled “Before”](#before) ```python for i in range(y, len(x), 1): do_t() ``` #### After [Section titled “After”](#after) ```python for i in range(y, len(x)): do_t() ``` #### Explanation [Section titled “Explanation”](#explanation) The default `step` value for a call to `range()` is `1`, so it is unnecessary to explicitly define it. This refactoring removes this argument, slightly shortening the code. # Remove Unnecessary Cast > Remove unnecessary casts to int, str, float and bool #### Sourcery refactoring id: `remove-unnecessary-cast` [Section titled “Sourcery refactoring id: remove-unnecessary-cast”](#sourcery-refactoring-id-remove-unnecessary-cast) #### Description [Section titled “Description”](#description) Remove unnecessary casts to int, str, float and bool #### Before [Section titled “Before”](#before) ```python num_hats = 1 update_hat_count(int(num_hats)) ``` #### After [Section titled “After”](#after) ```python num_hats = 1 update_hat_count(num_hats) ``` #### Explanation [Section titled “Explanation”](#explanation) If a variable is already an `int`, `str`, `float` or `bool` it is unnecessary to wrap it in the associated call later on in the code. Removing these unnecessary casts is a small improvement in readability, and can eliminate the mental overhead of trying to figure out why or whether the cast is required. # Remove Unnecessary Else > Remove unnecessary else after guard condition #### Sourcery refactoring id: `remove-unnecessary-else` [Section titled “Sourcery refactoring id: remove-unnecessary-else”](#sourcery-refactoring-id-remove-unnecessary-else) #### Description: [Section titled “Description:”](#description) Remove unnecessary `else` after guard condition #### Before: [Section titled “Before:”](#before) ```python def f(a=None): if a is None: return 42 else: # some long calculations var = (i % 2 for i in range(a)) return sum(var) ``` #### After: [Section titled “After:”](#after) ```python def f(a=None): if a is None: return 42 # some long calculations var = (i % 2 for i in range(a)) return sum(var) ``` #### Explanation: [Section titled “Explanation:”](#explanation) A common code pattern is to have some clauses at the start of a function, to check whether certain conditions have been fulfilled and return early or raise an exception if not. While this is perfectly valid code, it can run into problems with excessive nesting, particularly if the rest of the function is fairly long. Here we can take advantage of the fact that we don’t need the `else` if the main body of the `if` breaks the control flow by ending with `return` or `raise`. Rewriting the function as shown here is logically equivalent. Using a guard condition, or multiple guard conditions, in this way now doesn’t cause the rest of the function to be indented. In general the less we have to deal with indents the easier the code is to understand. # Remove Unreachable Code > Removes code that will never be executed #### Sourcery refactoring id: `remove-unreachable-code` [Section titled “Sourcery refactoring id: remove-unreachable-code”](#sourcery-refactoring-id-remove-unreachable-code) #### Description: [Section titled “Description:”](#description) Removes code that will never be executed #### Before: [Section titled “Before:”](#before) ```python for a in b: do_a() continue do_x() do_y() ``` #### After: [Section titled “After:”](#after) ```python for a in b: do_a() continue ``` #### Explanation: [Section titled “Explanation:”](#explanation) Statements after a `continue`, `return` or `raise` will never be executed. Leaving them in the code confuses the reader, who may believe that these statements have some effect. They should therefore be removed. # Remove Unused Enumerate > Remove unnecessary calls to enumerate when the index variable is not used. #### Sourcery refactoring id: `remove-unused-enumerate` [Section titled “Sourcery refactoring id: remove-unused-enumerate”](#sourcery-refactoring-id-remove-unused-enumerate) #### Description [Section titled “Description”](#description) Remove unnecessary calls to [`enumerate`](https://docs.python.org/3/library/functions.html#enumerate) when the index variable is not used. #### Before [Section titled “Before”](#before) ```python for index, hat in enumerate(hats): print("I like this hat: ", hat) for i, (key, value) in enumerate(my_dictionary.items()): do_something(key) do_something_else(value) beautiful_hats = [hat for hat_id, hat in enumerate(hats) if is_beautiful(hat)] ``` #### After [Section titled “After”](#after) ```python for hat in hats: print("I like this hat: ", hat) for key, value in my_dictionary.items(): do_something(key) do_something_else(value) beautiful_hats = [hat for hat in hats if is_beautiful(hat)] ``` #### Explanation [Section titled “Explanation”](#explanation) Enumerating iterables with [`enumerate`](https://docs.python.org/3/library/functions.html#enumerate) is a good practice for having access to both the values and their respective indices. However, when the indices are not necessary, it is cleaner to simply iterate over the original iterable and remove the call to `enumerate`. # Remove-Zero-From-Range > Replace range(0, x) with range(x) #### Sourcery rule id: `remove-zero-from-range` [Section titled “Sourcery rule id: remove-zero-from-range”](#sourcery-rule-id-remove-zero-from-range) #### Description [Section titled “Description”](#description) Replace range(0, x) with range(x) #### Before [Section titled “Before”](#before) ```python for i in range(0, len(x)): do_t() ``` #### After [Section titled “After”](#after) ```python for i in range(len(x)): do_t() ``` #### Explanation [Section titled “Explanation”](#explanation) The default starting value for a call to `range()` is 0, so it is unnecessary to explicitly define it. This refactoring removes such zeros, slightly shortening the code. # Replace `apply` With Method Call > Replace .apply with a call to a DataFrame's or Series's method. #### Sourcery refactoring id: `replace-apply-with-method-call` [Section titled “Sourcery refactoring id: replace-apply-with-method-call”](#sourcery-refactoring-id-replace-apply-with-method-call) #### Description [Section titled “Description”](#description) Replace `.apply` with a call to a DataFrame’s or Series’s method. #### Before [Section titled “Before”](#before) ```python import pandas as pd df = pd.DataFrame(data={"col1": [1, 2], "col2": [3, 4]}) df.apply("sum") ``` #### After [Section titled “After”](#after) ```python import pandas as pd df = pd.DataFrame(data={"col1": [1, 2], "col2": [3, 4]}) df.sum() ``` #### Before [Section titled “Before”](#before-1) ```python import pandas as pd series_ = pd.Series([1, 1, 2, 3, 5, 8]) series_.apply("min") ``` #### After [Section titled “After”](#after-1) ```python import pandas as pd series_ = pd.Series([1, 1, 2, 3, 5, 8]) series_.min() ``` #### Explanation [Section titled “Explanation”](#explanation) Instead of calling `apply` with an aggregation method, it’s less verbose to call the aggregation method itself. `apply` is a versatile method, that can be used for various different use cases. However, there’s often a less verbose or more performant alternative. Try to check out those alternatives - especially when you’re working with a big dataset. #### Related Rule [Section titled “Related Rule”](#related-rule) * [replace-apply-with-numpy-operation](/reference/refactorings/python/replace-apply-with-numpy-operation/) to use the faster and more performant NumPy operations instead of `apply` # Replace apply With NumPy Operation > Replace apply with a NumPy operation. #### Sourcery refactoring id: `replace-apply-with-numpy-operation` [Section titled “Sourcery refactoring id: replace-apply-with-numpy-operation”](#sourcery-refactoring-id-replace-apply-with-numpy-operation) #### Description [Section titled “Description”](#description) Replace `apply` with a NumPy operation. #### Before [Section titled “Before”](#before) ```python import pandas as pd series_ = pd.Series([5, 8, 13, 21, 34]) other = series_.apply(lambda num: num + 5) ``` #### After [Section titled “After”](#after) ```python import pandas as pd series_ = pd.Series([5, 8, 13, 21, 34]) other = series_ + 5 ``` #### Explanation [Section titled “Explanation”](#explanation) For numeric operations, using pandas directly gives you better performance than `apply`. The bigger your dataset, the bigger this performance gain tends to be. This is because pandas uses fast, vectorized NumPy code, but `apply` operates on each value of your `Series` or `DataFrame` separately. `apply` is a versatile method, that can be used for various different use cases. However, there’s often a less verbose or more performant alternative. Try to check out those alternatives - especially when you’re working with a big dataset. #### Related Rule [Section titled “Related Rule”](#related-rule) * [replace-apply-with-method-call](/reference/refactorings/python/replace-apply-with-method-call/) to use the aggregation methods of `Series` and `DataFrame` instead of `apply` # Replace Dict Items with Values > Replace calls to dict.items with dict.values when the keys are not used. #### Sourcery refactoring id: `replace-dict-items-with-values` [Section titled “Sourcery refactoring id: replace-dict-items-with-values”](#sourcery-refactoring-id-replace-dict-items-with-values) #### Description: [Section titled “Description:”](#description) Replace calls to [`dict.items`](https://docs.python.org/3/library/stdtypes.html#dict.items) with [`dict.values`](https://docs.python.org/3/library/stdtypes.html#dict.values) when the keys are not used. #### Before: [Section titled “Before:”](#before) ```python for key, value in d.items(): print(value) ``` #### After: [Section titled “After:”](#after) ```python for value in d.values(): print(value) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Calling [`dict.items`](https://docs.python.org/3/library/stdtypes.html#dict.items) is only necessary when both keys and values are needed. However, when the keys are not used, it is cleaner to simply iterate over the dictionary values. # Replace Interpolation With Fstring > Replace usage of string interpolation % operator with f-strings #### Sourcery refactoring id: `replace-interpolation-with-fstring` [Section titled “Sourcery refactoring id: replace-interpolation-with-fstring”](#sourcery-refactoring-id-replace-interpolation-with-fstring) #### Description [Section titled “Description”](#description) Replace usage of string interpolation `%` operator with f-strings #### Before [Section titled “Before”](#before) ```python print("Good %s, %s %s" % (time_of_day, first_name, last_name)) ``` #### After [Section titled “After”](#after) ```python print(f"Good {time_of_day}, {first_name} {last_name}") ``` #### Explanation [Section titled “Explanation”](#explanation) The [Python documentation](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting) notes that using the `%` operator to format strings can lead to ‘a variety of quirks that lead to a number of common errors’. Python added f-strings in version 3.6, with [PEP 498](https://www.python.org/dev/peps/pep-0498/). F-strings are a flexible and powerful way to format strings. They make the code shorter and more readable, since the code now looks more like the output. Note that for the moment this refactoring only supports replacement of formatting that uses the `%s` operator. # Return or Yield Outside Function > Remove return or yield statements found outside function definitions. #### Sourcery refactoring id: `return-or-yield-outside-function` [Section titled “Sourcery refactoring id: return-or-yield-outside-function”](#sourcery-refactoring-id-return-or-yield-outside-function) #### Description [Section titled “Description”](#description) Remove return or yield statements found outside function definitions. #### Before [Section titled “Before”](#before) ```python for i in range(10): print(i) return i ``` #### After [Section titled “After”](#after) ```python for i in range(10): print(i) ``` #### Alternative [Section titled “Alternative”](#alternative) If you intend to `return` or `yield` some value, make sure that the statement is correctly placed and indented in your code: *Before*: ```python def square(x): y = x**2 return y ``` *After*: ```python def square(x): y = x**2 return y ``` #### Explanation [Section titled “Explanation”](#explanation) The [`return`](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement) and [`yield`](https://docs.python.org/3/reference/simple_stmts.html#the-yield-statement) statements can only be used inside function definitions. Using them outside this context, or inside nested class definitions, is a [`SyntaxError`](https://docs.python.org/3/library/exceptions.html#SyntaxError). This error may be very easy to pass through since sometimes it is caused by a wrong indentation. # Set Comprehension > Replaces sets created with for loops with set comprehensions #### Sourcery refactoring id: `set-comprehension` [Section titled “Sourcery refactoring id: set-comprehension”](#sourcery-refactoring-id-set-comprehension) #### Description: [Section titled “Description:”](#description) Replaces sets created with `for` loops with set comprehensions #### Before: [Section titled “Before:”](#before) ```python cubes = set() for i in range(20): cubes.add(i**3) ``` #### After: [Section titled “After:”](#after) ```python cubes = {i**3 for i in range(20)} ``` #### Explanation: [Section titled “Explanation:”](#explanation) A set comprehension can create the set on one line, cutting out the clutter of declaring an empty set and then adding values. Turning three lines of code into one is a definite win - it means less scrolling back and forth when reading methods and helps keep things manageable. Squeezing code onto one line can make it more difficult to read, but for comprehensions this isn’t the case. All of the elements that you need are nicely presented, and once you are used to the syntax it is actually more readable than the for loop version. Another point is that the assignment is now more of an atomic operation - we’re declaring what `cubes` is rather than giving instructions on how to build it. This makes the code read like more of a narrative, since going forward we will care more about what `cubes` is than the details of its construction. Finally comprehensions will usually execute more quickly than building the collection in a loop, which is another factor if performance is a consideration. # Simplify Boolean Comparison > Removes unnecessarily verbose boolean comparisons #### Sourcery refactoring id: `simplify-boolean-comparison` [Section titled “Sourcery refactoring id: simplify-boolean-comparison”](#sourcery-refactoring-id-simplify-boolean-comparison) #### Description: [Section titled “Description:”](#description) Removes unnecessarily verbose boolean comparisons #### Before: [Section titled “Before:”](#before) ```python need_hat = not is_raining if need_hat == True: put_on_hat() ``` #### After: [Section titled “After:”](#after) ```python need_hat = not is_raining if need_hat: put_on_hat() ``` #### Explanation: [Section titled “Explanation:”](#explanation) It is unnecessary to compare boolean values to `True` or `False` in the test of an `if` condition. Removing these unnecessary checks makes the code slightly shorter and easier to parse. # Simplify-Constant-Sum > Simplify constant sum() call #### Sourcery rule id: `simplify-constant-sum` [Section titled “Sourcery rule id: simplify-constant-sum”](#sourcery-rule-id-simplify-constant-sum) #### Description [Section titled “Description”](#description) Simplify constant sum() call #### Before [Section titled “Before”](#before) ```python sum(1 for book in books if book.author == "Terry Pratchett") ``` #### After [Section titled “After”](#after) ```python sum(bool(book.author == "Terry Pratchett") for book in books) ``` #### Explanation [Section titled “Explanation”](#explanation) As `sum` add the values it treats `True` as `1`, and `False` as `0`. We make use of this fact to simplify the generator expression inside the `sum` call. # Simplify Dictionary Update > Add single value to dictionary directly rather than using update() #### Sourcery refactoring id: `simplify-dictionary-update` [Section titled “Sourcery refactoring id: simplify-dictionary-update”](#sourcery-refactoring-id-simplify-dictionary-update) #### Description: [Section titled “Description:”](#description) Add single value to dictionary directly rather than using update() #### Before: [Section titled “Before:”](#before) ```python def foo(d: dict) -> None: d.update({"request": HttpRequest()}) frobnicate(d) ``` #### After: [Section titled “After:”](#after) ```python def foo(d: dict) -> None: d["request"] = HttpRequest() frobnicate(d) ``` #### Explanation: [Section titled “Explanation:”](#explanation) When we are just adding one entry to a dictionary it is simpler to do it directly rather than via the `update()` method. This also eliminates the overhead of constructing another dictionary and calling a method, so should slightly improve performance. # Simplify Division > Use Python's built-in feature for succinct division syntax. #### Sourcery refactoring id: `simplify-division` [Section titled “Sourcery refactoring id: simplify-division”](#sourcery-refactoring-id-simplify-division) #### Description: [Section titled “Description:”](#description) Use Python’s built-in feature for succinct division syntax. #### Before: [Section titled “Before:”](#before) ```python result = int(42 / 10) ``` #### After: [Section titled “After:”](#after) ```python result = 42 // 10 ``` #### Before: [Section titled “Before:”](#before-1) ```python result = 42 // 10 remainder = 42 % 10 ``` #### After: [Section titled “After:”](#after-1) ```python result, remainder = divmod(42, 10) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Python has some great features to simplify division expressions. If you’re interested only in the whole number component of the quotient, you can use the `//` integer division operator. If you want to have the whole number component and the remainder in separate variables, the built-in `divmod` function comes handy. # Simplify Empty Collection Comparison > Replace an empty collection comparison with a more idiomatic unary operator. #### Sourcery refactoring id: `simplify-empty-collection-comparison` [Section titled “Sourcery refactoring id: simplify-empty-collection-comparison”](#sourcery-refactoring-id-simplify-empty-collection-comparison) #### Description: [Section titled “Description:”](#description) Replace an empty collection comparison with a more idiomatic unary operator. #### Before: [Section titled “Before:”](#before) ```python if a == "": do_anything() while l == []: handle(l) ``` #### After: [Section titled “After:”](#after) ```python if not a: do_anything() while not l: handle(l) ``` #### Explanation: [Section titled “Explanation:”](#explanation) This refactoring follows the [PEP 8 Programming Recommendations](https://www.python.org/dev/peps/pep-0008/#programming-recommendations), specifically the recommendation that > For sequences, (strings, lists, tuples), use the fact that empty sequences are false in the context of comparisons. # Simplify f-string Formatting > Simplify the formatting of replacements within an f-string. #### Sourcery refactoring id: `simplify-fstring-formatting` [Section titled “Sourcery refactoring id: simplify-fstring-formatting”](#sourcery-refactoring-id-simplify-fstring-formatting) #### Description [Section titled “Description”](#description) Simplify the formatting of replacements within an f-string. #### Before [Section titled “Before”](#before) ```python name = "Tom" celebration = "hurrah" f"{name} is {5} years old, {f'{celebration.upper()}'}" ``` #### After [Section titled “After”](#after) ```python name = "Tom" celebration = "hurrah" f"{name} is 5 years old, {celebration.upper()}" ``` #### Explanation [Section titled “Explanation”](#explanation) This refactoring simplifies f-string: * Inline constants directly into the f-strings * Remove unnecessary nested f-strings if they are not needed # Simplify Generator > An identity generator (a for a in coll) can be replaced directly with the collection coll #### Sourcery refactoring id: `simplify-generator` [Section titled “Sourcery refactoring id: simplify-generator”](#sourcery-refactoring-id-simplify-generator) #### Description: [Section titled “Description:”](#description) An identity generator `(a for a in coll)` can be replaced directly with the collection `coll` #### Before: [Section titled “Before:”](#before) ```python if any(hat for hat in wardrobe.hats): print("I have a hat!") ``` #### After: [Section titled “After:”](#after) ```python if any(wardrobe.hats): print("I have a hat!") ``` #### Explanation: [Section titled “Explanation:”](#explanation) The expression `(x for x in y)` is a generator that returns all of the elements of y. If being passed into a function like `any` or `all` that takes a generator or sequence, it can simply be replaced by `y` which is much clearer. # Simplify Length Comparison > Removes unnecessarily verbose length comparisons #### Sourcery refactoring id: `simplify-len-comparison` [Section titled “Sourcery refactoring id: simplify-len-comparison”](#sourcery-refactoring-id-simplify-len-comparison) #### Description: [Section titled “Description:”](#description) Removes unnecessarily verbose length comparisons #### Before: [Section titled “Before:”](#before) ```python if len(list_of_hats) > 0: hat_to_wear = choose_hat(list_of_hats) ``` #### After: [Section titled “After:”](#after) ```python if list_of_hats: hat_to_wear = choose_hat(list_of_hats) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Something we often do is check whether a list or sequence has elements before we try and do something with it. A Pythonic way of doing this is just to use the fact that Python lists and sequences evaluate to `True` if they have elements, and `False` otherwise. Doing it this way is a convention, set out in Python’s [PEP8](https://www.python.org/dev/peps/pep-0008/) style guide. Once you’ve gotten used to doing it this way it does make the code slightly easier to read and a bit less cluttered. # Simplify Negative Index > Replaces a[len(a)-1] with negative index lookup a[-1] #### Sourcery refactoring id: `simplify-negative-index` [Section titled “Sourcery refactoring id: simplify-negative-index”](#sourcery-refactoring-id-simplify-negative-index) #### Description: [Section titled “Description:”](#description) Replaces `a[len(a)-1]` with negative index lookup `a[-1]` #### Before: [Section titled “Before:”](#before) ```python a = [1, 2, 3] last_element = a[len(a) - 1] ``` #### After: [Section titled “After:”](#after) ```python a = [1, 2, 3] last_element = a[-1] ``` #### Explanation: [Section titled “Explanation:”](#explanation) In Python you can access the end of a list by using negative indices. So `my_list[-1]` gets the final element, `my_list[-2]` gets the penultimate one and so on. Once you know the trick, reading code that uses it is much easier than the alternative. # Simplify Numeric Comparison > Consolidates any mathematical operations in a numeric comparison so there is a direct comparison of variable to number #### Sourcery refactoring id: `simplify-numeric-comparison` [Section titled “Sourcery refactoring id: simplify-numeric-comparison”](#sourcery-refactoring-id-simplify-numeric-comparison) #### Description: [Section titled “Description:”](#description) Consolidates any mathematical operations in a numeric comparison so there is a direct comparison of variable to number #### Before: [Section titled “Before:”](#before) ```python if a + 1 < 2: do_x() ``` #### After: [Section titled “After:”](#after) ```python if a < 1: do_x() ``` #### Explanation: [Section titled “Explanation:”](#explanation) This refactoring removes unnecessary clutter from the code and makes it easier to determine what a variable is being compared to. # Simplify Single Exception Tuple > Replace length-one exception tuple with exception. #### Sourcery refactoring id: `simplify-single-exception-tuple` [Section titled “Sourcery refactoring id: simplify-single-exception-tuple”](#sourcery-refactoring-id-simplify-single-exception-tuple) #### Description [Section titled “Description”](#description) Replace length-one exception tuple with exception. #### Before [Section titled “Before”](#before) ```python try: read_file() except (FileNotFoundError,) as e: log_error(e) create_file() ``` #### After [Section titled “After”](#after) ```python try: read_file() except FileNotFoundError as e: log_error(e) create_file() ``` #### Explanation [Section titled “Explanation”](#explanation) Python supports catching multiple exception types at once by using tuples. However, when only a single exception type is being handled, there is no need to wrap it in a tuple. Using the exception type itself makes your code easier to read, and your intent clearer. # Simplify String Length Comparison > Changes an indirect comparison of a string's length to 0 into a direct comparison of the string to the empty string. #### Sourcery refactoring id: `simplify-string-len-comparison` [Section titled “Sourcery refactoring id: simplify-string-len-comparison”](#sourcery-refactoring-id-simplify-string-len-comparison) #### Description: [Section titled “Description:”](#description) Changes an indirect comparison of a string’s length to `0` into a direct comparison of the string to the empty string. #### Before: [Section titled “Before:”](#before) ```python if len(s) == 0: ... if len(r) > 0: ... ``` #### After: [Section titled “After:”](#after) ```python if s == "": ... if r != "": ... ``` #### Explanation: [Section titled “Explanation:”](#explanation) This refactoring avoids an unnecessary calculation and keeps the logic in the domain of string types. It tends to unlock further improvements to string comparisons. # Simplify substring search > Simplify finding if substrings are present in strings by using in #### Sourcery refactoring id: `simplify-substring-search` [Section titled “Sourcery refactoring id: simplify-substring-search”](#sourcery-refactoring-id-simplify-substring-search) #### Description: [Section titled “Description:”](#description) Simplify finding if substrings are present in strings by using `in` #### Before: [Section titled “Before:”](#before) ```python my_str = "Hello world" if my_str.find("ello") == -1: print("Not Found!") ``` #### After: [Section titled “After:”](#after) ```python my_str = "Hello world" if "ello" not in my_str: print("Not Found!") ``` #### Before: [Section titled “Before:”](#before-1) ```python my_str = "Hello world" if my_str.count("ello") > 0: print("Found!") ``` #### After: [Section titled “After:”](#after-1) ```python my_str = "Hello world" if "ello" in my_str: print("Found!") ``` #### Explanation: [Section titled “Explanation:”](#explanation) Making use of Python’s `in` operator for detecting if a substring is present in a string is more readable than using the `find` or `count` methods, and is also suggested in the [documentation](https://docs.python.org/3/library/stdtypes.html#str.find). These methods are more suitable for cases where you need more information about the substring’s location or the number of times it appears. # Skip Sorted List Construction > Removes an unnecessary intermediate construction call for a sorted list, in favour of the sorted builtin. #### Sourcery refactoring id: `skip-sorted-list-construction` [Section titled “Sourcery refactoring id: skip-sorted-list-construction”](#sourcery-refactoring-id-skip-sorted-list-construction) #### Description: [Section titled “Description:”](#description) Removes an unnecessary intermediate construction call for a sorted list, in favour of the `sorted` builtin. #### Before: [Section titled “Before:”](#before) ```python pies = list(pie_generator()) pies.sort() ``` #### After: [Section titled “After:”](#after) ```python pies = sorted(pie_generator()) ``` #### Explanation: [Section titled “Explanation:”](#explanation) It’s unnecessary to construct an intermediate list when sorting a generator, as the `sorted` builtin returns a list. # Split or ifs > Splits out conditions combined with an or in an if statement into their own if statement. #### Sourcery refactoring id: `split-or-ifs` [Section titled “Sourcery refactoring id: split-or-ifs”](#sourcery-refactoring-id-split-or-ifs) #### Description: [Section titled “Description:”](#description) Splits out conditions combined with an `or` in an `if` statement into their own `if` statement. #### Before: [Section titled “Before:”](#before) ```python if a or b: c() ``` #### After: [Section titled “After:”](#after) ```python if a: c() elif b: c() ``` #### Explanation: [Section titled “Explanation:”](#explanation) It is logically equivalent to transform an `or` condition into an `if` branch and an `elif` branch of a conditional. This is not prima facie an improvement to the code, but it can unlock further improvements, and Sourcery will only make this change if such improvements are possible. # Square Identity > Replaces cases of a variable being multiplied by itself with squaring that variable #### Sourcery refactoring id: `square-identity` [Section titled “Sourcery refactoring id: square-identity”](#sourcery-refactoring-id-square-identity) #### Description: [Section titled “Description:”](#description) Replaces cases of a variable being multiplied by itself with squaring that variable #### Before: [Section titled “Before:”](#before) ```python def square(variable: int): return variable * variable ``` #### After: [Section titled “After:”](#after) ```python def square(variable: int): return variable**2 ``` #### Explanation: [Section titled “Explanation:”](#explanation) This identity can make the code slightly shorter and easier to read. # Str Prefix Suffix > Replace explicit str prefix/suffix check with call to startswith/endswith. #### Sourcery refactoring id: `str-prefix-suffix` [Section titled “Sourcery refactoring id: str-prefix-suffix”](#sourcery-refactoring-id-str-prefix-suffix) #### Description [Section titled “Description”](#description) Replace explicit str prefix/suffix check with call to `startswith`/`endswith`. #### Before [Section titled “Before”](#before) ```python name[:8] == "Sourcery" name[-8:] == "Sourcery" ``` #### After [Section titled “After”](#after) ```python name.startswith("Sourcery") name.endswith("Sourcery") ``` #### Explanation [Section titled “Explanation”](#explanation) When we read code like `name[:8] == 'Sourcery'` it takes a moment to understand exactly what it is doing. Instead when we read `name.startswith('Sourcery')` or `name.endswith('Sourcery')` it is immediate clear what the intent of the code is. Additionally we don’t have to manually count the length of the prefix and check it is the same as the slice end; if we ever change the prefix we are less likely to make a mistake. # Sum Comprehension > Replaces summed values created with for loops with sum comprehensions #### Sourcery refactoring id: `sum-comprehension` [Section titled “Sourcery refactoring id: sum-comprehension”](#sourcery-refactoring-id-sum-comprehension) #### Description: [Section titled “Description:”](#description) Replaces summed values created with `for` loops with `sum` comprehensions #### Before: [Section titled “Before:”](#before) ```python total = 0 for hat in hats: total += hat.price ``` #### After: [Section titled “After:”](#after) ```python total = sum(hat.price for hat in hats) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Much of programming is about adding up lists of things, and Python has the built-in `sum()` function to help with this. This is much shorter, which is a definite bonus. The code also now explicitly tells you what it is trying to do - sum the price of all the hats. # Swap If Else Branches > Swaps if and else branches of conditionals #### Sourcery refactoring id: `swap-if-else-branches` [Section titled “Sourcery refactoring id: swap-if-else-branches”](#sourcery-refactoring-id-swap-if-else-branches) #### Description: [Section titled “Description:”](#description) Swaps `if` and `else` branches of conditionals #### Before: [Section titled “Before:”](#before) ```python if location == OUTSIDE: pass else: take_off_hat() ``` #### After: [Section titled “After:”](#after) ```python if location != OUTSIDE: take_off_hat() else: pass ``` #### Explanation: [Section titled “Explanation:”](#explanation) One pattern we sometimes see is a conditional where nothing happens in the main body, and all of the action is in the `else` clause. In this case we can make the code shorter and more concise by swapping the main body and the `else` around. We have to make sure to invert the conditional, then the logic from the `else` clause moves into the main body. We then have an `else` clause which does nothing, so we can remove it. ```python if location != OUTSIDE: take_off_hat() ``` This is easier to read, and the intent of the conditional is clearer. When reading the code I don’t have to mentally invert it to understand it, since that has been done for me. Sourcery will also make this change if the `else` can be dropped since the body of the `if` is a guard condition. # Swap-If-Expression > Swap if/else branches of if expression to remove negation #### Sourcery rule id: `swap-if-expression` [Section titled “Sourcery rule id: swap-if-expression”](#sourcery-rule-id-swap-if-expression) #### Description [Section titled “Description”](#description) Swap if/else branches of if expression to remove negation #### Before [Section titled “Before”](#before) ```python my_hat = hats.bowler if not weather.is_sunny() else hats.baseball_cap ``` #### After [Section titled “After”](#after) ```python my_hat = hats.baseball_cap if weather.is_sunny() else hats.bowler ``` #### Explanation [Section titled “Explanation”](#explanation) Negated conditions are more difficult to read than positive ones, so it is best to avoid them where we can. By swapping the `if` and `else` conditions around we can invert the condition and make it positive. # Swap Nested Ifs > Swaps the order of nested if statements #### Sourcery refactoring id: `swap-nested-ifs` [Section titled “Sourcery refactoring id: swap-nested-ifs”](#sourcery-refactoring-id-swap-nested-ifs) #### Description: [Section titled “Description:”](#description) Swaps the order of nested `if` statements #### Before: [Section titled “Before:”](#before) ```python if a: if b: return c ``` #### After: [Section titled “After:”](#after) ```python if b: if a: return c ``` #### Explanation: [Section titled “Explanation:”](#explanation) This refactoring identity will not improve code quality on its own, but can be part of a series of steps that will improve quality. Sourcery will only suggest this refactoring where it unlocks further improvements. # Swap Variable > Swap variable values with tuple assignment #### Sourcery refactoring id: `swap-variable` [Section titled “Sourcery refactoring id: swap-variable”](#sourcery-refactoring-id-swap-variable) #### Description: [Section titled “Description:”](#description) Swap variable values with tuple assignment #### Before: [Section titled “Before:”](#before) ```python temp = a a = b b = temp ``` #### After: [Section titled “After:”](#after) ```python a, b = b, a ``` #### Explanation: [Section titled “Explanation:”](#explanation) When trying to swap the values held by two variables, we may avoid using an extra temporary variable unnecessarily by employing Python’s tuple assignment. As a consequence of this refactoring, the code is not only more concise but also reflects its intention more clearly. # Switch > Simplify conditionals into a form more like a switch statement #### Sourcery refactoring id: `switch` [Section titled “Sourcery refactoring id: switch”](#sourcery-refactoring-id-switch) #### Description: [Section titled “Description:”](#description) Simplify conditionals into a form more like a `switch` statement #### Before: [Section titled “Before:”](#before) ```python if item.a == 1: q += 2 elif item.a == 2: q += 2 elif item.a == 4: q = 0 ``` #### After: [Section titled “After:”](#after) ```python if item.a in [1, 2]: q += 2 elif item.a == 4: q = 0 ``` #### Explanation: [Section titled “Explanation:”](#explanation) This refactoring examines complex conditionals where a variable is being compared to various different values, and tries to put them into the simplest form possible, eliminating duplicated blocks. # Ternary to If Expression > Replace boolean ternary with inline if expression. #### Sourcery refactoring id: `ternary-to-if-expression` [Section titled “Sourcery refactoring id: ternary-to-if-expression”](#sourcery-refactoring-id-ternary-to-if-expression) #### Description [Section titled “Description”](#description) Replace boolean ternary with inline if expression. #### Before [Section titled “Before”](#before) ```python protocol = is_ssl and "https" or "http" ``` #### After [Section titled “After”](#after) ```python protocol = "https" if is_ssl else "http" ``` #### Explanation [Section titled “Explanation”](#explanation) Prior to Python version 2.5 the best way to create a ternary operator was to use `and` & `or` together in the before example. The if expression syntax shows the intent of the code much clearer and so is preferred. # Tuple-Literal > Replace `tuple()` with `()` #### Sourcery rule id: `tuple-literal` [Section titled “Sourcery rule id: tuple-literal”](#sourcery-rule-id-tuple-literal) #### Description [Section titled “Description”](#description) Replace `tuple()` with `()` #### Before [Section titled “Before”](#before) ```python x = tuple() ``` #### After [Section titled “After”](#after) ```python x = () ``` #### Explanation [Section titled “Explanation”](#explanation) The most concise and Pythonic way to create an empty tuple is to use the `()` literal. This fits in with the way we create tuples with items, saving a bit of mental energy that might be taken up with thinking about two different ways of creating tuples: ```python x = ("first", "second") ``` Doing things this way has the added advantage of being a nice little performance improvement. Here are the timings before and after the change: ```plaintext $ python3 -m timeit "tuple()" 10000000 loops, best of 5: 22.6 nsec per loop ``` ```plaintext $ python3 -m timeit "()" 50000000 loops, best of 5: 5.46 nsec per loop ``` # Unwrap Iterable Construction > Unwrap an iterable constructor into a literal iterable. #### Sourcery refactoring id: `unwrap-iterable-construction` [Section titled “Sourcery refactoring id: unwrap-iterable-construction”](#sourcery-refactoring-id-unwrap-iterable-construction) #### Description [Section titled “Description”](#description) Unwrap an iterable constructor into a literal iterable. #### Before [Section titled “Before”](#before) ```python tuple([user_config, *package_config, local_config]) ``` #### After [Section titled “After”](#after) ```python (user_config, *package_config, local_config) ``` #### Explanation [Section titled “Explanation”](#explanation) Creating the intermediate constant is unnecessary and needlessly verbose. # Use_iloc > Use the [`.iloc`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.iloc.html) attribute for index-based selection #### Sourcery rule id: `use_iloc` [Section titled “Sourcery rule id: use\_iloc”](#sourcery-rule-id-use_iloc) #### Description [Section titled “Description”](#description) Use the [`.iloc`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.iloc.html) attribute for index-based selection #### Before [Section titled “Before”](#before) ```python df: pandas.DataFrame data = df[1] ``` #### After [Section titled “After”](#after) ```python df: pandas.DataFrame data = df.iloc[1] ``` #### Explanation [Section titled “Explanation”](#explanation) Using ‘iloc’ is optimized for selecting data in Pandas based on index position (). This rule replaces the generic indexing with ‘iloc’ for index-based selection. #### Before: [Section titled “Before:”](#before-1) ```python data = df[5] ``` #### After: [Section titled “After:”](#after-1) ```python data = df.iloc[5] ``` # Use Any > Use any rather than a for loop #### Sourcery refactoring id: `use-any` [Section titled “Sourcery refactoring id: use-any”](#sourcery-refactoring-id-use-any) #### Description: [Section titled “Description:”](#description) Use `any` rather than a `for` loop #### Before: [Section titled “Before:”](#before) ```python found = False for hat in hats: if hat == SOMBRERO: found = True break ``` #### After: [Section titled “After:”](#after) ```python found = any(hat == SOMBRERO for hat in hats) ``` #### Explanation: [Section titled “Explanation:”](#explanation) A common pattern is that we need to find if some condition holds for one or all of the items in a collection. Using Python’s `any()` and `all()` built in functions is a more concise way of doing this than using a `for` loop. `any()` will return `True` when at least one of the elements evaluates to `True`, `all()` will return `True` only when all the elements evaluate to `True`. These will also short-circuit execution where possible. If the call to `any()` finds an element that evalutes to `True` it can return immediately. This can lead to performance improvements if the code wasn’t already short-circuiting. # Use Assigned Variable > Uses a variable that was previously defined in the function instead of repeating what was defined in the variable #### Sourcery refactoring id: `use-assigned-variable` [Section titled “Sourcery refactoring id: use-assigned-variable”](#sourcery-refactoring-id-use-assigned-variable) #### Description: [Section titled “Description:”](#description) Uses a variable that was previously defined in the function instead of repeating what was defined in the variable #### Before: [Section titled “Before:”](#before) ```python wardrobe = {"blue_hat": 1, "red_hat": 3} for item in wardrobe: count = wardrobe[item] add_to_total(wardrobe[item]) ``` #### After: [Section titled “After:”](#after) ```python wardrobe = {"blue_hat": 1, "red_hat": 3} for item in wardrobe: count = wardrobe[item] add_to_total(count) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Where possible, it is preferable to re-use local variables that have been assigned to. These variables will often have a more descriptive name that can aid in code comprehension, and additionally re-use can reduce duplication. # Use `contextlib.suppress` > Use contextlib's suppress method to silence a specific error, instead of passing in an exception handler. This refactoring will add an import for… #### Sourcery refactoring id: `use-contextlib-suppress` [Section titled “Sourcery refactoring id: use-contextlib-suppress”](#sourcery-refactoring-id-use-contextlib-suppress) #### Description [Section titled “Description”](#description) Use [`contextlib`](https://docs.python.org/3/library/contextlib.html)’s `suppress` method to silence a specific error, instead of `pass`ing in an exception handler. This refactoring will add an import for `contextlib` if needed. #### Before [Section titled “Before”](#before) ```python try: travel_world(days=80) except DistractionError: pass ``` #### After [Section titled “After”](#after) ```python import contextlib with contextlib.suppress(DistractionError): travel_world(days=80) ``` #### Explanation [Section titled “Explanation”](#explanation) The context manager slightly shortens the code and significantly clarifies the author’s intention to ignore the specific errors. The standard library feature was introduced following a [discussion](https://bugs.python.org/issue15806), where the consensus was that > A key benefit here is in the priming effect for readers… The with statement form makes it clear before you start reading the code that certain exceptions won’t propagate. #### Related Rules [Section titled “Related Rules”](#related-rules) * [merge-except-handler](/reference/refactorings/python/merge-except-handler/) to merge except handlers with the same content * [remove-redundant-exception](/reference/refactorings/python/remove-redundant-exception/) to clean up the tuple of an except clause * [remove-redundant-except-handler](/reference/refactorings/python/remove-redundant-except-handler/) to remove unreachable `except` blocks * [do-not-use-bare-except](/reference/refactorings/python/do-not-use-bare-except/) to always specify which errors an `except` block is handling # Use Count > Replaces sum() with count() where appropriate #### Sourcery refactoring id: `use-count` [Section titled “Sourcery refactoring id: use-count”](#sourcery-refactoring-id-use-count) #### Description: [Section titled “Description:”](#description) Replaces `sum()` with `count()` where appropriate #### Before: [Section titled “Before:”](#before) ```python return sum(hat == "bowler" for hat in hats) ``` #### After: [Section titled “After:”](#after) ```python return hats.count("bowler") ``` #### Explanation: [Section titled “Explanation:”](#explanation) This simplification can be made where a call to `sum()` is counting the number of elements that match a condition. # Use Datetime Now Not Today > Replace calls to datetime.datetime.today() with datetime.datetime.now(). They are functionally equivalent, but now is a more expressive name. #### Sourcery refactoring id: `use-datetime-now-not-today` [Section titled “Sourcery refactoring id: use-datetime-now-not-today”](#sourcery-refactoring-id-use-datetime-now-not-today) #### Description [Section titled “Description”](#description) Replace calls to `datetime.datetime.today()` with `datetime.datetime.now()`. They are functionally equivalent, but `now` is a more expressive name. #### Before [Section titled “Before”](#before) ```python from datetime import datetime print(datetime.today()) ``` #### After [Section titled “After”](#after) ```python from datetime import datetime print(datetime.now()) ``` #### Explanation [Section titled “Explanation”](#explanation) `datetime.datetime.today()` has a misleading name. It doesn’t return a `date` object as the name `today` would imply, but a `datetime` object containing the current time. For this reason, the functionally equivalent `datetime.datetime.now()` is preferred. (See the [Python Standard Library Docs](https://docs.python.org/3/library/datetime.html#datetime.datetime.now) .) # Use Dictionary Items > Use dictionary.items() in for loops to access both key and value at same time #### Sourcery refactoring id: `use-dict-items` [Section titled “Sourcery refactoring id: use-dict-items”](#sourcery-refactoring-id-use-dict-items) #### Description: [Section titled “Description:”](#description) Use `dictionary.items()` in `for` loops to access both key and value at same time #### Before: [Section titled “Before:”](#before) ```python hats_by_colour = {"blue": ["panama", "baseball_cap"]} for hat_colour in hats_by_colour: hats = hats_by_colour[hat_colour] if hat_colour in FAVOURITE_COLOURS: think_about_wearing(hats) ``` #### After: [Section titled “After:”](#after) ```python hats_by_colour = {"blue": ["panama", "baseball_cap"]} for hat_colour, hats in hats_by_colour.items(): if hat_colour in FAVOURITE_COLOURS: think_about_wearing(hats) ``` #### Explanation: [Section titled “Explanation:”](#explanation) When iterating over a dictionary a good tip is that you can use `items()` to let you access the keys and values at the same time. This saves us the line that we used to assign to `hats`, incorporating it into the for loop. The code now reads more naturally, with a touch less duplication. # Use Dictionary Union > Replace a sequence of unpacked dictionaries with a use of the dictionary union operator. #### Sourcery refactoring id: `use-dictionary-union` [Section titled “Sourcery refactoring id: use-dictionary-union”](#sourcery-refactoring-id-use-dictionary-union) #### Description [Section titled “Description”](#description) Replace a sequence of unpacked dictionaries with a use of the dictionary union operator. #### Before [Section titled “Before”](#before) ```python tube_stops = { "Piccadilly": ["Heathrow", "King's Cross"], "Bakerloo": ["Baker Street", "Oxford Circus"], } bus_stops = { "65": ["Kingston Station", "Kew Bridge"], } all_stops = {**tube_stops, **bus_stops} ``` #### After [Section titled “After”](#after) ```python tube_stops = { "Piccadilly": ["Heathrow", "King's Cross"], "Bakerloo": ["Baker Street", "Oxford Circus"], } bus_stops = { "65": ["Kingston Station", "Kew Bridge"], } all_stops = tube_stops | bus_stops ``` #### Explanation [Section titled “Explanation”](#explanation) The dictionary union operator was introduced in Python 3.9 (see [PEP 584](https://peps.python.org/pep-0584/)). It provides an interface for dictionaries similar to set unions, where the result combines the keys from both dictionaries, with the right dictionary taking precedence in the case of duplicates. This is clearer, more concise, and more discoverable than using dictionary unpacking to create a new dictionary. See also: [`dict-assign-update-to-union`](/reference/refactorings/python/dict-assign-update-to-union/) # Use File Iterator > Use the in-built file iterator rather than calling readlines() #### Sourcery refactoring id: `use-file-iterator` [Section titled “Sourcery refactoring id: use-file-iterator”](#sourcery-refactoring-id-use-file-iterator) #### Description [Section titled “Description”](#description) Use the in-built file iterator rather than calling `readlines()` #### Before [Section titled “Before”](#before) ```python with open("foo") as f: for line in f.readlines(): print(line) ``` #### After [Section titled “After”](#after) ```python with open("foo") as f: for line in f: print(line) ``` #### Explanation [Section titled “Explanation”](#explanation) The file object that Python returns when you open a file is a lazy iterator over that file’s lines. This means that there is no need to call `readlines` to iterate over it. Iterating directly is shorter, and also does not load the whole file into memory with a list object ( which `readlines` does) so can be more performant. # Use Fstring For Concatenation > Use f-strings for concatenating strings instead of '+' #### Sourcery refactoring id: `use-fstring-for-concatenation` [Section titled “Sourcery refactoring id: use-fstring-for-concatenation”](#sourcery-refactoring-id-use-fstring-for-concatenation) #### Description: [Section titled “Description:”](#description) Use f-strings for concatenating strings instead of ’+‘ #### Before: [Section titled “Before:”](#before) ```python output = "Name: " + name + ", Age: " + age ``` #### After: [Section titled “After:”](#after) ```python output = f"Name: {name}, Age: {age}" ``` #### Explanation: [Section titled “Explanation:”](#explanation) Python added f-strings in version 3.6, with [PEP 498](https://www.python.org/dev/peps/pep-0498/). F-strings are a flexible and powerful way to concatenate strings. They make the code shorter and more readable, since the code now looks more like the output. This refactoring replaces string concatenations using ’+’ with f-strings. This will only trigger if the concatenation is a mixture of strings and calculated values. # Use FString For Formatting > Replace calls to string.format() with f-strings. #### Sourcery refactoring id: `use-fstring-for-formatting` [Section titled “Sourcery refactoring id: use-fstring-for-formatting”](#sourcery-refactoring-id-use-fstring-for-formatting) #### Description [Section titled “Description”](#description) Replace calls to `string.format()` with f-strings. #### Before [Section titled “Before”](#before) ```python print("Good {}, {} {}".format(time_of_day, first_name, last_name)) ``` #### After [Section titled “After”](#after) ```python print(f"Good {time_of_day}, {first_name} {last_name}") ``` #### Explanation [Section titled “Explanation”](#explanation) Python added f-strings in version 3.6, with [PEP 498](https://www.python.org/dev/peps/pep-0498/). F-strings are a flexible and powerful way to format strings. They make the code shorter and more readable, since the code now looks more like the output. # Use Getitem For Re Match Groups > Access groups in re.Match objects using getitem #### Sourcery refactoring id: `use-getitem-for-re-match-groups` [Section titled “Sourcery refactoring id: use-getitem-for-re-match-groups”](#sourcery-refactoring-id-use-getitem-for-re-match-groups) #### Description [Section titled “Description”](#description) Access groups in [`re.Match`](https://docs.python.org/3/library/re.html#match-objects) objects using `getitem` #### Before [Section titled “Before”](#before) ```python m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") entire = m.group(0) # The entire match first = m.group(1) # The first parenthesized subgroup. ``` #### After [Section titled “After”](#after) ```python m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") entire = m[0] # The entire match first = m[1] # The first parenthesized subgroup. ``` #### Explanation [Section titled “Explanation”](#explanation) In Python 3.6 the ability to access groups from a match using `__getitem__` was [introduced](https://docs.python.org/3/library/re.html#re.Match.__getitem__). This is slightly shorter, and once you’ve understood that it is possible, easier to read and understand. # Use-Isna > Use `.isna()` or `.isnull()` instead of `== np.nan` for detecting missing values. #### Sourcery rule id: `use-isna` [Section titled “Sourcery rule id: use-isna”](#sourcery-rule-id-use-isna) #### Description [Section titled “Description”](#description) Use `.isna()` or `.isnull()` instead of `== np.nan` for detecting missing values. #### Before [Section titled “Before”](#before) ```python import numpy as np df['column'] == np.nan ``` #### After [Section titled “After”](#after) ```python import numpy as np df['column'].isna() ``` #### Explanation [Section titled “Explanation”](#explanation) Use `.isna()` or `.isnull()` for detecting missing values. A comparison like `df['column'] == np.nan` doesn’t produce the expected results when checking for missing or NaN (Not a Number) values. This is due to the peculiar nature of NaN: It is not considered equal to any value, even itself. ```plaintext data = { 'A': [1, 2, np.nan, 4], 'B': [9, 10, 11, 12] } df = pd.DataFrame(data) print(df['A'] == np.nan) ``` The output: 0 False 1 False 2 False 3 False Name: A, dtype: bool ```plaintext print(df['A'].isna()) ``` The output: 0 False 1 False 2 True 3 False Name: A, dtype: bool See also [Pandas Docs / Missing Data](https://pandas.pydata.org/pandas-docs/stable/user_guide/missing_data.html) # Use `itertools.product` > Replaces a nested for loop over independent iterables with itertools.product. This refactoring won't be activated if either iterable is a literal such as… #### Sourcery refactoring id: `use-itertools-product` [Section titled “Sourcery refactoring id: use-itertools-product”](#sourcery-refactoring-id-use-itertools-product) #### Description [Section titled “Description”](#description) Replaces a nested `for` loop over independent iterables with [`itertools.product`](https://docs.python.org/3/library/itertools.html#itertools.product). This refactoring won’t be activated if either iterable is a literal such as a list, tuple, or set. #### Before [Section titled “Before”](#before) ```python width = 600 height = 400 i_max, j_max = (0, 0) for i in range(width): for j in range(height): if pixels[i, j] > pixels[i_max, j_max]: i_max, j_max = i, j ``` #### After [Section titled “After”](#after) ```python import itertools width = 600 height = 400 i_max, j_max = (0, 0) for i, j in itertools.product(range(width), range(height)): if pixels[i, j] > pixels[i_max, j_max]: i_max, j_max = i, j ``` #### Explanation [Section titled “Explanation”](#explanation) The itertools product simplifies repeated for loops into a single iterator. This shortens the code, removes one level of nesting, collects together related variables (facilitating refactoring), and has better semantics (c.f. [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product)) in a number of applications (such as grid operations as in the example above). See also: [`itertools.combinations`](https://docs.python.org/3/library/itertools.html#itertools.combinations) # Use str.join() > Use str.join() instead of for loop #### Sourcery refactoring id: `use-join` [Section titled “Sourcery refactoring id: use-join”](#sourcery-refactoring-id-use-join) #### Description: [Section titled “Description:”](#description) Use `str.join()` instead of `for` loop #### Before: [Section titled “Before:”](#before) ```python all_hats = "" for hat in my_wardrobe.hats: all_hats += hat.description ``` #### After: [Section titled “After:”](#after) ```python all_hats = "".join(hat.description for hat in my_wardrobe.hats) ``` #### Explanation: [Section titled “Explanation:”](#explanation) The most straightforward way to concatenate strings in Python is to just use the `+` operator: ```python hat_description = hat.colour + hat.type ``` This is perfectly fine when you are joining together small numbers of strings (though f-strings are the best choice for doing more complicated string handling). The problem with using `+` or `+=` comes when they are used to concatenate large lists of strings. For example you might use them in a for loop like the ‘Before’ example. This is cumbersome to read, and also isn’t very performant. A new string has to be created for every iteration in the for loop, which slows things down. Luckily Python strings come with the `join` method to solve this problem. This accomplishes the same task in one line and is quite a lot faster to boot. You can also add a separator in between each string easily, without having to worry about extra separators being added at the beginning or end of the result. ```python all_hats = ", ".join(hat.description for hat in my_wardrobe.hats) ``` # Use len() > Replaces sum() with len where appropriate #### Sourcery refactoring id: `use-len` [Section titled “Sourcery refactoring id: use-len”](#sourcery-refactoring-id-use-len) #### Description: [Section titled “Description:”](#description) Replaces `sum()` with `len` where appropriate #### Before: [Section titled “Before:”](#before) ```python if sum(1 for hat in hats) > 0: self.shout("I have hats") ``` #### After: [Section titled “After:”](#after) ```python if len(hats) > 0: self.shout("I have hats") ``` #### Explanation: [Section titled “Explanation:”](#explanation) This simplification can be made where the `sum()` call is effectively just determining the length of a sized object. It can only be made where Sourcery can determine that `hats` supports the `__len__()` method. # Use Named Expression > Merge assignment followed by conditional check using a named expression. #### Sourcery refactoring id: `use-named-expression` [Section titled “Sourcery refactoring id: use-named-expression”](#sourcery-refactoring-id-use-named-expression) #### Description: [Section titled “Description:”](#description) Merge assignment followed by conditional check using a named expression. #### Before: [Section titled “Before:”](#before) ```python env_base = os.environ.get("PYTHONUSERBASE", None) if env_base: return env_base ``` ```python chunk = file.read(8192) while chunk: process(chunk) chunk = file.read(8192) ``` #### After: [Section titled “After:”](#after) ```python if env_base := os.environ.get("PYTHONUSERBASE", None): return env_base ``` ```python while chunk := file.read(8192): process(chunk) ``` #### Explanation: [Section titled “Explanation:”](#explanation) Assignment (or named) expressions were introduced into Python 3.8 via [PEP 572](https://www.python.org/dev/peps/pep-0572/). They are a way of assigning to a variable inside an expression. The value of such a named expression is the same as the incorporated expression, with the additional side-effect that the target is assigned that value. For example: ```python while chunk := file.read(8192): process(chunk) ``` The named expression here returns the result of `file.read()` to decide whether to continue the `while` loop, and also assigns the result to `chunk` so that it can be processed. Using a named expression to merge an assignment followed immediately by a condition checking the result leads to cleaner code - once you know the syntax it can be understood at a glance. It can also lead to simplification of more complex expressions involving if - for example the below: Before: ```python if self._is_special: ans = self._check_nans(context=context) if ans: return ans ``` After applying the named expression then merging the if conditions: ```python if self._is_special and (ans := self._check_nans(context=context)): return ans ``` # Use Next > Use the built-in function next instead of a for-loop. #### Sourcery refactoring id: `use-next` [Section titled “Sourcery refactoring id: use-next”](#sourcery-refactoring-id-use-next) #### Description [Section titled “Description”](#description) Use the built-in function [`next`](https://docs.python.org/3/library/functions.html#next) instead of a for-loop. #### Before [Section titled “Before”](#before) ```python def get_first_even_number(numbers): for number in numbers: if number % 2 == 0: return number return None ``` #### After [Section titled “After”](#after) ```python def get_first_even_number(numbers): return next((number for number in numbers if number % 2 == 0), None) ``` #### Explanation [Section titled “Explanation”](#explanation) When choosing the first item from an iterable that passes a condition, we can use the `next` built-in function instead of a for-loop to make our code and our intent clearer. # Use-Or-For-Fallback > Use `or` for providing a fallback value #### Sourcery rule id: `use-or-for-fallback` [Section titled “Sourcery rule id: use-or-for-fallback”](#sourcery-rule-id-use-or-for-fallback) #### Description [Section titled “Description”](#description) Use `or` for providing a fallback value #### Before [Section titled “Before”](#before) ```python pattern = provided_pattern if not pattern: pattern = pattern_from_config ``` #### After [Section titled “After”](#after) ```python pattern = provided_pattern or pattern_from_config ``` #### Explanation [Section titled “Explanation”](#explanation) Thanks to the flexibility of Python’s `or` operator, you can use a single assignment statement, even if a variable can retrieve its value from various sources. This is shorter and easier to read than using multiple assignments with `if not` conditions. # Use String Remove Affix > Replaces string slicing with the Python 3.9 features removesuffix and removeprefix. #### Sourcery refactoring id: `use-string-remove-affix` [Section titled “Sourcery refactoring id: use-string-remove-affix”](#sourcery-refactoring-id-use-string-remove-affix) #### Description: [Section titled “Description:”](#description) Replaces string slicing with the Python 3.9 features `removesuffix` and `removeprefix`. #### Before: [Section titled “Before:”](#before) ```python if text.startswith("Hello, "): text = text[7:] if text.endswith(" World!"): text = text[: -len(" World!")] ``` #### After: [Section titled “After:”](#after) ```python text = text.removeprefix("Hello, ") text = text.removesuffix(" World!") ``` #### Explanation: [Section titled “Explanation:”](#explanation) This refactoring is motivated by [PEP 616](https://www.python.org/dev/peps/pep-0616/). As stated in the [documentation](https://www.python.org/dev/peps/pep-0616/#id18), using these methods confers several advantages, including being more robust to user error, more performant, and more descriptive. # Useless-Else-On-Loop > Loop's else clause is always executed - move code to same level as loop #### Sourcery rule id: `useless-else-on-loop` [Section titled “Sourcery rule id: useless-else-on-loop”](#sourcery-rule-id-useless-else-on-loop) #### Description [Section titled “Description”](#description) Loop’s else clause is always executed - move code to same level as loop #### Before [Section titled “Before”](#before) ```python evens = [] for n in numbers: if n % 2: evens.append(n) else: print("Done!") ``` #### After [Section titled “After”](#after) ```python evens = [] for n in numbers: if n % 2: evens.append(n) print("Done!") ``` #### Explanation [Section titled “Explanation”](#explanation) Loops should only have an `else` clause if they can exit early with a `break` statement. If there is no `break` then the code in the `else` is always executed. In this case the `else` statements can be moved to the same scope as the loop itself, making the code slightly easier to understand (no need to look up what the `else` does). # While-Guard-To-Condition > Move a guard clause in a while statement's body into its test #### Sourcery rule id: `while-guard-to-condition` [Section titled “Sourcery rule id: while-guard-to-condition”](#sourcery-rule-id-while-guard-to-condition) #### Description [Section titled “Description”](#description) Move a guard clause in a while statement’s body into its test #### Before [Section titled “Before”](#before) ```python location = "Work" destination = "Home" while True: if location == destination: break location = travel() ``` #### After [Section titled “After”](#after) ```python location = "Work" destination = "Home" while not location == destination: location = travel() ``` #### Explanation [Section titled “Explanation”](#explanation) Removing the guard clause simplifies the code and makes clearer the intention of the loop. # Replace while with for > Replaces a while loop with a counter by a for loop #### Sourcery refactoring id: `while-to-for` [Section titled “Sourcery refactoring id: while-to-for”](#sourcery-refactoring-id-while-to-for) #### Description: [Section titled “Description:”](#description) Replaces a `while` loop with a counter by a `for` loop #### Before: [Section titled “Before:”](#before) ```python i = 0 while i < 10: print("I love hats") i += 1 ``` #### After: [Section titled “After:”](#after) ```python for i in range(10): print("I love hats") ``` #### Explanation: [Section titled “Explanation:”](#explanation) We often need to iterate over the same bit of code a certain number of times. Developers who are unfamiliar with Python may consider using a `while` loop for this, but it’s almost certainly better to use `for`. Converting from `while` to `for` means the explicit handling of the counter can be removed, shortening the code and making it esier to read. # Yield from > Replaces yield as part of for loops with yield from #### Sourcery refactoring id: `yield-from` [Section titled “Sourcery refactoring id: yield-from”](#sourcery-refactoring-id-yield-from) #### Description: [Section titled “Description:”](#description) Replaces `yield` as part of `for` loops with `yield from` #### Before: [Section titled “Before:”](#before) ```python def get_content(entry): for block in entry.get_blocks(): yield block ``` #### After: [Section titled “After:”](#after) ```python def get_content(entry): yield from entry.get_blocks() ``` #### Explanation: [Section titled “Explanation:”](#explanation) One little trick that often gets missed is that Python’s `yield` keyword has a corresponding `yield from` for collections, so there’s no need to iterate over a collection with a for loop. This makes the code slightly shorter and removes the mental overhead and extra variable used by the for loop. Eliminating the for loop also makes the `yield from` version about 15% faster. Note that this cannot be done on `async` functions. # Code reviews > What Sourcery posts on every pull request, and where to go next based on your role. Sourcery reviews your pull requests automatically, and every review posts the same things. ## What every review posts [Section titled “What every review posts”](#what-every-review-posts) Every review posts: * A summary in the pull request description, covering the change’s purpose and risk. * A reviewer’s guide comment that maps the change set file by file and explains how to verify it. * Inline comments on the offending lines, with one-click apply on suggested fixes. * A status check called **Sourcery review** that reports the review state and can gate merges. No status check posts on GitLab merge requests. [Anatomy of a review](/reviews/anatomy-of-a-review/) walks through each one in detail. ## When Sourcery reviews [Section titled “When Sourcery reviews”](#when-sourcery-reviews) Sourcery reviews a pull request as soon as it opens, and again when you push new commits. It reviews code in any language GitHub recognizes as a programming or markup language. A few cases skip the code review by default: draft pull requests, pull requests from dependency bots, packaging-only changes, and pull requests whose diff is over your plan’s size cap. You can change most of this in [the review settings](/reviews/configure/). ## What you can tune [Section titled “What you can tune”](#what-you-can-tune) * [Configure code reviews](/reviews/configure/) sets what Sourcery posts, which pull requests it reviews, and per-repo overrides. * [Review rules](/reviews/review-rules/) encode your team’s conventions as explicit checks. * [Review rules](/reviews/review-rules/) states your conventions so Sourcery checks them every time. * [Review language](/reviews/configure/#review-language) writes reviews in a language other than English. ## What’s next [Section titled “What’s next”](#whats-next) [Anatomy of a review ](/reviews/anatomy-of-a-review/)Read one review from top to bottom. [Commands ](/reviews/commands/)Drive Sourcery from pull request comments with @sourcery-ai. [Analytics ](/reviews/analytics/)See how reviews affect your team's throughput and cycle time. # Code review analytics > What each analytics tab measures and how to read them as an engineering manager. Analytics show how reviews affect your team’s throughput. They live on the [**Analytics**](https://app.sourcery.ai/dashboard/analytics) page in the dashboard and are a [Team plan](/admin/plans/) feature. ## The tabs [Section titled “The tabs”](#the-tabs) | Tab | What it shows | | ------------ | ---------------------------------------------------------- | | Overview | The landing tab, with the headline numbers rolled up. | | PR Lifecycle | Cycle time by stage, and where pull requests wait. | | Code Reviews | Review volume from Sourcery compared with human reviewers. | | Developers | The same metrics per developer. | | Repositories | The same metrics per repository. | | Trends | The numbers plotted over time. | # Anatomy of a review > Read one Sourcery review from top to bottom: summary, reviewer's guide, inline comments, status check, and what changes when you push again. Every Sourcery review has the same parts. Here’s what each one is, where to find it on the pull request, and what changes when you push more commits. ## Summary [Section titled “Summary”](#summary) Sourcery writes a summary into the pull request description itself, under a **Summary by Sourcery** heading. It covers what the change does and where the risk is. Because it lives in the description rather than a comment, it stays at the top of the pull request as the conversation grows. Sourcery writes the summary when the review runs; re-reviews don’t regenerate it. To refresh it, comment `@sourcery-ai summary`. ## Reviewer’s guide [Section titled “Reviewer’s guide”](#reviewers-guide) The reviewer’s guide is a comment that maps the whole change set. It opens with a short overview, then a file-level changes table showing what changed and why, so a human reviewer knows where to focus. For changes with non-trivial control flow, the guide can include a sequence diagram. Diagrams are on by default; use the **Enable sequence diagrams** toggle in [the review settings](/reviews/configure/) to turn them off. ## Inline comments [Section titled “Inline comments”](#inline-comments) Inline comments are per-line feedback, posted as native review comments on the lines in question. Each comment is prefixed with its type and area, so you can tell what kind of feedback it is: `issue (bug_risk):`, `suggestion (performance):`, `issue (security):`, and so on. Security comments carry a 🚨 marker. Where Sourcery can propose a concrete fix, the comment includes a suggestion block you apply with GitHub’s **Commit suggestion** button. When a pull request removes code that a security scan previously flagged, the review also lists the findings it resolves, linked to the dashboard. See [security and pull request checks](/security/github-status-checks/). ### Reacting to a comment [Section titled “Reacting to a comment”](#reacting-to-a-comment) Every review comment carries a thumbs up and a thumbs down, and reacting teaches Sourcery what your team finds useful. Where many similar past comments were marked unhelpful, Sourcery suppresses similar new ones. It’s a gradual signal. One downvote won’t silence a topic, it takes a consistent pattern across similar comments, and a comment can still appear where the surrounding code warrants it. A comment that goes outdated counts as positive: the code it flagged got changed, so the comment did its job. Reactions are scoped per git organization, so a reaction in one repository carries across all of them. When the same request keeps coming up across reviews, a [review rule](/reviews/review-rules/) states it outright instead of waiting for reactions to add up. ## Status check [Section titled “Status check”](#status-check) On GitHub, every review reports a single status check called **Sourcery review** on the pull request’s head commit. (GitLab merge requests don’t get a status check; there, the review is the summary, guide, and inline comments.) It moves through these states: * In progress, while the review runs. * Success, when the review posts with no blocking issues. * Failure, when blocking security findings require changes. * Skipped, when a rate limit or the re-review cap applies. Because the check posts on the head commit, you can add **Sourcery review** to a branch protection rule and require it before merge. The check posts even when the review is skipped, so requiring it never blocks a merge. See [security and pull request checks](/security/github-status-checks/) for how the **Sourcery review** check behaves. ## Linked issues [Section titled “Linked issues”](#linked-issues) Sourcery uses related GitHub Issues as review context. For issues linked in the pull request description, it assesses whether the change actually addresses them. It also searches your repository for other issues that look related and links them in the reviewer’s guide. ## Re-reviews [Section titled “Re-reviews”](#re-reviews) Sourcery reviews a pull request when it opens and reacts to every commit after that. The reaction is lighter than the first review. A re-review: * Re-checks each existing Sourcery comment against the new code and resolves the threads the new commits address. * Re-runs the security scans and posts any new findings as a fresh review. * Leaves the rest of the review in place. It doesn’t regenerate the summary, reviewer’s guide, or the full set of inline comments. Automatic re-reviews are capped at five per pull request. Past the cap, Sourcery stops re-reviewing automatically and the check reports Skipped. Comment `@sourcery-ai review` to reset the counter and run a complete review from scratch. See [commands](/reviews/commands/) for the rest of the command set. Rate limits are separate from the re-review cap. Sourcery measures them in diff characters and skips a review when one is reached, and the limits vary by plan. See [plans and pricing](/admin/plans/) for the caps and how the budget works. Reviews adapt to your team The parts are the same on every review, but the comments themselves change as your team reacts to them and as you add [review rules](/reviews/review-rules/). # Commands > Every @sourcery-ai command and the chat behavior that backs it, for GitHub and GitLab. You drive Sourcery from pull request comments. Mention `@sourcery-ai` followed by a command, or reply in a thread to chat. The handle is the same on GitHub and GitLab, and variants such as `@sourceryai` also work. ## Commands [Section titled “Commands”](#commands) Post the handle and the command as their own comment, for example `@sourcery-ai review`. | Command | What it does | | -------------- | ----------------------------------------------------------------------------------------------------------------- | | `review` | Runs a full review on demand and resets the per-pull-request re-review counter. | | `summary` | Regenerates the summary in the pull request description. | | `guide` | Regenerates the reviewer’s guide comment. | | `title` | Regenerates the pull request title. | | `resolve` | Resolves Sourcery’s open review comments on the pull request. | | `dismiss` | Dismisses the **Sourcery review** status check. | | `create issue` | Creates a GitHub issue from a review comment. Works only as a reply inside a review comment thread (GitHub only). | ## Chat [Section titled “Chat”](#chat) Anything that isn’t a command becomes chat. A reply in a thread Sourcery started goes to its chat handler, so you can ask a follow-up question on a comment and get an answer in the same thread. A top-level pull request comment is only read as chat when it mentions `@sourcery-ai`. Comments from bots are ignored, so a thread doesn’t loop. `@sourcery-ai review` resets the per-pull-request re-review counter and runs a fresh full review. See [re-reviews](/reviews/anatomy-of-a-review/#re-reviews) for what the counter controls. # Review settings > What a review posts, which pull requests get reviewed, what language reviews are written in, and how to override any of it for one repository. [**Review Settings**](https://app.sourcery.ai/dashboard/review-settings) controls what Sourcery posts on a pull request and which pull requests it reviews. It sets the defaults for every repository, and **Repo settings** overrides them for one. ## What a review posts [Section titled “What a review posts”](#what-a-review-posts) **Enable Sourcery on pull requests** is the top-level switch. Turn it off and Sourcery posts nothing on a repository’s pull requests. The rest are independent of each other. | Toggle | What it posts | | ------------------------------- | -------------------------------------------- | | **Enable AI review comments** | The inline comments and the review body. | | **Enable pull request summary** | The summary in the pull request description. | | **Enable review guide** | The reviewer’s guide comment. | | **Enable sequence diagrams** | Sequence diagrams in the reviewer’s guide. | | **Enable tips and commands** | The tips comment that lists chat commands. | ## Which pull requests get reviewed [Section titled “Which pull requests get reviewed”](#which-pull-requests-get-reviewed) Filters narrow which pull requests Sourcery reviews. An empty filter adds no restriction of its own, so Sourcery applies its default behavior. | Filter | What it does | | ------------------------------ | ------------------------------------------------------------------------------------------ | | **Ignore title keywords** | Skips a pull request whose title contains any listed keyword. | | **Labels** | An allowlist. When set, Sourcery reviews only pull requests carrying one of the labels. | | **Base branches** | An allowlist of regular expressions matched against the base branch. | | **Review draft pull requests** | Turns on reviews for drafts, which are skipped by default. | | **Path Filters** | A single glob list on the General tab that excludes matching files from the reviewed diff. | ## Review language [Section titled “Review language”](#review-language) Sourcery writes reviews in English by default, but it can write in many others. The language applies to the summary, the reviewer’s guide, and the inline comments. Pick a language from the **Select language** dropdown on the General tab. The change applies to reviews from then on. Open the dropdown for the current list. ## Settings for one repository [Section titled “Settings for one repository”](#settings-for-one-repository) Settings apply at two scopes. Organization defaults cover every repository, and a repository can override them. A repository follows the organization defaults while its **Use Organization Settings** toggle is on. Turn the toggle off to edit that repository on its own. Turning it back on and applying restores the organization defaults and discards the repository’s own settings. There is no separate revert action. 1. Open the repository's settings Open [**Repo settings**](https://app.sourcery.ai/dashboard/repo-settings) and pick the repository you want to change. 2. Turn off Use Organization Settings The form unlocks, pre-filled with the organization defaults as a starting point. 3. Edit and apply Change the settings for this repository and apply. If you change your mind later, turn **Use Organization Settings** back on and apply to go back to the organization defaults. Note Per-team conventions live on the Review rules tab. See [review rules](/reviews/review-rules/). ## What’s next [Section titled “What’s next”](#whats-next) [Review rules ](/reviews/review-rules/)Encode your team's conventions as review rules. [Re-reviews ](/reviews/anatomy-of-a-review/#re-reviews)What happens when you push new commits. # Write review rules > Write a review rule in plain language and scope it to the paths it applies to. Most teams already have the raw material for review rules written down somewhere. A style guide, the conventions section of an engineering handbook, a pull request checklist, or just the three things you find yourself repeating in review every week. A review rule takes one of those, states it in plain language, and scopes it to the files it applies to. Rules live on the **Review rules** tab of [**Review Settings**](https://app.sourcery.ai/dashboard/review-settings). They’re available on all plans, and were previously called custom rules. ## Write a rule [Section titled “Write a rule”](#write-a-rule) 1. Open the Review rules tab Go to [**Review Settings**](https://app.sourcery.ai/dashboard/review-settings) and open the **Review rules** tab. 2. Set the path patterns Add one or more globs under **Path patterns**, such as `src/api/**`. The rule applies only to files that match. Not sure how narrow to go? Leave it broad to apply everywhere. 3. Write the rule Describe what Sourcery should check in the **Rule** field, which takes up to 3,000 characters of free-form text. Be specific about what good and bad look like. ## How a rule changes a review [Section titled “How a rule changes a review”](#how-a-rule-changes-a-review) Sourcery checks the rule against every changed file its path patterns match. Where the code falls short, you get an ordinary inline comment on the line in question, carrying the same `type (area):` prefix as the rest of the review, with the rule quoted underneath so you can tell which one produced it. A rule adds to a review rather than narrowing it. Sourcery still raises the bugs and issues it would have raised on those files anyway, and a rule only ever looks at what the pull request changes, not the rest of the repository. ## Example rules [Section titled “Example rules”](#example-rules) ### From your style guide [Section titled “From your style guide”](#from-your-style-guide) **Path patterns**: `**` > Every new public function needs a docstring that explains its purpose. Flag any new public function without one. ### From your naming conventions [Section titled “From your naming conventions”](#from-your-naming-conventions) **Path patterns**: `src/**/*.tsx` > Custom React hooks must be named with a `use` prefix and live in `src/hooks`. Flag any hook declared elsewhere or named without the prefix. ### From your architecture rules [Section titled “From your architecture rules”](#from-your-architecture-rules) **Path patterns**: `src/api/**` > Endpoints must validate request input before using it. Flag any handler that reads request data without validating it first, and suggest the validation to add. ### From a migration in flight [Section titled “From a migration in flight”](#from-a-migration-in-flight) **Path patterns**: `**/*.py` > We are migrating from `requests` to `httpx`. Flag any new code that imports `requests` and suggest the `httpx` equivalent. Each one says what to flag and what you want instead, which is what separates a rule Sourcery can act on from a sentence of good intentions. Note Review rules apply only to code review. The security scanners run fixed rule sets and are not affected by your review rules. ## What’s next [Section titled “What’s next”](#whats-next) [Review settings ](/reviews/configure/)Tune what Sourcery posts and which pull requests get reviewed. [Anatomy of a review ](/reviews/anatomy-of-a-review/)Where rule comments appear, and how reactions shape later reviews. # Security scanning > What Sourcery's security scanners cover, how they run, and where findings appear. Sourcery scans your repositories for security problems and collects everything it finds in one place. The scanners run together. You triage secrets, code vulnerabilities, misconfigurations, and dependency risk from a single view. ## What gets scanned [Section titled “What gets scanned”](#what-gets-scanned) The scanners run as one logical scan on your default branch: * Secrets, for committed credentials and tokens. * Static analysis (SAST), for code-level vulnerabilities. * Infrastructure as code, for misconfigurations in Terraform, Kubernetes, and other infrastructure files. * Dependencies and licenses, for vulnerable packages and restrictive licenses. [The scanners](/security/scanners/) lists the tool behind each one and the issue type it produces. ## Where findings go [Section titled “Where findings go”](#where-findings-go) Every scanner writes to the same store, [**Issues**](https://app.sourcery.ai/dashboard/security/issues). The workflow is the same whatever the scanner: each finding carries a severity, a status, and the same set of actions, so you snooze, ignore, or override a secret the same way you would a dependency vulnerability. [Findings and severity](/security/findings/) covers how a finding is modeled, and [triage a finding](/security/triage/) walks through every action you can take on one. ## When scans run [Section titled “When scans run”](#when-scans-run) Scans run on a schedule that depends on your plan, against the default branch only. [When scans run](/security/scan-schedule/) covers the cadence and how to queue a scan on demand. ## Plans [Section titled “Plans”](#plans) Security scanning is a Team plan feature. Open Source and Pro include a limited preview. See [plans and pricing](/admin/plans/) for the plan-by-plan details. ## What’s next [Section titled “What’s next”](#whats-next) [The scanners ](/security/scanners/)What each scanner finds and the issue type it produces. [Findings and severity ](/security/findings/)How a finding is scored, grouped, and resolved. [Triage a finding ](/security/triage/)Snooze, ignore, and override the findings you see. # Fix a finding with an agent > Copy a ready-made agent prompt or open a finding in Cursor to hand the fix to an AI agent. Once you have a finding you want fixed, Sourcery can write the AI agent’s prompt for you. Two actions on each finding hand it off, and both work on any issue type. ## Copy the prompt [Section titled “Copy the prompt”](#copy-the-prompt) Click **Copy agent prompt** to put a ready-made prompt on your clipboard. Paste it into whatever coding agent you use. The prompt is filled with the finding’s details: ```text Please fix the following security issue: [title] [risk] [cause] src/api/handlers.py:12-34 [the recommended fix] [what the fix changes] Keep the changes minimal - only make the necessary code changes to fix the security issue. ``` When a fix also needs configuration or infrastructure changes, the prompt ends by asking the agent to tell you about them. ## Open in Cursor [Section titled “Open in Cursor”](#open-in-cursor) Click **Open in Cursor** to open the same prompt directly in Cursor through a deeplink, so you skip the copy and paste. This action appears only when the Cursor integration is turned on for your account. An admin turns it on with the Cursor card on the [**Integrations**](https://app.sourcery.ai/dashboard/integrations) page. Until then, only **Copy agent prompt** shows. ## What’s next [Section titled “What’s next”](#whats-next) [Triage a finding ](/security/triage/)Snooze, ignore, or re-rate a finding instead of fixing it. [Findings and severity ](/security/findings/)How to read the findings you're handing off. # Findings and severity > How a security finding is scored, grouped, and resolved, so you can read the issues view. Every scanner produces findings of the same shape. Each finding carries a severity, a status, and a place in a group. Those three are all you need to find your way around [**Issues**](https://app.sourcery.ai/dashboard/security/issues). ## Severity [Section titled “Severity”](#severity) Each finding has a severity score from 0 to 100, which the dashboard buckets into levels: | Level | Score range | | -------- | ----------- | | No risk | 0 | | Low | 1–34 | | Medium | 35–69 | | High | 70–89 | | Critical | 90–100 | The score combines the rule’s base risk with your repository’s risk profile, so the same rule can come out at different severities in different repositories. See [set a repository’s risk profile](/security/repository-risk-profile/). You can also [override the severity](/security/triage/) on a finding when you’d rank it differently. ## Status [Section titled “Status”](#status) A finding has one of these statuses: | Status | Meaning | | ------- | -------------------------------------------------------- | | Active | Detected on the latest scan and not set aside | | Snoozed | Set aside temporarily from triage, until you unsnooze it | | Ignored | Marked as not needing action from triage | | Solved | Not detected on the latest scan | Active and solved are system-driven. When a scan no longer detects a finding, Sourcery marks it solved; if the same problem reappears in a later scan, it becomes active again. You don’t mark findings solved yourself. Snoozed and ignored are the two states you set, from [triage](/security/triage/). ## Grouping [Section titled “Grouping”](#grouping) Findings that look alike are grouped together by default, so a rule that fires across many files reads as one row rather than dozens. Turn off the **Group issues** toggle on the issues view to see every finding on its own. ## What’s next [Section titled “What’s next”](#whats-next) [The scanners ](/security/scanners/)Where findings come from and the issue type each scanner produces. [Triage a finding ](/security/triage/)Snooze, ignore, and override the findings you see. [Security reports ](/security/reports/)See findings as trends over time. [Sourcery API ](/api/)Pull findings into your own tools with the REST API. # Security and pull request checks > Whether security scanning can gate your pull requests, and how security findings reach the Sourcery review status check. Security scanning can block a pull request from merging. It does this through the single **Sourcery review** check rather than a security check of its own: when a review finds a security problem in the diff, it posts a blocking comment that fails that check. The scheduled security scan is separate and reports to the dashboard. ## Where the scheduled scan reports [Section titled “Where the scheduled scan reports”](#where-the-scheduled-scan-reports) The [scheduled security scan](/security/scan-schedule/) runs against your default branch and reports to the dashboard. It is tied to the branch rather than a pull request or commit, so it posts to the dashboard and cannot be required in branch protection. ## Security findings in a review [Section titled “Security findings in a review”](#security-findings-in-a-review) The blocking comment is part of the normal [review](/reviews/anatomy-of-a-review/), and the check it fails is the same **Sourcery review** check every review reports. The check stays failed while a blocking security comment is unresolved. ## When a pull request fixes a finding [Section titled “When a pull request fixes a finding”](#when-a-pull-request-fixes-a-finding) The reverse also shows up on the pull request. When a pull request removes code that a scan previously flagged, Sourcery marks the finding [solved](/security/findings/) and lists the findings the pull request resolves, each linked to its entry in the dashboard, so a reviewer sees the security impact of the change in context. This is part of [security scanning](/security/), so it runs when scanning is enabled on the repository. ## Requiring the check [Section titled “Requiring the check”](#requiring-the-check) If you want a blocking finding to stop the merge, require the **Sourcery review** check in your GitHub branch protection rules. That one check covers blocking security findings alongside everything else a review can block on. See [anatomy of a review](/reviews/anatomy-of-a-review/) for how the check moves between its states. # Security reports > What each of the three security reports shows and how to filter them. The [**Security Reports**](https://app.sourcery.ai/dashboard/security/reports) page turns your [findings](/security/findings/) into trends over time. There are three reports. ## The three reports [Section titled “The three reports”](#the-three-reports) | Report | What it shows | | ----------- | ------------------------------------------------------------------------- | | Trends | Open issues over time, broken out by severity. | | Remediation | Resolution rate, mean time to remediate, overdue issues, and backlog age. | | Risk Impact | Your total risk score, how it changed over the window, and its trend. | ## Filters [Section titled “Filters”](#filters) Every report shares four filters, so you can narrow each one to the same slice: | Filter | What it narrows to | | ----------- | ----------------------------- | | Repository | A single repository. | | Issue type | One scanner’s issue type. | | Severity | A minimum severity level. | | Time period | The window the report covers. | A report only shows meaningful data once scans have run, so a freshly connected repository fills in over its first few scans. # Set a repository's risk profile > Set a repository's network exposure and data sensitivity so Sourcery weights its security severities to match. A finding on a public, customer-facing service is more urgent than the same finding on an internal tool. A repository’s risk profile is how you tell Sourcery which kind of repository it’s looking at, so it weights severities to match. ## The two dimensions [Section titled “The two dimensions”](#the-two-dimensions) A risk profile has two dimensions: | Dimension | Choices | | ---------------- | ----------------------------------- | | Network exposure | Public network, internal only | | Data sensitivity | Critical, high, standard, low, none | The more exposed the network and the more sensitive the data, the more Sourcery raises the severity of findings in that repository. A repository with no profile set is treated as standard data sensitivity, with no network-exposure adjustment. ## Set the profile [Section titled “Set the profile”](#set-the-profile) 1. Open the repositories page Go to [**Repositories**](https://app.sourcery.ai/dashboard/security/repositories) and find the repository. 2. Choose its exposure and sensitivity Set the network exposure and data sensitivity that describe how this repository is deployed and what data it handles. 3. Save Save the profile. It applies on the repository’s next scan. ## When it takes effect [Section titled “When it takes effect”](#when-it-takes-effect) A risk profile feeds severity scoring, which happens at scan time. Setting a profile does not re-score what is already on screen. On the next scan, Sourcery re-scores the repository’s findings using the new profile, so existing findings pick up the change then. If you’d rather not wait for the schedule, [queue a scan](/security/scan-schedule/) to pick it up sooner. ## What’s next [Section titled “What’s next”](#whats-next) [Findings and severity ](/security/findings/)How the profile feeds into a finding's severity score. [When scans run ](/security/scan-schedule/)The schedule a new profile takes effect on. # When scans run > The security scan cadence by plan, and how to queue a scan on demand. Sourcery scans on a schedule that depends on your plan. Every scan runs against the default branch. Security scanning is a Team plan feature; the Open Source and Pro cadence is part of their limited preview. See [plans and pricing](/admin/plans/) for what each plan covers. ## Cadence by plan [Section titled “Cadence by plan”](#cadence-by-plan) | Plan | Scan cadence | | ----------- | --------------------------------- | | Open Source | Twice weekly, Monday and Thursday | | Pro | Twice weekly, Monday and Thursday | | Team | Daily | A repository is scanned only while security scanning is enabled on it. The switch is in the **Scanning enabled** column on [**Repositories**](https://app.sourcery.ai/dashboard/security/repositories). Repositories with scanning disabled are skipped, as are archived repositories. ## Queue a scan on demand [Section titled “Queue a scan on demand”](#queue-a-scan-on-demand) On the Team plan, you can run a scan without waiting for the schedule. On [**Repositories**](https://app.sourcery.ai/dashboard/security/repositories), find the repository and click **Start Scan**. The scan runs against the current default branch and updates that repository’s findings when it finishes. [Set a repository’s risk profile](/security/repository-risk-profile/) and [findings and severity](/security/findings/) explain how a completed scan scores what it finds. # The scanners > What each Sourcery security scanner finds and which issue type it produces. Sourcery runs these scanners over your default branch. Together they produce the issue types listed on [**Issues**](https://app.sourcery.ai/dashboard/security/issues); the [Sourcery API](/api/) refers to them by the `issue_type` codes. | Scanner | Tool | What it finds | Issue type | API `issue_type` | | ------------ | ----------- | ---------------------------------------------------------------------------- | ---------------------- | ---------------- | | Secrets | BetterLeaks | Committed credentials and tokens | Exposed Secrets | `secret` | | SAST | OpenGrep | Code-level vulnerabilities | SAST | `sast` | | IaC | Checkov | Terraform, CloudFormation, Kubernetes, Docker, and Ansible misconfigurations | Infrastructure as Code | `iac` | | Dependencies | Trivy | Vulnerable packages, including transitive paths | Dependencies | `dependency` | | Licenses | Trivy | Restrictive or unknown dependency licenses | Licenses | `license` | ## What the scanners cover [Section titled “What the scanners cover”](#what-the-scanners-cover) * Secrets scanning is language-agnostic. It checks the working tree and the full git history. A credential committed and later removed is still reported. * SAST covers C, C#, Go, Java, JavaScript, Kotlin, PHP, Python, Ruby, Rust, Scala, and TypeScript. * IaC checks Terraform, CloudFormation, Kubernetes, Docker, and Ansible files, with rules across AWS, Azure, and GCP. * Dependency and license scanning covers the package ecosystems Trivy detects from your lockfiles, including development dependencies. ## How the tools behave [Section titled “How the tools behave”](#how-the-tools-behave) Trivy produces both the Dependencies and Licenses issue types from a single run. For a dependency finding, the dashboard renders the dependency graph. Trace a vulnerable transitive package back to the direct dependency that pulled it in. BetterLeaks is a fork of Gitleaks and keeps its rule IDs. A secret finding’s rule matches the Gitleaks rule of the same name. # Triage a finding > Snooze, ignore, and override the severity of a security finding, and when to use each. You decide what happens to a finding: snooze it, ignore it, or override its severity, all from its actions menu. The same actions are available on a single finding and on a group of similar findings, on [**Issues**](https://app.sourcery.ai/dashboard/security/issues). Every action takes an optional reason you can record alongside it. ## Snooze [Section titled “Snooze”](#snooze) Snooze a finding when you plan to deal with it later. Set a snooze-until date and an optional reason. The finding moves to a snoozed state and stays there until you unsnooze it from the same menu. Snoozing does not solve the finding or stop later scans from detecting it. ## Ignore [Section titled “Ignore”](#ignore) Ignore a finding when you have decided it does not need action. The dialog gives you three choices: ignore just the findings you selected, ignore current and future findings under a path in the repository, or ignore current and future findings for a security rule, in one repository or all of them. The path and rule choices also cover findings that future scans turn up, so you ignore a class of finding once rather than each time it reappears. Pick the narrowest choice that covers your decision; an ignore that’s too broad hides future findings you’d want to see. ## Override severity [Section titled “Override severity”](#override-severity) Override the severity when the computed level isn’t how you’d rank it. Pick a new level, add a reason if you want one, and save. The finding shows your level instead of the scored one. An override isn’t permanent: removing it restores the computed severity. You can override a single finding or a whole group. ## What’s next [Section titled “What’s next”](#whats-next) [Findings and severity ](/security/findings/)How severity and status are computed in the first place. [Fix a finding with an agent ](/security/agent-fix-prompts/)Hand a finding to an AI agent to fix. # Get started > Pick the quickstart for your Git platform, or run a structured pilot if you're evaluating Sourcery for your team. Sourcery reviews every pull request and scans your repos for security issues. Pick the quickstart that matches your Git platform. ## Quickstarts [Section titled “Quickstarts”](#quickstarts) [Quickstart on GitHub.com ](/start/quickstart-github/)Install the Sourcery GitHub app on your organization. [Quickstart on GitLab.com ](/start/quickstart-gitlab/)Connect your GitLab.com group through OAuth. [Quickstart on GitHub Enterprise Server ](/start/quickstart-ghes/)Set up the OAuth app and GitHub App on your GHES instance. [Quickstart on self-hosted GitLab ](/start/quickstart-gitlab-self-hosted/)Create a GitLab application and connect Sourcery to your instance. ## Evaluating Sourcery [Section titled “Evaluating Sourcery”](#evaluating-sourcery) If you have a mandate to evaluate Sourcery for your team, the pilot page lays out a two-week plan and the evidence to collect along the way. [Run a pilot ](/start/run-a-pilot/)A structured trial framework for evaluators. # Quickstart on GitHub Enterprise Server > Connect a GitHub Enterprise Server instance, create the Sourcery GitHub App, and read your first review. Sourcery reviews every pull request on the repos you connect. Set it up on your GitHub Enterprise Server instance: connect the instance, create the Sourcery GitHub App on it, and open a pull request to read your first review. This works on every plan. ## Before you start [Section titled “Before you start”](#before-you-start) Required permissions You connect GHES as an admin. You need a personal access token with the `read:org` scope, and creating the Sourcery GitHub App on an organization is an owner-level action, so you need owner permissions on each organization you connect. If your GHES instance sits behind a firewall, allow Sourcery’s static egress IPs so Sourcery can reach it. See [Network requirements](/trust/network-requirements/) for the addresses to allowlist. ## Install Sourcery [Section titled “Install Sourcery”](#install-sourcery) 1. Enter your GHES instance URL Go to [`/login/github-enterprise-server`](https://app.sourcery.ai/login/github-enterprise-server), enter your **GitHub Enterprise Server URL** (for example, `https://github.example.com`), and click **Login**. If the instance isn’t connected yet, Sourcery moves you to setup. 2. Provide a read:org access token In the **GitHub access token** field, paste a personal access token with the `read:org` scope, created from an admin account. Sourcery uses it to fetch your organizations and seed the GitHub App. Click **Continue**. You can delete the token once setup finishes. 3. Create the GitHub App on your organization Choose an organization for the `sourcery-ai` GitHub App, then click **Create GitHub App**. GHES creates the App from a manifest and redirects back to Sourcery, which finishes setup. **Permissions** The GHES App requests the same permissions as the GitHub.com app: Read-only access metadata, organization members, and email addresses Read and write access actions, checks, code, commit statuses, issues, pull requests, and workflows Your code stays yours Write access lets Sourcery post reviews, set the status check, and apply suggested fixes. We don’t retain your source code after a review. See [Code retention](/trust/code-retention/) for the detail your security team will want. 4. Log in and start reviewing That’s it for setup: Sourcery logs you in through the new connection, the account dashboard opens, and code review is ready on the connected organization. If you have other organizations to connect, just repeat the setup for each one. Already using Sourcery? To connect another GHES organization later, run the same login flow and pick a different organization when you create the GitHub App. ## Your first review [Section titled “Your first review”](#your-first-review) Open a pull request on any connected repo. Review behavior matches GitHub.com, and you’ll see: * A summary in the pull request description covering the change’s purpose and risk. * A reviewer’s guide comment with the code paths that changed and how to verify them. * Inline comments on the specific lines they’re about. * A status check called **Sourcery review** next to the merge button. Re-review on new commits and `@sourcery-ai` chat commands work the same as on GitHub.com. ## What’s next [Section titled “What’s next”](#whats-next) [Network requirements ](/trust/network-requirements/)Egress IPs and connectivity rules for connecting Sourcery to GHES. [Anatomy of a review ](/reviews/anatomy-of-a-review/)Walk through one Sourcery review from top to bottom. [Configure code reviews ](/reviews/configure/)Adjust Sourcery's review behavior per repo or per organization. # Quickstart on GitHub.com > Install the Sourcery GitHub app, choose what it covers, and read your first review. Sourcery reviews every pull request on the repos you connect. Set it up in a few minutes: install the GitHub app, pick your repos, and open a pull request to read your first review. ## Before you start [Section titled “Before you start”](#before-you-start) Required permissions You install Sourcery as a GitHub app. Installing an app on an organization is an owner-level action, so you need owner permissions on that organization. For a personal account, you already have everything you need. ## Install Sourcery [Section titled “Install Sourcery”](#install-sourcery) 1. Log in with GitHub Go to the [Sourcery dashboard](https://app.sourcery.ai/) and click **Log in with GitHub**. The first time you log in, Sourcery sends you straight to GitHub to install the app. 2. Choose where Sourcery runs Pick the organization or personal account where you want reviews. If you belong to several organizations, just pick one to start; you can connect the others later. 3. Set access and authorize Choose which repositories Sourcery can reach, then review the permissions it requests. **Repository access** You have two options here. **All repositories** covers everything in the organization, now and in the future. **Only select repositories** limits Sourcery to the repos you pick. We recommend all repositories for most teams: new repos get reviews automatically, and you can always narrow the selection later. **Permissions** Read-only access metadata, organization members, and email addresses Read and write access actions, checks, code, commit statuses, issues, pull requests, and workflows Your code stays yours Write access lets Sourcery post reviews, set the status check, and apply suggested fixes. We don’t retain your source code after a review. See [Code retention](/trust/code-retention/) for the detail your security team will want. Click **Install & Authorize**. GitHub sends you back to Sourcery. 4. Choose what to scan Sourcery asks which repositories to scan for security issues and pre-selects your most recently updated ones. Pick the repos you want covered and click **Start**. That’s it for setup: the security dashboard opens, and code review is already running on every pull request. Already using Sourcery? To add another organization later, click your account name at the top of the sidebar to open the account switcher, then click **Connect GitHub organization**. ## Your first review [Section titled “Your first review”](#your-first-review) Now for the good part. Open a pull request on any connected repo. Sourcery reviews the change, and you’ll see: * A summary in the pull request description covering the change’s purpose and risk. * A reviewer’s guide comment with the code paths that changed and how to verify them. * Inline comments on the specific lines they’re about. * A status check called **Sourcery review** next to the merge button. On your first review, Sourcery also posts a tips and commands comment that lists the chat commands and configuration available. It’s handy while you learn the commands; once you don’t need it, turn off **Enable tips and commands** in [Configure code reviews](/reviews/configure/). ## What’s next [Section titled “What’s next”](#whats-next) [Anatomy of a review ](/reviews/anatomy-of-a-review/)Walk through one Sourcery review from top to bottom. [Commands ](/reviews/commands/)Talk to Sourcery from pull request comments using @sourcery-ai. [Configure code reviews ](/reviews/configure/)Adjust Sourcery's review behavior per repo or per organization. # Quickstart on GitLab.com > Connect a GitLab.com namespace, pick the projects to review, and read your first merge request review. Sourcery reviews every merge request on the projects you connect. Set it up in a few minutes: log in with GitLab, pick your projects, and open a merge request to read your first review. ## Before you start [Section titled “Before you start”](#before-you-start) Required permissions Sourcery installs a webhook on each project it reviews, which needs the Maintainer or Owner role. Make sure you have Maintainer or Owner on the group, or on each project you want reviewed. ## Install Sourcery [Section titled “Install Sourcery”](#install-sourcery) 1. Log in with GitLab Go to the [Sourcery dashboard](https://app.sourcery.ai/) and click **Log in with GitLab**. The first time you log in, Sourcery sends you straight to the namespace picker. 2. Authorize Sourcery on GitLab.com GitLab asks you to authorize Sourcery. Sourcery requests the `api`, `openid`, and `email` scopes in a single OAuth consent. The `api` scope lets Sourcery read your projects and post reviews; `openid` and `email` identify you when you sign in. Approve the prompt to continue. 3. Pick a namespace On the **Where do you want to install Sourcery?** screen, choose the group or namespace you want reviews on; if you belong to several, start with one and connect the rest later. The list shows the namespaces where you have admin access. If a namespace already has Sourcery, click **Configure** instead. 4. Choose projects and install Sourcery lists every project in the namespace, selected by default. Choose the ones you want reviewed, then click **Install**. That’s it for setup: Sourcery adds the `@sourcery-ai` bot user and a webhook to each selected project, so it sees merge requests and comments as they happen. Already using Sourcery? To connect another GitLab group later, click your account name at the top of the sidebar to open the account switcher, then click **Connect GitLab organization**. ## Your first review [Section titled “Your first review”](#your-first-review) Open a merge request on any connected project and Sourcery reviews it. You’ll see: * A summary in the merge request description covering the change’s purpose and risk. * A reviewer’s guide comment with the code paths that changed and how to verify them. * Inline comments on the specific lines they’re about. Push more commits and Sourcery reviews the new changes. To talk to it, comment `@sourcery-ai` on the merge request. ## What’s next [Section titled “What’s next”](#whats-next) [Anatomy of a review ](/reviews/anatomy-of-a-review/)Walk through one Sourcery review from top to bottom. [Commands ](/reviews/commands/)Talk to Sourcery from merge request comments using @sourcery-ai. [Configure code reviews ](/reviews/configure/)Adjust Sourcery's review behavior per project or per namespace. # Quickstart on self-hosted GitLab > Connect a self-hosted GitLab instance to Sourcery, pick the projects to review, and read your first merge request review. Sourcery reviews every merge request on the projects you connect. Connect your self-hosted GitLab instance one of two ways: an automatic path that uses an admin token, or a manual one where you create the OAuth application and review user yourself. ## Before you start [Section titled “Before you start”](#before-you-start) Required permissions You connect a self-hosted GitLab instance as an admin. The automatic path needs an admin personal access token with the `api` scope. The manual path needs admin access to create an instance-wide OAuth application and a review user. Allow Sourcery’s static egress IPs so Sourcery’s API can reach the self-hosted instance. See [Network requirements](/trust/network-requirements/) for the addresses to allowlist. ## Install Sourcery [Section titled “Install Sourcery”](#install-sourcery) 1. Enter your GitLab URL Go to [`/login/gitlab-self-hosted`](https://app.sourcery.ai/login/gitlab-self-hosted), enter your **GitLab URL** (for example, `https://gitlab.example.com`), and click **Login**. If the instance isn’t connected yet, Sourcery shows the setup view with **Automatic** and **Manual** tabs. 2. Set up the connection The automatic path is the easier one: hand Sourcery an admin token and it does the setup for you. Pick manual if your policy requires you to create the OAuth application and review user yourself. * Automatic On the **Automatic** tab, paste an admin personal access token with the `api` scope, then click **Setup**. Sourcery uses the token to create the `sourcery-ai` review user and an instance-wide OAuth application on your GitLab. You can delete the admin token afterward. * Manual On the **Manual** tab, first create an instance-wide OAuth application at `Admin > Applications` named `sourcery-ai`, with the redirect URIs `https://auth.sourcery.ai/login/callback` and `https://sourcery.eu.auth0.com/login/callback`, and the scopes `api`, `openid`, and `email`. Then create a `sourcery-ai` review user and a personal access token with the `api` scope. Paste the OAuth application ID and secret and the review user’s token into the form, then click **Setup**. 3. Choose projects Sourcery signs you in and the account dashboard opens. Enable Sourcery on each project you want reviewed. That’s it for setup: Sourcery installs a webhook on each selected project, so it sees merge requests and comments as they happen. ## Your first review [Section titled “Your first review”](#your-first-review) Open a merge request on any connected project and Sourcery reviews it. You’ll see: * A summary in the merge request description covering the change’s purpose and risk. * A reviewer’s guide comment with the code paths that changed and how to verify them. * Inline comments on the specific lines they’re about. Push more commits and Sourcery reviews the new changes. To talk to it, comment `@sourcery-ai` on the merge request. Billing is handled per seat. See [Billing](/admin/billing/) for how to manage it. ## What’s next [Section titled “What’s next”](#whats-next) [Network requirements ](/trust/network-requirements/)Egress IPs and connectivity rules for connecting Sourcery to your GitLab instance. [Anatomy of a review ](/reviews/anatomy-of-a-review/)Walk through one Sourcery review from top to bottom. [Configure code reviews ](/reviews/configure/)Adjust Sourcery's review behavior per project or per namespace. # Run a pilot > Set Sourcery up on your repositories, let it run for two weeks, then read the results from the Analytics and Security Reports dashboards. We want Sourcery to earn its place on your team, and a pilot is how you find out if it does. Spend two weeks running it on your real pull requests. You’ll see whether it catches bugs and security issues your team would have missed, and whether code review gets faster. ## Ask for help if you want it [Section titled “Ask for help if you want it”](#ask-for-help-if-you-want-it) In-app chat is on every dashboard page, and during a pilot it’s the fastest way to reach us. If a finding looks wrong or you can’t find a setting, ask there. You can also email . ## Set up in the first few days [Section titled “Set up in the first few days”](#set-up-in-the-first-few-days) ### Turn on reviews and scans [Section titled “Turn on reviews and scans”](#turn-on-reviews-and-scans) Pick two to five repositories that get plenty of pull requests, and include one codebase the team knows well. Aim for at least 50 reviewed pull requests by the end of the pilot. The final numbers need that much volume to mean anything. If the repositories you picked won’t get there, add a few more. Walk through the [quickstart for your platform](/start/) to connect them, and Sourcery reviews every new pull request from then on. Start a security scan too. Open [**Repositories**](https://app.sourcery.ai/dashboard/security/repositories) and click **Start Scan**. ### Bring in the team [Section titled “Bring in the team”](#bring-in-the-team) Everyone in your organization has access during the trial. Bring in the engineers who open and review pull requests in the pilot repositories, plus a security lead for the scan findings. Ask everyone to react to Sourcery’s comments with a thumbs up or down. Reactions [teach Sourcery](/reviews/anatomy-of-a-review/#reacting-to-a-comment) which comments the team wants, and they feed the comment-quality number you’ll read later. ### Configure reviews to fit your team [Section titled “Configure reviews to fit your team”](#configure-reviews-to-fit-your-team) Sourcery reviews sensibly out of the box, and you can shape it further. Open [**Review Settings**](https://app.sourcery.ai/dashboard/review-settings) to choose what it posts and which pull requests it reviews, and set them up how you like. Recurring conventions can go in as [review rules](/reviews/review-rules/). Do this at the start, so the results reflect how your team actually runs Sourcery. ## Read your stats at the end [Section titled “Read your stats at the end”](#read-your-stats-at-the-end) By then the dashboards have enough to read. Two weeks is a short window, though, so the numbers are still forming. Read them for direction and rough size. ### Code reviews [Section titled “Code reviews”](#code-reviews) The [**Analytics**](https://app.sourcery.ai/dashboard/analytics) page holds the code-review numbers. It’s available for GitHub; if you’re piloting on GitLab, read the code-review results from the reviews on the merge requests themselves. **Sourcery Acceptance Rate**, on the Overview tab, is the share of Sourcery’s comments that developers acted on, meaning the comment led to a code change. It shows whether the comments are worth acting on, and it’s the number you’d otherwise have counted by hand. **Sourcery Comment Quality**, on the Code Reviews tab, is the thumbs-up ratio from the reactions the team collected. **Median Cycle Time** and **Time to First Review**, both on the Overview tab, show whether review got faster. Each shows the change against the previous period. ### Security findings [Section titled “Security findings”](#security-findings) The [**Security Reports**](https://app.sourcery.ai/dashboard/security/reports) page covers the scan, on GitHub and GitLab alike. The Risk Impact tab shows Open Issues and the breakdown by severity, from Critical down to Low. The Trends tab shows New against Resolved, the findings that came in during the pilot and the ones that got fixed. The report counts findings, but it can’t tell you which ones are false positives. Take a sample from the codebase you know best and judge for yourself which findings are real. ## Check in with your teammates [Section titled “Check in with your teammates”](#check-in-with-your-teammates) The dashboards don’t tell you everything. Near the end of the pilot, ask the engineers whether the comments were worth reading and whether the reactions habit stuck. If the team wants to keep Sourcery, that tells you as much as any number. ## Optional extras [Section titled “Optional extras”](#optional-extras) If Jira is your tracker, add [Jira autocreate](/admin/connect-jira/) to the pilot. If your engineers fix issues with coding agents, try [agent fix prompts](/security/agent-fix-prompts/). ## What it costs [Section titled “What it costs”](#what-it-costs) Costs shouldn’t be a surprise at the end of a pilot. Pricing is public on the [plans page](/admin/plans/). Note The trial runs for 14 days with no credit card, and everyone in your organization gets access. If two weeks turns out to be too short for a fair sample, tell us in the chat and we’ll work it out. ## What’s next [Section titled “What’s next”](#whats-next) [Pick a quickstart ](/start/)Connect Sourcery to GitHub.com, GitLab.com, GHES, or self-hosted GitLab. [Code retention ](/trust/code-retention/)How Sourcery handles your code, the LLM providers, and certifications. [Plans ](/admin/plans/)Which features need which plan, and what the tiers cost. # Trust and compliance > Sourcery's data handling posture and where to find every answer your security and procurement teams need. Sourcery does not retain your code after a review. None of your code is used by OpenAI, Anthropic, or Sourcery to train any model. Sourcery is SOC 2 Type 2 certified. This section answers the questions a security or procurement review asks. ## Data handling [Section titled “Data handling”](#data-handling) [Code retention ](/trust/code-retention/)What Sourcery and the LLM providers keep, and what is never used for training. [LLM providers and data handling ](/trust/llm-providers/)Which providers Sourcery uses and what they do with your data. ## Compliance [Section titled “Compliance”](#compliance) [Compliance and certifications ](/trust/compliance/)SOC 2, the trust center, and the data processing agreement. [Network requirements ](/trust/network-requirements/)Sourcery's static egress IPs and connectivity rules. For the permissions Sourcery requests on your Git host, see the [GitHub quickstart](/start/quickstart-github/) and [GitLab quickstart](/start/quickstart-gitlab/). ## Deployment and model choice [Section titled “Deployment and model choice”](#deployment-and-model-choice) [Bring your own LLM ](/trust/bring-your-own-llm/)Use your own model deployment, available on the Team plan. [Self-hosted Sourcery ](/trust/self-hosted-sourcery/)Run Sourcery in your own environment, arranged through sales. To request the SOC 2 report or a data processing agreement, visit [the Sourcery trust center](https://trust.mycroft.io/sourcery). # Bring your own LLM > Use your own model deployment with Sourcery, available on the Team plan. In short Available on the Team plan. Sourcery sets it up for you on request. Bring your own LLM lets Sourcery send your code to a model deployment you control. Teams use it when they already have an enterprise LLM contract, such as Anthropic or Azure OpenAI, and want reviews to run through it. There is no dashboard setting for it today. To start, [contact sales](mailto:sales@sourcery.ai). For how Sourcery’s default providers handle your code, see [LLM providers and data handling](/trust/llm-providers/). # Sourcery does not retain your code > How Sourcery handles your code during a review, what the LLM providers retain, and what is never used for training. In short Sourcery does not retain your code after a review, and none of your code is used to train any model. ## Sourcery reads your diff and related context [Section titled “Sourcery reads your diff and related context”](#sourcery-reads-your-diff-and-related-context) To review a pull request, Sourcery reads the diff of the changed code. For some changes it needs broader context and supplements the diff with related code from your repository, such as the other places a function you changed is used. To produce the review, Sourcery sends the relevant sections of the diff and that context to OpenAI and Anthropic, the [LLM providers it works with](/trust/llm-providers/). ## Sourcery retains none of your code [Section titled “Sourcery retains none of your code”](#sourcery-retains-none-of-your-code) Sourcery does not retain any of your code. The provider retention windows are: * Anthropic retains nothing. Sourcery has a zero-day retention agreement with Anthropic, so they hold none of your code or data at any time. * OpenAI retains data for no more than 30 days. ## Your code never trains a model [Section titled “Your code never trains a model”](#your-code-never-trains-a-model) None of your code is used by OpenAI, Anthropic, or Sourcery to train any model. ## Sourcery is SOC 2 Type 2 certified [Section titled “Sourcery is SOC 2 Type 2 certified”](#sourcery-is-soc-2-type-2-certified) Sourcery is SOC 2 Type 2 certified. To request the SOC 2 report, visit [the Sourcery trust center](https://trust.mycroft.io/sourcery). For the full list of [compliance and certifications](/trust/compliance/), see the compliance page. For the full data processing terms, see [the data processing page](https://www.sourcery.ai/data-processing). ## You can request a stricter setup [Section titled “You can request a stricter setup”](#you-can-request-a-stricter-setup) If you need a zero-retention setup across all providers, want to use your own model deployments, or need a self-hosted version of Sourcery, [contact us](mailto:sales@sourcery.ai). # Compliance and certifications > Sourcery's SOC 2 status, the trust center, and where to find the data processing agreement. In short Sourcery is SOC 2 Type 2 certified, and the report is available on request. ## SOC 2 [Section titled “SOC 2”](#soc-2) To request the report, visit [the Sourcery trust center](https://trust.mycroft.io/sourcery), where you can also review the current security posture. ## Data processing [Section titled “Data processing”](#data-processing) Sourcery’s data processing agreement is published at [sourcery.ai/data-processing](https://www.sourcery.ai/data-processing). It sets out how Sourcery processes your data. The LLM providers Sourcery relies on are listed under [LLM providers and data handling](/trust/llm-providers/). For how your code is handled and retained, see [code retention](/trust/code-retention/). # LLM providers and data handling > Which LLM providers Sourcery uses, what each retains, and how to use your own model. In short Sourcery sends the relevant diff and code context to OpenAI and Anthropic. None of your code trains OpenAI’s, Anthropic’s, or Sourcery’s models. Anthropic retains nothing, and OpenAI retains data for no more than 30 days. ## Providers [Section titled “Providers”](#providers) To review your code, Sourcery sends the relevant sections of the diff and the surrounding code context to two LLM providers, OpenAI and Anthropic. ## Retention and training [Section titled “Retention and training”](#retention-and-training) None of your code is used by OpenAI, Anthropic, or Sourcery to train any model. The retention windows are: * Anthropic retains nothing, under a zero-day retention agreement. * OpenAI retains data for no more than 30 days. For how Sourcery itself handles your code, see [code retention](/trust/code-retention/). ## Choosing your own provider [Section titled “Choosing your own provider”](#choosing-your-own-provider) You cannot switch providers from the dashboard today. If you need Sourcery to use your own model deployment, such as an existing enterprise contract with Anthropic or Azure OpenAI, [bring your own LLM](/trust/bring-your-own-llm/) is available on the Team plan. # Network requirements > Sourcery's static egress IP addresses and the inbound and outbound traffic needed to connect a Git host. Sourcery reaches your Git host from two static IP addresses. If your host or network restricts traffic by IP, allow these addresses. ## Egress IP addresses [Section titled “Egress IP addresses”](#egress-ip-addresses) Sourcery makes outbound requests from: * `34.142.124.129` * `34.147.223.148` ## Traffic model [Section titled “Traffic model”](#traffic-model) A connection has two directions: * Inbound to Sourcery – your Git host sends webhooks to Sourcery over HTTPS when pull requests and comments change. * Outbound from Sourcery – Sourcery calls your Git host’s API to read diffs and post reviews. These calls come from the egress IPs above. For a self-hosted Git server (GitHub Enterprise Server or self-hosted GitLab), the outbound direction is the one that needs firewall changes: your server must accept API traffic from Sourcery’s egress IPs. See the [GHES quickstart](/start/quickstart-ghes/) and the [self-hosted GitLab quickstart](/start/quickstart-gitlab-self-hosted/). If you also run [self-hosted Sourcery](/trust/self-hosted-sourcery/), these egress rules apply to that deployment too. ## IP-restricted GitHub.com [Section titled “IP-restricted GitHub.com”](#ip-restricted-githubcom) GitHub organizations can restrict access to allowlisted IP addresses. If yours does, add Sourcery’s egress IPs to the organization’s IP allowlist so Sourcery can reach your repositories. This is configured in GitHub’s organization settings. Note These addresses are stable, but confirm them against this page before you hard-code them into a firewall rule. # Self-hosted Sourcery > Run Sourcery in your own environment, arranged through sales. In short Sourcery can run entirely inside your own infrastructure, under an [Enterprise agreement](/admin/plans/#enterprise). If your security requirements rule out a hosted service, self-hosting keeps every part of Sourcery inside your infrastructure. To explore it, [contact sales](mailto:sales@sourcery.ai) to discuss the architecture and a quote. If you also need to keep model traffic inside your own contracts, see [bring your own LLM](/trust/bring-your-own-llm/). For the egress and firewall rules a self-hosted deployment needs to reach your Git host, see [network requirements](/trust/network-requirements/).