├── .codecov.yml ├── .cruft.json ├── .deepsource.toml ├── .dockerignore ├── .editorconfig ├── .github ├── FUNDING.yml ├── dependabot.yml ├── labels.yml ├── release-drafter.yml └── workflows │ ├── integration.yml │ ├── labeler.yml │ ├── lint.yml │ ├── release-dev.yml │ ├── release-drafter.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .pre-commit-hooks.yaml ├── .pymon ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── art ├── isort_loves_black.png ├── logo.png ├── logo.xcf ├── logo_5.png ├── logo_large.png ├── logo_large.xcf └── stylesheets │ └── extra.css ├── docs ├── configuration │ ├── action_comments.md │ ├── add_or_remove_imports.md │ ├── black_compatibility.md │ ├── config_files.md │ ├── custom_sections_and_ordering.md │ ├── git_hook.md │ ├── github_action.md │ ├── multi_line_output_modes.md │ ├── options.md │ ├── pre-commit.md │ ├── profiles.md │ └── setuptools_integration.md ├── contributing │ ├── 1.-contributing-guide.md │ ├── 2.-coding-standard.md │ ├── 3.-code-of-conduct.md │ └── 4.-acknowledgements.md ├── howto │ └── shared_profiles.md ├── major_releases │ ├── introducing_isort_5.md │ └── release_policy.md ├── quick_start │ ├── 0.-try.md │ ├── 1.-install.md │ ├── 2.-cli.md │ ├── 3.-api.md │ ├── interactive.css │ ├── interactive.js │ ├── isort-5.0.0-py3-none-any.whl │ └── isort-5.0.1-py3-none-any.whl ├── upgrade_guides │ └── 5.0.0.md └── warning_and_error_codes │ └── W0500.md ├── example.gif ├── example_isort_formatting_plugin ├── example_isort_formatting_plugin.py ├── pyproject.toml └── uv.lock ├── example_isort_sorting_plugin ├── example_isort_sorting_plugin.py ├── pyproject.toml └── uv.lock ├── example_shared_isort_profile ├── example_shared_isort_profile.py ├── pyproject.toml └── uv.lock ├── isort ├── __init__.py ├── __main__.py ├── _vendored │ └── tomli │ │ ├── LICENSE │ │ ├── __init__.py │ │ ├── _parser.py │ │ ├── _re.py │ │ └── py.typed ├── _version.py ├── api.py ├── comments.py ├── core.py ├── deprecated │ ├── __init__.py │ └── finders.py ├── exceptions.py ├── files.py ├── format.py ├── hooks.py ├── identify.py ├── io.py ├── literal.py ├── logo.py ├── main.py ├── output.py ├── parse.py ├── place.py ├── profiles.py ├── py.typed ├── pylama_isort.py ├── sections.py ├── settings.py ├── setuptools_commands.py ├── sorting.py ├── stdlibs │ ├── __init__.py │ ├── all.py │ ├── py2.py │ ├── py27.py │ ├── py3.py │ ├── py310.py │ ├── py311.py │ ├── py312.py │ ├── py313.py │ ├── py36.py │ ├── py37.py │ ├── py38.py │ └── py39.py ├── utils.py ├── wrap.py └── wrap_modes.py ├── logo.png ├── mkdocs.yml ├── pyproject.toml ├── rtd └── index.md ├── scripts ├── build_config_option_docs.py ├── build_profile_docs.py ├── check_acknowledgments.py ├── clean.sh ├── docker.sh ├── done.sh ├── mkstdlibs.py ├── test.sh └── test_integration.sh ├── tests ├── __init__.py ├── benchmark │ └── test_api.py ├── integration │ ├── test_hypothesmith.py │ ├── test_literal.py │ ├── test_projects_using_isort.py │ ├── test_setting_combinations.py │ └── test_ticketed_features.py └── unit │ ├── __init__.py │ ├── conftest.py │ ├── example_crlf_file.py │ ├── example_projects │ └── namespaces │ │ ├── almost-implicit │ │ ├── .isort.cfg │ │ └── root │ │ │ ├── nested │ │ │ ├── __init__.py │ │ │ └── x.py │ │ │ └── y.py │ │ ├── implicit │ │ ├── .isort.cfg │ │ └── root │ │ │ └── nested │ │ │ ├── __init__.py │ │ │ └── x.py │ │ ├── none │ │ ├── .isort.cfg │ │ └── root │ │ │ ├── __init__.py │ │ │ └── nested │ │ │ └── __init__.py │ │ ├── pkg_resource │ │ ├── .isort.cfg │ │ └── root │ │ │ ├── __init__.py │ │ │ └── nested │ │ │ ├── __init__.py │ │ │ └── x.py │ │ ├── pkgutil │ │ ├── .isort.cfg │ │ └── root │ │ │ ├── __init__.py │ │ │ └── nested │ │ │ ├── __init__.py │ │ │ └── x.py │ │ └── weird_encoding │ │ ├── .isort.cfg │ │ └── root │ │ ├── __init__.py │ │ └── nested │ │ └── __init__.py │ ├── profiles │ ├── __init__.py │ ├── test_attrs.py │ ├── test_black.py │ ├── test_django.py │ ├── test_google.py │ ├── test_hug.py │ ├── test_open_stack.py │ ├── test_plone.py │ ├── test_pycharm.py │ └── test_wemake.py │ ├── test_action_comments.py │ ├── test_api.py │ ├── test_comments.py │ ├── test_deprecated_finders.py │ ├── test_exceptions.py │ ├── test_files.py │ ├── test_format.py │ ├── test_hooks.py │ ├── test_identify.py │ ├── test_importable.py │ ├── test_io.py │ ├── test_isort.py │ ├── test_literal.py │ ├── test_main.py │ ├── test_output.py │ ├── test_parse.py │ ├── test_place.py │ ├── test_pylama_isort.py │ ├── test_regressions.py │ ├── test_settings.py │ ├── test_setuptools_command.py │ ├── test_ticketed_features.py │ ├── test_utils.py │ ├── test_wrap.py │ ├── test_wrap_modes.py │ └── utils.py ├── tox.ini └── uv.lock /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: yes 4 | patch: yes 5 | changes: yes 6 | 7 | comment: 8 | layout: "header, diff" 9 | behavior: default 10 | require_changes: no 11 | -------------------------------------------------------------------------------- /.cruft.json: -------------------------------------------------------------------------------- 1 | { 2 | "template": "https://github.com/timothycrosley/cookiecutter-python/", 3 | "commit": "71391fd9999067ef4b38aa05e7116087fac431f8", 4 | "context": { 5 | "cookiecutter": { 6 | "full_name": "Timothy Crosley", 7 | "email": "timothy.crosley@gmail.com", 8 | "github_username": "pycqa", 9 | "project_name": "isort", 10 | "description": "A Python utility / library to sort Python imports.", 11 | "version": "4.3.21", 12 | "_template": "https://github.com/timothycrosley/cookiecutter-python/" 13 | } 14 | }, 15 | "directory": "", 16 | "checkout": null 17 | } 18 | -------------------------------------------------------------------------------- /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | test_patterns = ["tests/**"] 4 | exclude_patterns = [ 5 | "tests/**", 6 | "scripts/**", 7 | "isort/_vendored/**", 8 | ] 9 | 10 | 11 | [[analyzers]] 12 | name = "python" 13 | enabled = true 14 | 15 | [analyzers.meta] 16 | runtime_version = "3.x.x" 17 | max_line_length = 100 18 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # project build medadata that can't be used in docker 2 | Dockerfile 3 | .appveyor.yml 4 | .dockerignore 5 | .pre-commit-hooks.yaml 6 | .travis.yml 7 | .git* 8 | 9 | # documentation not needed 10 | CHANGELOG.md 11 | LICENSE 12 | MANIFEST.in 13 | .undertake 14 | example.gif 15 | logo.png 16 | art/ 17 | docs/ 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.py] 4 | max_line_length = 100 5 | indent_style = space 6 | indent_size = 4 7 | known_first_party = isort 8 | known_third_party = kate 9 | ignore_frosted_errors = E103 10 | skip = build,.tox,venv 11 | balanced_wrapping = true 12 | 13 | [*.{rst,ini}] 14 | indent_style = space 15 | indent_size = 4 16 | 17 | [*.yml] 18 | indent_style = space 19 | indent_size = 2 20 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: "timothycrosley" 2 | tidelift: "pypi/isort" 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 2 | version: 2 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "monthly" 8 | groups: 9 | github-actions: 10 | patterns: 11 | - "*" 12 | -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Labels names are important as they are used by Release Drafter to decide 3 | # regarding where to record them in changelog or if to skip them. 4 | # 5 | # The repository labels will be automatically configured using this file and 6 | # the GitHub Action https://github.com/marketplace/actions/github-labeler. 7 | - name: breaking 8 | description: Breaking Changes 9 | color: "bfd4f2" 10 | - name: bug 11 | description: Something isn't working 12 | color: "d73a4a" 13 | - name: build 14 | description: Build System and Dependencies 15 | color: "bfdadc" 16 | - name: ci 17 | description: Continuous Integration 18 | color: "4a97d6" 19 | - name: dependencies 20 | description: Pull requests that update a dependency file 21 | color: "0366d6" 22 | - name: documentation 23 | description: Improvements or additions to documentation 24 | color: "0075ca" 25 | - name: duplicate 26 | description: This issue or pull request already exists 27 | color: "cfd3d7" 28 | - name: enhancement 29 | description: New feature or request 30 | color: "a2eeef" 31 | - name: github_actions 32 | description: Pull requests that update Github_actions code 33 | color: "000000" 34 | - name: good first issue 35 | description: Good for newcomers 36 | color: "7057ff" 37 | - name: help wanted 38 | description: Extra attention is needed 39 | color: "008672" 40 | - name: invalid 41 | description: This doesn't seem right 42 | color: "e4e669" 43 | - name: performance 44 | description: Performance 45 | color: "016175" 46 | - name: python 47 | description: Pull requests that update Python code 48 | color: "2b67c6" 49 | - name: question 50 | description: Further information is requested 51 | color: "d876e3" 52 | - name: refactoring 53 | description: Refactoring 54 | color: "ef67c4" 55 | - name: removal 56 | description: Removals and deprecations 57 | color: "9ae7ea" 58 | - name: style 59 | description: Style 60 | color: "c120e5" 61 | - name: testing 62 | description: Testing 63 | color: "b1fc6f" 64 | - name: wontfix 65 | description: This will not be worked on 66 | color: "ffffff" 67 | - name: skip-changelog 68 | description: This will not be added to release notes 69 | color: "dddddd" 70 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | template: | 3 | ## Changes 4 | 5 | $CHANGES 6 | 7 | categories: 8 | - title: ":boom: Breaking Changes" 9 | label: "breaking" 10 | - title: ":rocket: Features" 11 | label: "enhancement" 12 | - title: ":fire: Removals and Deprecations" 13 | label: "removal" 14 | - title: ":beetle: Fixes" 15 | label: "bug" 16 | - title: ":raising_hand: Help wanted" 17 | label: "help wanted" 18 | - title: ":racehorse: Performance" 19 | label: "performance" 20 | - title: ":rotating_light: Testing" 21 | label: "testing" 22 | - title: ":construction_worker: Continuous Integration" 23 | label: "ci" 24 | - title: ":books: Documentation" 25 | label: "documentation" 26 | - title: ":hammer: Refactoring" 27 | label: "refactoring" 28 | - title: ":lipstick: Style" 29 | label: "style" 30 | - title: ":package: Dependencies" 31 | labels: 32 | - "dependencies" 33 | - "build" 34 | 35 | exclude-labels: 36 | - "skip-changelog" 37 | -------------------------------------------------------------------------------- /.github/workflows/integration.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Integration 3 | 4 | "on": 5 | push: 6 | pull_request: 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | python-version: ["3.12"] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v5 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | 26 | - name: Install UV 27 | uses: astral-sh/setup-uv@v6 28 | with: 29 | enable-cache: true 30 | version: ">=0.6.0" 31 | 32 | - name: Install dependencies 33 | run: uv sync --all-extras --frozen 34 | 35 | - name: Test integration 36 | run: ./scripts/test_integration.sh 37 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Labeler 3 | 4 | "on": 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | labeler: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out the repository 14 | uses: actions/checkout@v4 15 | 16 | - name: Run Labeler 17 | uses: crazy-max/ghaction-github-labeler@v5 18 | with: 19 | skip-delete: true 20 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Lint 3 | 4 | "on": 5 | push: 6 | pull_request: 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | python-version: ["3.12"] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v5 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | 26 | - name: Install UV 27 | uses: astral-sh/setup-uv@v6 28 | with: 29 | enable-cache: true 30 | version: ">=0.6.0" 31 | 32 | - name: Install dependencies 33 | run: uv sync --all-extras --frozen 34 | 35 | - name: Lint 36 | run: uv run --with tox-uv tox -e lint 37 | -------------------------------------------------------------------------------- /.github/workflows/release-dev.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release Dev 3 | 4 | "on": 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | release: 11 | if: github.repository_owner == 'PyCQA' 12 | name: Release 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check out the repository 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 2 19 | 20 | - name: Set up Python 21 | uses: actions/setup-python@v5 22 | with: 23 | python-version: "3.13" 24 | 25 | - name: Install UV 26 | uses: astral-sh/setup-uv@v6 27 | with: 28 | version: ">=0.6.0" 29 | 30 | - name: Build package 31 | run: | 32 | uv build 33 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release Drafter 3 | 4 | "on": 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | draft_release: 11 | if: github.repository_owner == 'PyCQA' 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: release-drafter/release-drafter@v6 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 3 | 4 | "on": 5 | push: 6 | tags: 7 | - "[0-9]+.[0-9]+.[0-9]+" 8 | 9 | jobs: 10 | release: 11 | if: github.repository_owner == 'PyCQA' 12 | name: Release 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check out the repository 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 2 19 | 20 | - name: Set Tag env 21 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 22 | 23 | - name: Set up Python 24 | uses: actions/setup-python@v5 25 | with: 26 | python-version: "3.13" 27 | 28 | - name: Install UV 29 | uses: astral-sh/setup-uv@v6 30 | with: 31 | version: ">=0.6.0" 32 | 33 | - name: Install dependencies 34 | run: | 35 | uv sync --all-extras --frozen 36 | 37 | - name: Check if there is a parent commit 38 | id: check-parent-commit 39 | run: | 40 | echo "::set-output name=sha::$(git rev-parse --verify --quiet HEAD^)" 41 | 42 | - name: Build package 43 | run: | 44 | uv build 45 | 46 | - name: Publish package on PyPI 47 | uses: pypa/gh-action-pypi-publish@release/v1 48 | with: 49 | password: ${{ secrets.PYPI_API_TOKEN }} 50 | 51 | - name: Publish the release notes 52 | uses: release-drafter/release-drafter@v6 53 | with: 54 | publish: ${{ steps.check-version.outputs.tag != '' }} 55 | tag: ${{ steps.check-version.outputs.tag }} 56 | env: 57 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 58 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test 3 | 4 | on: 5 | push: 6 | pull_request: 7 | 8 | permissions: 9 | contents: read 10 | 11 | env: 12 | FORCE_COLOR: 1 13 | 14 | jobs: 15 | build: 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 21 | os: [ubuntu-latest, macos-latest, windows-latest] 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: Set up Python ${{ matrix.python-version }} 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: ${{ matrix.python-version }} 30 | 31 | - name: Install UV 32 | uses: astral-sh/setup-uv@v6 33 | with: 34 | enable-cache: true 35 | version: ">=0.6.0" 36 | 37 | - name: Install dependencies 38 | run: uv sync --all-extras --frozen 39 | 40 | - name: Test 41 | run: uv run --with tox-uv tox -e py,coverage_report-ci 42 | 43 | - name: Report Coverage 44 | if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11' 45 | uses: codecov/codecov-action@v5 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | __pycache__ 3 | .DS_Store 4 | # C extensions 5 | *.so 6 | 7 | # Packages 8 | *.egg 9 | *.egg-info 10 | .eggs 11 | build 12 | eggs 13 | parts 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | MANIFEST 21 | .eggs 22 | 23 | # Installer logs 24 | pip-log.txt 25 | npm-debug.log 26 | 27 | # Unit test / coverage reports 28 | .coverage 29 | .coverage.* 30 | coverage.xml 31 | .pytest_cache 32 | .tox 33 | nosetests.xml 34 | htmlcov 35 | .cache 36 | .pytest_cache/ 37 | .hypothesis/ 38 | 39 | # Translations 40 | *.mo 41 | 42 | # Mr Developer 43 | .mr.developer.cfg 44 | .project 45 | .pydevproject 46 | 47 | # SQLite 48 | test_exp_framework 49 | 50 | # npm 51 | node_modules/ 52 | 53 | # dolphin 54 | .directory 55 | libpeerconnection.log 56 | 57 | # setuptools 58 | dist 59 | 60 | # IDE Files 61 | atlassian-ide-plugin.xml 62 | .idea/ 63 | *.swp 64 | *.kate-swp 65 | .ropeproject/ 66 | .vscode 67 | 68 | # pip 69 | pip-selfcheck.json 70 | 71 | # Python3 Venv Files 72 | .python-version 73 | .venv/ 74 | pyvenv.cfg 75 | 76 | # mypy 77 | .mypy_cache 78 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pycqa/isort 3 | rev: 5.13.2 4 | hooks: 5 | - id: isort 6 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | - id: isort 2 | name: isort 3 | entry: isort 4 | stages: [pre-commit, pre-merge-commit, pre-push, manual] 5 | require_serial: true 6 | language: python 7 | types_or: [cython, pyi, python] 8 | args: ['--filter-files'] 9 | minimum_pre_commit_version: '3.2.0' 10 | -------------------------------------------------------------------------------- /.pymon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/.pymon -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.13 2 | 3 | WORKDIR /isort 4 | COPY pyproject.toml uv.lock /isort/ 5 | 6 | # Install uv 7 | COPY --from=ghcr.io/astral-sh/uv:0.6.0 /uv /uvx /bin/ 8 | 9 | # Setup as minimal a stub project as possible, simply to allow caching base dependencies 10 | # between builds. 11 | # 12 | # If error is encountered in these steps, can safely be removed locally. 13 | RUN mkdir -p /isort/isort 14 | RUN mkdir -p /isort/tests 15 | RUN touch /isort/isort/__init__.py 16 | RUN touch /isort/tests/__init__.py 17 | RUN touch /isort/README.md 18 | COPY . /isort 19 | RUN SETUPTOOLS_SCM_PRETEND_VERSION=0.0.0 uv sync --all-extras --frozen 20 | 21 | # Install latest code for actual project 22 | RUN rm -rf /isort 23 | 24 | # Run full test suite 25 | CMD /isort/scripts/test.sh 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Timothy Edmund Crosley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /art/isort_loves_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/art/isort_loves_black.png -------------------------------------------------------------------------------- /art/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/art/logo.png -------------------------------------------------------------------------------- /art/logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/art/logo.xcf -------------------------------------------------------------------------------- /art/logo_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/art/logo_5.png -------------------------------------------------------------------------------- /art/logo_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/art/logo_large.png -------------------------------------------------------------------------------- /art/logo_large.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/art/logo_large.xcf -------------------------------------------------------------------------------- /art/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | [data-md-color-scheme="isort"] { 2 | --md-primary-fg-color: #EF8336; 3 | --md-primary-fg-color--light: #1674B1; 4 | --md-primary-fg-color--dark: #1674b1; 5 | } 6 | -------------------------------------------------------------------------------- /docs/configuration/action_comments.md: -------------------------------------------------------------------------------- 1 | # Action Comments 2 | 3 | The most basic way to configure the flow of isort within a single file is action comments. These comments are picked up and interpreted by the isort parser during parsing. 4 | 5 | 6 | ## isort: skip_file 7 | 8 | Tells isort to skip the entire file. 9 | 10 | Example: 11 | 12 | ```python 13 | # !/bin/python3 14 | # isort: skip_file 15 | import os 16 | import sys 17 | 18 | ... 19 | ``` 20 | 21 | !!! warning 22 | This should be placed as high in the file as reasonably possible. 23 | Since isort uses a streaming architecture, it may have already completed some work before it reaches the comment. Usually, this is okay - but can be confusing if --diff or any interactive options are used from the command line. 24 | 25 | 26 | ## isort: skip 27 | 28 | If placed on the same line as (or within the continuation of a) an import statement, isort will not sort this import. 29 | More specifically, it prevents the import statement from being recognized by isort as an import. In consequence, this line will be treated as code and be pushed down to below the import section of the file. 30 | 31 | Example: 32 | 33 | ```python 34 | import b 35 | import a # isort: skip <- this will now stay below b 36 | ``` 37 | !!! note 38 | It is recommended to where possible use `# isort: off` and `# isort: on` or `# isort: split` instead as the behavior is more explicit and predictable. 39 | 40 | ## isort: off 41 | 42 | Turns isort parsing off. Every line after an `# isort: off` statement will be passed along unchanged until an `# isort: on` comment or the end of the file. 43 | 44 | Example: 45 | 46 | ```python 47 | import e 48 | import f 49 | 50 | # isort: off 51 | 52 | import b 53 | import a 54 | ``` 55 | 56 | ## isort: on 57 | 58 | Turns isort parsing back on. This only makes sense if an `# isort: off` comment exists higher in the file! This allows you to have blocks of unsorted imports, around otherwise sorted ones. 59 | 60 | Example: 61 | 62 | ```python 63 | 64 | import e 65 | import f 66 | 67 | # isort: off 68 | 69 | import b 70 | import a 71 | 72 | # isort: on 73 | 74 | import c 75 | import d 76 | 77 | ``` 78 | 79 | ## isort: split 80 | 81 | Tells isort the current sort section is finished, and all future imports belong to a new sort grouping. 82 | 83 | Example: 84 | 85 | ```python 86 | 87 | import e 88 | import f 89 | 90 | # isort: split 91 | 92 | import a 93 | import b 94 | import c 95 | import d 96 | 97 | ``` 98 | 99 | You can also use it inline to keep an import from having imports above or below it swap position: 100 | 101 | ```python 102 | import c 103 | import b # isort: split 104 | import a 105 | ``` 106 | 107 | !!! tip 108 | isort split is exactly the same as placing an `# isort: on` immediately below an `# isort: off` 109 | 110 | 111 | ## isort: dont-add-imports 112 | 113 | Tells isort to not automatically add imports to this file, even if --add-imports is set. 114 | 115 | ## isort: dont-add-import: [IMPORT_LINE] 116 | 117 | Tells isort to not automatically add a particular import, even if --add-imports says to add it. 118 | -------------------------------------------------------------------------------- /docs/configuration/add_or_remove_imports.md: -------------------------------------------------------------------------------- 1 | 2 | ## Adding an import to multiple files 3 | isort makes it easy to add an import statement across multiple files, 4 | while being assured it's correctly placed. 5 | 6 | To add an import to all files: 7 | 8 | ```bash 9 | isort -a "from __future__ import print_function" *.py 10 | ``` 11 | 12 | To add an import only to files that already have imports: 13 | 14 | ```bash 15 | isort -a "from __future__ import print_function" --append-only *.py 16 | ``` 17 | 18 | 19 | ## Removing an import from multiple files 20 | 21 | isort also makes it easy to remove an import from multiple files, 22 | without having to be concerned with how it was originally formatted. 23 | 24 | From the command line: 25 | 26 | ```bash 27 | isort --rm "os.system" *.py 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/configuration/black_compatibility.md: -------------------------------------------------------------------------------- 1 | ![isort loves black](https://raw.githubusercontent.com/pycqa/isort/main/art/isort_loves_black.png) 2 | 3 | # Compatibility with black 4 | 5 | Compatibility with black is very important to the isort project and comes baked in starting with version 5. 6 | All that's required to use isort alongside black is to set the isort profile to "black". 7 | 8 | !!! tip 9 | Beyond the profile, it is common to set [skip_gitignore](https://pycqa.github.io/isort/docs/configuration/options.html#skip-gitignore) (which is not enabled by default for isort as it requires git to be installed) and [line_length](https://pycqa.github.io/isort/docs/configuration/options.html#line-length) as it is common to deviate from black's default of 88. 10 | 11 | 12 | ## Using a config file (such as .isort.cfg) 13 | 14 | For projects that officially use both isort and black, we recommend setting the black profile in a config file at the root of your project's repository. 15 | That way it's independent of how users call isort (pre-commit, CLI, or editor integration) the black profile will automatically be applied. 16 | 17 | For instance, your _pyproject.toml_ file would look something like 18 | 19 | ```ini 20 | [tool.isort] 21 | profile = "black" 22 | ``` 23 | 24 | Read More about supported [config files](https://pycqa.github.io/isort/docs/configuration/config_files.html). 25 | 26 | ## CLI 27 | 28 | To use the profile option when calling isort directly from the commandline simply add the --profile black argument: `isort --profile black`. 29 | 30 | A demo of how this would look like in your _.travis.yml_ 31 | 32 | ```yaml 33 | language: python 34 | python: 35 | - "3.10" 36 | - "3.9" 37 | 38 | install: 39 | - pip install -r requirements-dev.txt 40 | - pip install isort black 41 | - pip install coveralls 42 | script: 43 | - pytest my-package 44 | - isort --profile black my-package 45 | - black --check --diff my-package 46 | after_success: 47 | - coveralls 48 | 49 | ``` 50 | 51 | See [built-in profiles](https://pycqa.github.io/isort/docs/configuration/profiles.html) for more profiles. 52 | 53 | ## Integration with pre-commit 54 | 55 | You can also set the profile directly when integrating isort within pre-commit. 56 | 57 | ```yaml 58 | - repo: https://github.com/pycqa/isort 59 | rev: 5.13.2 60 | hooks: 61 | - id: isort 62 | args: ["--profile", "black", "--filter-files"] 63 | ``` 64 | -------------------------------------------------------------------------------- /docs/configuration/custom_sections_and_ordering.md: -------------------------------------------------------------------------------- 1 | # Custom Sections and Ordering 2 | 3 | isort provides lots of features to enable configuring how it sections imports 4 | and how it sorts imports within those sections. 5 | You can change the section order with `sections` option from the default 6 | of: 7 | 8 | ```ini 9 | FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER 10 | ``` 11 | 12 | to your preference (if defined, omitting a default section may cause errors): 13 | 14 | ```ini 15 | sections=FUTURE,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER 16 | ``` 17 | 18 | You also can define your own sections and their order. 19 | 20 | Example: 21 | 22 | ```ini 23 | known_django=django 24 | known_pandas=pandas,numpy 25 | sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,PANDAS,FIRSTPARTY,LOCALFOLDER 26 | ``` 27 | 28 | would create two new sections with the specified known modules. 29 | 30 | The `no_lines_before` option will prevent the listed sections from being 31 | split from the previous section by an empty line. 32 | 33 | Example: 34 | 35 | ```ini 36 | sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER 37 | no_lines_before=LOCALFOLDER 38 | ``` 39 | 40 | would produce a section with both FIRSTPARTY and LOCALFOLDER modules 41 | combined. 42 | 43 | **IMPORTANT NOTE**: It is very important to know when setting `known` sections that the naming 44 | does not directly map for historical reasons. For custom settings, the only difference is 45 | capitalization (`known_custom=custom` VS `sections=CUSTOM,...`) for all others reference the 46 | following mapping: 47 | 48 | - `known_standard_library` : `STANDARD_LIBRARY` 49 | - `extra_standard_library` : `STANDARD_LIBRARY` # Like known standard library but appends instead of replacing 50 | - `known_future_library` : `FUTURE` 51 | - `known_first_party`: `FIRSTPARTY` 52 | - `known_third_party`: `THIRDPARTY` 53 | - `known_local_folder`: `LOCALFOLDER` 54 | 55 | This will likely be changed in isort 6.0.0+ in a backwards compatible way. 56 | 57 | 58 | ## Auto-comment import sections 59 | 60 | Some projects prefer to have import sections uniquely titled to aid in 61 | identifying the sections quickly when visually scanning. isort can 62 | automate this as well. To do this simply set the 63 | `import_heading_{section_name}` setting for each section you wish to 64 | have auto commented - to the desired comment. 65 | 66 | For Example: 67 | 68 | ```ini 69 | import_heading_stdlib=Standard Library 70 | import_heading_firstparty=My Stuff 71 | ``` 72 | 73 | Would lead to output looking like the following: 74 | 75 | ```python 76 | # Standard Library 77 | import os 78 | import sys 79 | 80 | import django.settings 81 | 82 | # My Stuff 83 | import myproject.test 84 | ``` 85 | 86 | ## Ordering by import length 87 | 88 | isort also makes it easy to sort your imports by length, simply by 89 | setting the `length_sort` option to `True`. This will result in the 90 | following output style: 91 | 92 | ```python 93 | from evn.util import ( 94 | Pool, 95 | Dict, 96 | Options, 97 | Constant, 98 | DecayDict, 99 | UnexpectedCodePath, 100 | ) 101 | ``` 102 | 103 | It is also possible to opt-in to sorting imports by length for only 104 | specific sections by using `length_sort_` followed by the section name 105 | as a configuration item, e.g.: 106 | 107 | length_sort_stdlib=1 108 | 109 | ## Controlling how isort sections `from` imports 110 | 111 | By default isort places straight (`import y`) imports above from imports (`from x import y`): 112 | 113 | ```python 114 | import b 115 | from a import a # This will always appear below because it is a from import. 116 | ``` 117 | 118 | However, if you prefer to keep strict alphabetical sorting you can set [force sort within sections](https://pycqa.github.io/isort/docs/configuration/options.html#force-sort-within-sections) to true. Resulting in: 119 | 120 | 121 | ```python 122 | from a import a # This will now appear at top because a appears in the alphabet before b 123 | import b 124 | ``` 125 | 126 | You can even tell isort to always place from imports on top, instead of the default of placing them on bottom, using [from first](https://pycqa.github.io/isort/docs/configuration/options.html#from-first). 127 | 128 | ```python 129 | from b import b # If from first is set to True, all from imports will be placed before non-from imports. 130 | import a 131 | ``` 132 | -------------------------------------------------------------------------------- /docs/configuration/git_hook.md: -------------------------------------------------------------------------------- 1 | # Git Hook 2 | 3 | isort provides a hook function that can be integrated into your Git 4 | pre-commit script to check Python code before committing. 5 | 6 | To cause the commit to fail if there are isort errors (strict mode), 7 | include the following in `.git/hooks/pre-commit`: 8 | 9 | ```python 10 | #!/usr/bin/env python 11 | import sys 12 | from isort.hooks import git_hook 13 | 14 | sys.exit(git_hook(strict=True, modify=True, lazy=True, settings_file="")) 15 | ``` 16 | 17 | If you just want to display warnings, but allow the commit to happen 18 | anyway, call `git_hook` without the strict parameter. If you want to 19 | display warnings, but not also fix the code, call `git_hook` without the 20 | modify parameter. 21 | The `lazy` argument is to support users who are "lazy" to add files 22 | individually to the index and tend to use `git commit -a` instead. 23 | Set it to `True` to ensure all tracked files are properly isorted, 24 | leave it out or set it to `False` to check only files added to your 25 | index. 26 | 27 | If you want to use a specific configuration file for the hook, you can pass its 28 | path to settings_file. If no path is specifically requested, `git_hook` will 29 | search for the configuration file starting at the directory containing the first 30 | staged file, as per `git diff-index` ordering, and going upward in the directory 31 | structure until a valid configuration file is found or 32 | [`MAX_CONFIG_SEARCH_DEPTH`](src/config.py:35) directories are checked. 33 | The settings_file parameter is used to support users who keep their configuration 34 | file in a directory that might not be a parent of all the other files. 35 | -------------------------------------------------------------------------------- /docs/configuration/github_action.md: -------------------------------------------------------------------------------- 1 | # Github Action 2 | 3 | isort provides an official [Github Action][github-action-docs] that can be used as part of a CI/CD workflow to ensure a project's imports are properly sorted. 4 | The action can be found on the [Github Actions Marketplace][python-isort]. 5 | 6 | ## Usage 7 | 8 | The `python-isort` plugin is designed to be run in combination with the [`checkout`][checkout-action] and [`setup-python`][setup-python] actions. 9 | By default, it will run recursively from the root of the repository being linted and will exit with an error if the code is not properly sorted. 10 | 11 | ### Inputs 12 | 13 | #### `isort-version` 14 | 15 | Optional. Version of `isort` to use. Defaults to latest version of `isort`. 16 | 17 | #### `sort-paths` 18 | 19 | Optional. List of paths to sort, relative to your project root. Defaults to `.` 20 | 21 | #### `configuration` 22 | 23 | Optional. `isort` configuration options to pass to the `isort` CLI. Defaults to `--check-only --diff`. 24 | 25 | #### `requirements-files` 26 | 27 | Optional. Paths to python requirements files to install before running isort. 28 | If multiple requirements files are provided, they should be separated by a space. 29 | If custom package installation is required, dependencies should be installed in a separate step before using this action. 30 | 31 | !!! tip 32 | It is important that the project's dependencies be installed before running isort so that third-party libraries are properly sorted. 33 | 34 | ### Outputs 35 | 36 | #### `isort-result` 37 | 38 | Output of the `isort` CLI. 39 | 40 | ### Example usage 41 | 42 | ```yaml 43 | name: Run isort 44 | on: 45 | - push 46 | 47 | jobs: 48 | build: 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/checkout@v4 52 | - uses: actions/setup-python@v5 53 | with: 54 | python-version: 3.13 55 | - uses: isort/isort-action@v1 56 | with: 57 | requirementsFiles: "requirements.txt requirements-test.txt" 58 | ``` 59 | 60 | [github-action-docs]: https://docs.github.com/en/free-pro-team@latest/actions 61 | [python-isort]: https://github.com/marketplace/actions/python-isort 62 | [checkout-action]: https://github.com/actions/checkout 63 | [setup-python]: https://github.com/actions/setup-python 64 | -------------------------------------------------------------------------------- /docs/configuration/multi_line_output_modes.md: -------------------------------------------------------------------------------- 1 | # Multi Line Output Modes 2 | 3 | This [config option](https://pycqa.github.io/isort/docs/configuration/options.html#multi-line-output) defines how from imports wrap when they extend past the line\_length 4 | limit and has 12 possible settings: 5 | 6 | ## 0 - Grid 7 | 8 | ```python 9 | from third_party import (lib1, lib2, lib3, 10 | lib4, lib5, ...) 11 | ``` 12 | 13 | ## 1 - Vertical 14 | 15 | ```python 16 | from third_party import (lib1, 17 | lib2, 18 | lib3 19 | lib4, 20 | lib5, 21 | ...) 22 | ``` 23 | 24 | ## 2 - Hanging Indent 25 | 26 | ```python 27 | from third_party import \ 28 | lib1, lib2, lib3, \ 29 | lib4, lib5, lib6 30 | ``` 31 | 32 | ## 3 - Vertical Hanging Indent 33 | 34 | ```python 35 | from third_party import ( 36 | lib1, 37 | lib2, 38 | lib3, 39 | lib4 40 | ) 41 | ``` 42 | 43 | ## 4 - Hanging Grid 44 | 45 | ```python 46 | from third_party import ( 47 | lib1, lib2, lib3, lib4, 48 | lib5, ...) 49 | ``` 50 | 51 | ## 5 - Hanging Grid Grouped 52 | 53 | ```python 54 | from third_party import ( 55 | lib1, lib2, lib3, lib4, 56 | lib5, ... 57 | ) 58 | ``` 59 | 60 | ## 6 - Hanging Grid Grouped 61 | 62 | Same as Mode 5. Deprecated. 63 | 64 | ## 7 - NOQA 65 | 66 | ```python 67 | from third_party import lib1, lib2, lib3, ... # NOQA 68 | ``` 69 | 70 | Alternatively, you can set `force_single_line` to `True` (`-sl` on the 71 | command line) and every import will appear on its own line: 72 | 73 | ```python 74 | from third_party import lib1 75 | from third_party import lib2 76 | from third_party import lib3 77 | ... 78 | ``` 79 | 80 | ## 8 - Vertical Hanging Indent Bracket 81 | 82 | Same as Mode 3 - _Vertical Hanging Indent_ but the closing parentheses 83 | on the last line is indented. 84 | 85 | ```python 86 | from third_party import ( 87 | lib1, 88 | lib2, 89 | lib3, 90 | lib4, 91 | ) 92 | ``` 93 | 94 | ## 9 - Vertical Prefix From Module Import 95 | 96 | Starts a new line with the same `from MODULE import` prefix when lines are longer than the line length limit. 97 | 98 | ```python 99 | from third_party import lib1, lib2, lib3 100 | from third_party import lib4, lib5, lib6 101 | ``` 102 | 103 | ## 10 - Hanging Indent With Parentheses 104 | 105 | Same as Mode 2 - _Hanging Indent_ but uses parentheses instead of backslash 106 | for wrapping long lines. 107 | 108 | ```python 109 | from third_party import ( 110 | lib1, lib2, lib3, 111 | lib4, lib5, lib6) 112 | ``` 113 | 114 | ## 11 - Backslash Grid 115 | 116 | Same as Mode 0 - _Grid_ but uses backslashes instead of parentheses to group imports. 117 | 118 | ```python 119 | from third_party import lib1, lib2, lib3, \ 120 | lib4, lib5 121 | ``` 122 | -------------------------------------------------------------------------------- /docs/configuration/pre-commit.md: -------------------------------------------------------------------------------- 1 | Using isort with pre-commit 2 | ======== 3 | 4 | isort provides official support for [pre-commit](https://pre-commit.com/). 5 | 6 | ### isort pre-commit step 7 | 8 | To use isort's official pre-commit integration add the following config: 9 | 10 | ```yaml 11 | - repo: https://github.com/pycqa/isort 12 | rev: 5.13.2 13 | hooks: 14 | - id: isort 15 | name: isort (python) 16 | ``` 17 | 18 | under the `repos` section of your projects `.pre-commit-config.yaml` file. Optionally if you want to have different hooks 19 | over different file types (ex: python vs cython vs pyi) you can do so with the following config: 20 | 21 | ```yaml 22 | - repo: https://github.com/pycqa/isort 23 | rev: 5.13.2 24 | hooks: 25 | - id: isort 26 | name: isort (python) 27 | - id: isort 28 | name: isort (cython) 29 | types: [cython] 30 | - id: isort 31 | name: isort (pyi) 32 | types: [pyi] 33 | ``` 34 | 35 | ### seed-isort-config 36 | 37 | Older versions of isort used a lot of magic to determine import placement, that could easily break when running on CI/CD. 38 | To fix this, a utility called `seed-isort-config` was created. Since isort 5 however, the project has drastically improved its placement 39 | logic and ensured a good level of consistency across environments. 40 | If you have a step in your pre-commit config called `seed-isort-config` or similar, it is highly recommend that you remove this. 41 | It is guaranteed to slow things down, and can conflict with isort's own module placement logic. 42 | -------------------------------------------------------------------------------- /docs/configuration/profiles.md: -------------------------------------------------------------------------------- 1 | Built-in Profile for isort 2 | ======== 3 | 4 | The following profiles are built into isort to allow easy interoperability with 5 | common projects and code styles. 6 | 7 | To use any of the listed profiles, use `isort --profile PROFILE_NAME` from the command line, or `profile=PROFILE_NAME` in your configuration file. 8 | 9 | 10 | #black 11 | 12 | 13 | - **multi_line_output**: `3` 14 | - **include_trailing_comma**: `True` 15 | - **split_on_trailing_comma**: `True` 16 | - **force_grid_wrap**: `0` 17 | - **use_parentheses**: `True` 18 | - **ensure_newline_before_comments**: `True` 19 | - **line_length**: `88` 20 | 21 | #django 22 | 23 | 24 | - **combine_as_imports**: `True` 25 | - **include_trailing_comma**: `True` 26 | - **multi_line_output**: `5` 27 | - **line_length**: `79` 28 | 29 | #pycharm 30 | 31 | 32 | - **multi_line_output**: `3` 33 | - **force_grid_wrap**: `2` 34 | - **lines_after_imports**: `2` 35 | 36 | #google 37 | 38 | 39 | - **force_single_line**: `True` 40 | - **force_sort_within_sections**: `True` 41 | - **lexicographical**: `True` 42 | - **single_line_exclusions**: `('typing',)` 43 | - **order_by_type**: `False` 44 | - **group_by_package**: `True` 45 | 46 | #open_stack 47 | 48 | 49 | - **force_single_line**: `True` 50 | - **force_sort_within_sections**: `True` 51 | - **lexicographical**: `True` 52 | 53 | #plone 54 | 55 | 56 | - **force_alphabetical_sort**: `True` 57 | - **force_single_line**: `True` 58 | - **lines_after_imports**: `2` 59 | - **line_length**: `200` 60 | 61 | #attrs 62 | 63 | 64 | - **atomic**: `True` 65 | - **force_grid_wrap**: `0` 66 | - **include_trailing_comma**: `True` 67 | - **lines_after_imports**: `2` 68 | - **lines_between_types**: `1` 69 | - **multi_line_output**: `3` 70 | - **use_parentheses**: `True` 71 | 72 | #hug 73 | 74 | 75 | - **multi_line_output**: `3` 76 | - **include_trailing_comma**: `True` 77 | - **force_grid_wrap**: `0` 78 | - **use_parentheses**: `True` 79 | - **line_length**: `100` 80 | 81 | #wemake 82 | 83 | 84 | - **multi_line_output**: `3` 85 | - **include_trailing_comma**: `True` 86 | - **use_parentheses**: `True` 87 | - **line_length**: `80` 88 | 89 | #appnexus 90 | 91 | 92 | - **multi_line_output**: `3` 93 | - **include_trailing_comma**: `True` 94 | - **force_grid_wrap**: `0` 95 | - **use_parentheses**: `True` 96 | - **ensure_newline_before_comments**: `True` 97 | - **line_length**: `88` 98 | - **force_sort_within_sections**: `True` 99 | - **order_by_type**: `False` 100 | - **case_sensitive**: `False` 101 | - **reverse_relative**: `True` 102 | - **sort_relative_in_force_sorted_sections**: `True` 103 | - **sections**: `['FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'APPLICATION', 'LOCALFOLDER']` 104 | - **no_lines_before**: `'LOCALFOLDER'` 105 | -------------------------------------------------------------------------------- /docs/configuration/setuptools_integration.md: -------------------------------------------------------------------------------- 1 | # Setuptools integration 2 | 3 | Upon installation, isort enables a `setuptools` command that checks 4 | Python files declared by your project. 5 | 6 | Running `python setup.py isort` on the command line will check the files 7 | listed in your `py_modules` and `packages`. If any warning is found, the 8 | command will exit with an error code: 9 | 10 | ```bash 11 | $ python setup.py isort 12 | ``` 13 | 14 | Also, to allow users to be able to use the command without having to 15 | install isort themselves, add isort to the setup\_requires of your 16 | `setup()` like so: 17 | 18 | ```python 19 | setup( 20 | name="project", 21 | packages=["project"], 22 | 23 | setup_requires=[ 24 | "isort" 25 | ] 26 | ) 27 | ``` 28 | -------------------------------------------------------------------------------- /docs/contributing/1.-contributing-guide.md: -------------------------------------------------------------------------------- 1 | Contributing to isort 2 | ======== 3 | 4 | Looking for a useful open source project to contribute to? 5 | Want your contributions to be warmly welcomed and acknowledged? 6 | Welcome! You have found the right place. 7 | 8 | ## Getting isort set up for local development 9 | The first step when contributing to any project is getting it set up on your local machine. isort aims to make this as simple as possible. 10 | 11 | Account Requirements: 12 | 13 | - [A valid GitHub account](https://github.com/join) 14 | 15 | Base System Requirements: 16 | 17 | - Python3.9+ 18 | - uv 19 | - bash or a bash compatible shell (should be auto-installed on Linux / Mac) 20 | - WSL users running Ubuntu may need to install Python's venv module even after installing Python. 21 | 22 | Once you have verified that your system matches the base requirements you can start to get the project working by following these steps: 23 | 24 | 1. [Fork the project on GitHub](https://github.com/pycqa/isort/fork). 25 | 1. Clone your fork to your local file system: 26 | `git clone https://github.com/$GITHUB_ACCOUNT/isort.git` 27 | 1. `cd isort` 28 | 1. `uv sync --all-extras --frozen` 29 | * Optionally, isolate uv's installation from the rest of your system using the instructions on the uv site here: https://docs.astral.sh/uv/ 30 | 1. `./scripts/test.sh` should yield Success: no issues found 31 | 1. `./scripts/clean.sh` should yield a report checking packages 32 | 33 | **TIP**: `./scripts/done.sh` will run both clean and test in one step. 34 | 35 | ### Docker development 36 | 37 | If you would instead like to develop using Docker, the only local requirement is docker. 38 | See the [docker docs](https://docs.docker.com/get-started/) if you have not used docker before. 39 | 40 | Once you have the docker daemon running and have cloned the repository, you can get started by following these steps: 41 | 42 | 1. `cd isort` 43 | 2. `./scripts/docker.sh` 44 | 45 | A local test cycle might look like the following: 46 | 47 | 1. `docker build ./ -t isort:latest` 48 | 2. `docker run isort` 49 | 3. if #2 fails, debug, save, and goto #1 50 | * `docker run -it isort bash` will get you into the failed environment 51 | * `docker run -v $(git rev-parse --show-toplevel):/isort` will make changes made in the docker environment persist on your local checkout. 52 | **TIP**: combine both to get an interactive docker shell that loads changes made locally, even after build, to quickly rerun that pesky failing test 53 | 4. `./scripts/docker.sh` 54 | 5. if #4 fails, debug, save and goto #1; you may need to specify a different `--build-arg VERSION=$VER` 55 | 6. congrats! you are probably ready to push a contribution 56 | 57 | ## Making a contribution 58 | Congrats! You're now ready to make a contribution! Use the following as a guide to help you reach a successful pull-request: 59 | 60 | 1. Check the [issues page](https://github.com/pycqa/isort/issues) on GitHub to see if the task you want to complete is listed there. 61 | - If it's listed there, write a comment letting others know you are working on it. 62 | - If it's not listed in GitHub issues, go ahead and log a new issue. Then add a comment letting everyone know you have it under control. 63 | - If you're not sure if it's something that is good for the main isort project and want immediate feedback, you can discuss it [here](https://gitter.im/timothycrosley/isort). 64 | 2. Create an issue branch for your local work `git checkout -b issue/$ISSUE-NUMBER`. 65 | 3. Do your magic here. 66 | 4. Ensure your code matches the [HOPE-8 Coding Standard](https://github.com/hugapi/HOPE/blob/master/all/HOPE-8--Style-Guide-for-Hug-Code.md#hope-8----style-guide-for-hug-code) used by the project. 67 | 5. Run tests locally to make sure everything is still working 68 | `./scripts/done.sh` 69 | _Or if you are using Docker_ 70 | `docker run isort:latest` 71 | 6. Submit a pull request to the main project repository via GitHub. 72 | 73 | Thanks for the contribution! It will quickly get reviewed, and, once accepted, will result in your name being added to the acknowledgments list :). 74 | 75 | ## Thank you! 76 | I can not tell you how thankful I am for the hard work done by isort contributors like *you*. 77 | 78 | Thank you! 79 | 80 | ~Timothy Crosley 81 | 82 | -------------------------------------------------------------------------------- /docs/contributing/2.-coding-standard.md: -------------------------------------------------------------------------------- 1 | # HOPE 8 -- Style Guide for Hug Code 2 | 3 | | | | 4 | | ------------| ------------------------------------------- | 5 | | HOPE: | 8 | 6 | | Title: | Style Guide for Hug Code | 7 | | Author(s): | Timothy Crosley | 8 | | Status: | Active | 9 | | Type: | Process | 10 | | Created: | 19-May-2019 | 11 | | Updated: | 17-August-2019 | 12 | 13 | ## Introduction 14 | 15 | This document gives coding conventions for the Hug code comprising the Hug core as well as all official interfaces, extensions, and plugins for the framework. 16 | Optionally, projects that use Hug are encouraged to follow this HOPE and link to it as a reference. 17 | 18 | ## PEP 8 Foundation 19 | 20 | All guidelines in this document are in addition to those defined in Python's [PEP 8](https://www.python.org/dev/peps/pep-0008/) and [PEP 257](https://www.python.org/dev/peps/pep-0257/) guidelines. 21 | 22 | ## Line Length 23 | 24 | Too short of lines discourage descriptive variable names where they otherwise make sense. 25 | Too long of lines reduce overall readability and make it hard to compare 2 files side by side. 26 | There is no perfect number: but for Hug, we've decided to cap the lines at 100 characters. 27 | 28 | ## Descriptive Variable names 29 | 30 | Naming things is hard. Hug has a few strict guidelines on the usage of variable names, which hopefully will reduce some of the guesswork: 31 | - No one character variable names. 32 | - Except for x, y, and z as coordinates. 33 | - It's not okay to override built-in functions. 34 | - Except for `id`. Guido himself thought that shouldn't have been moved to the system module. It's too commonly used, and alternatives feel very artificial. 35 | - Avoid Acronyms, Abbreviations, or any other short forms - unless they are almost universally understand. 36 | 37 | ## Adding new modules 38 | 39 | New modules added to the a project that follows the HOPE-8 standard should all live directly within the base `PROJECT_NAME/` directory without nesting. If the modules are meant only for internal use within the project, they should be prefixed with a leading underscore. For example, def _internal_function. Modules should contain a docstring at the top that gives a general explanation of the purpose and then restates the project's use of the MIT license. 40 | There should be a `tests/test_$MODULE_NAME.py` file created to correspond to every new module that contains test coverage for the module. Ideally, tests should be 1:1 (one test object per code object, one test method per code method) to the extent cleanly possible. 41 | 42 | ## Automated Code Cleaners 43 | 44 | All code submitted to Hug should be formatted using Black and isort. 45 | Black should be run with the line length set to 100, and isort with Black compatible settings in place. 46 | 47 | ## Automated Code Linting 48 | 49 | All code submitted to hug should run through the following tools: 50 | 51 | - Black and isort verification. 52 | - Flake8 53 | - flake8-bugbear 54 | - Bandit 55 | - ruff 56 | - pep8-naming 57 | - vulture 58 | -------------------------------------------------------------------------------- /docs/contributing/3.-code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # HOPE 11 -- Code of Conduct 2 | 3 | | | | 4 | | ------------| ------------------------------------------- | 5 | | HOPE: | 11 | 6 | | Title: | Code of Conduct | 7 | | Author(s): | Timothy Crosley | 8 | | Status: | Active | 9 | | Type: | Process | 10 | | Created: | 17-August-2019 | 11 | | Updated: | 17-August-2019 | 12 | 13 | ## Abstract 14 | 15 | Defines the Code of Conduct for Hug and all related projects. 16 | 17 | ## Our Pledge 18 | 19 | In the interest of fostering an open and welcoming environment, we as 20 | contributors and maintainers pledge to making participation in our project and 21 | our community a harassment-free experience for everyone, regardless of age, body 22 | size, disability, ethnicity, sex characteristics, gender identity and expression, 23 | level of experience, education, socio-economic status, nationality, personal 24 | appearance, race, religion, or sexual identity and orientation. 25 | 26 | ## Our Standards 27 | 28 | Examples of behavior that contributes to creating a positive environment 29 | include: 30 | 31 | * Using welcoming and inclusive language 32 | * Being respectful of differing viewpoints and experiences 33 | * Gracefully accepting constructive criticism 34 | * Focusing on what is best for the community 35 | * Showing empathy towards other community members 36 | 37 | Examples of unacceptable behavior by participants include: 38 | 39 | * The use of sexualized language or imagery and unwelcome sexual attention or 40 | advances 41 | * Trolling, insulting/derogatory comments, and personal or political attacks 42 | * Public or private harassment 43 | * Publishing others' private information, such as a physical or electronic 44 | address, without explicit permission 45 | * Other conduct which could reasonably be considered inappropriate in a 46 | professional setting 47 | 48 | ## Our Responsibilities 49 | 50 | Project maintainers are responsible for clarifying the standards of acceptable 51 | behavior and are expected to take appropriate and fair corrective action in 52 | response to any instances of unacceptable behavior. 53 | 54 | Project maintainers have the right and responsibility to remove, edit, or 55 | reject comments, commits, code, wiki edits, issues, and other contributions 56 | that are not aligned to this Code of Conduct, or to ban temporarily or 57 | permanently any contributor for other behaviors that they deem inappropriate, 58 | threatening, offensive, or harmful. 59 | 60 | ## Scope 61 | 62 | This Code of Conduct applies both within project spaces and in public spaces 63 | when an individual is representing the project or its community. Examples of 64 | representing a project or community include using an official project e-mail 65 | address, posting via an official social media account, or acting as an appointed 66 | representative at an online or offline event. Representation of a project may be 67 | further defined and clarified by project maintainers. 68 | 69 | ## Enforcement 70 | 71 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 72 | reported by contacting [timothy.crosley@gmail.com](mailto:timothy.crosley@gmail.com). All 73 | complaints will be reviewed and investigated and will result in a response that 74 | is deemed necessary and appropriate to the circumstances. Confidentiality will be maintained 75 | with regard to the reporter of an incident. 76 | Further details of specific enforcement policies may be posted separately. 77 | 78 | Project maintainers who do not follow or enforce the Code of Conduct in good 79 | faith may face temporary or permanent repercussions as determined by other 80 | members of the project's leadership. 81 | 82 | ## Attribution 83 | 84 | This Code of Conduct is adapted from the [Contributor Covenant][https://www.contributor-covenant.org], version 1.4, 85 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 86 | 87 | For answers to common questions about this code of conduct, see 88 | https://www.contributor-covenant.org/faq 89 | -------------------------------------------------------------------------------- /docs/howto/shared_profiles.md: -------------------------------------------------------------------------------- 1 | # Shared Profiles 2 | 3 | As well as the [built in 4 | profiles](https://pycqa.github.io/isort/docs/configuration/profiles.html), you 5 | can define and share your own profiles. 6 | 7 | All that's required is to create a Python package that exposes an entry point to 8 | a dictionary exposing profile settings under `isort.profiles`. An example is 9 | available [within the `isort` 10 | repo](https://github.com/PyCQA/isort/tree/main/example_shared_isort_profile) 11 | 12 | ### Example `.isort.cfg` 13 | 14 | ``` 15 | [options.entry_points] 16 | isort.profiles = 17 | shared_profile=my_module:PROFILE 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/major_releases/release_policy.md: -------------------------------------------------------------------------------- 1 | # isort Project Official Release Policy 2 | 3 | isort has moved from being a simple hobby project for individuals to sort imports in their Python files 4 | to an essential part of the CI/CD pipeline for large companies and significant Open Source projects. 5 | Due to this evolution, it is now of increased importance that isort maintains a level of quality, predictability, and consistency 6 | that gives projects big and small confidence to depend on it. 7 | 8 | ## Formatting guarantees 9 | 10 | With isort 5.1.0, the isort Project guarantees that formatting will stay the same for the options given in accordance to its test suite for the duration of all major releases. This means projects can safely use isort > 5.1.0 < 6.0.0 11 | without worrying about major formatting changes disrupting their Project. 12 | 13 | ## Packaging guarantees 14 | 15 | Starting with the 5.0.0 release isort includes the following project guarantees to help guide development: 16 | 17 | - isort will never have dependencies, optional, required, or otherwise. 18 | - isort will always act the same independent to the Python environment it is installed in. 19 | 20 | ## Versioning 21 | 22 | isort follows the [Semantic Versioning 2.0.0 specification](https://semver.org/spec/v2.0.0.html) meaning it has three numerical version parts with distinct rules 23 | `MAJOR.MINOR.PATCH`. 24 | 25 | ### Patch Releases x.x.1 26 | 27 | Within the isort Project, patch releases are really meant solely to fix bugs and minor oversights. 28 | Patch releases should *never* drastically change formatting, even if it's for the better. 29 | 30 | ### Minor Releases x.1.x 31 | 32 | Minor changes can contain new backward-incompatible features, and of particular note can include bug fixes 33 | that result in intentional formatting changes - but they should still never be too large in scope. 34 | API backward compatibility should strictly be maintained. 35 | 36 | ### Major Releases 1.x.x 37 | 38 | Major releases are the only place where backward-incompatible changes or substantial formatting changes can occur. 39 | Because these kind of changes are likely to break projects that utilize isort, either as a formatter or library, 40 | isort must do the following: 41 | 42 | - Release a release candidate with at least 2 weeks for bugs to be reported and fixed. 43 | - Keep releasing follow up release candidates until there are no or few bugs reported. 44 | - Provide an upgrade guide that helps users work around any backward-incompatible changes. 45 | - Provide a detailed changelog of all changes. 46 | - Where possible, warn and point to the upgrade guide instead of breaking when options are removed. 47 | -------------------------------------------------------------------------------- /docs/quick_start/0.-try.md: -------------------------------------------------------------------------------- 1 | Try isort from your browser! 2 | ======== 3 | 4 | Use our live isort editor to see how isort can help improve the formatting of your Python imports. 5 | 6 | !!! important - "Safe to use. No code is transmitted." 7 | The below live isort tester doesn't transmit any of the code you paste to our server or anyone else's. Instead, this page runs a complete Python3 installation with isort installed entirely within your browser. To accomplish this, it utilizes the [pyodide](https://github.com/iodide-project/pyodide) project. 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 |
from future import braces 23 | import b 24 | import b 25 | import os 26 | import a 27 | from future import braces 28 | import b 29 | import a 30 | import b, a 31 |
32 |
Loading...
33 |
34 | 35 |  Configuration (Note: the below must follow JSON format). Full configuration guide is here: 36 | 37 |
{"line_length": 80, 38 | "profile": "black", 39 | "atomic": true 40 | } 41 |
42 |
43 |
44 |
45 | 46 | 47 |
48 | Like what you saw? Installing isort to use locally is as simple as `pip3 install isort`. 49 | 50 | [Click here for full installation instructions.](https://pycqa.github.io/isort/docs/quick_start/1.-install) 51 | -------------------------------------------------------------------------------- /docs/quick_start/1.-install.md: -------------------------------------------------------------------------------- 1 | Install `isort` using your preferred Python package manager: 2 | 3 | `pip3 install isort` 4 | 5 | OR 6 | 7 | `uv add isort` 8 | 9 | OR 10 | 11 | `poetry add isort` 12 | 13 | OR 14 | 15 | `pipenv install isort` 16 | 17 | OR 18 | 19 | For a fully isolated user installation you can use [pipx](https://github.com/pipxproject/pipx) 20 | 21 | `pipx install isort` 22 | 23 | 24 | 25 | !!!tip 26 | If you want isort to act as a linter for projects, it probably makes sense to add isort as an explicit development dependency for each project that uses it. If, on the other hand, you are an individual developer simply using isort as a personal tool to clean up your own commits, a global or user level installation makes sense. Both are seamlessly supported on a single machine. 27 | -------------------------------------------------------------------------------- /docs/quick_start/2.-cli.md: -------------------------------------------------------------------------------- 1 | # Command Line Usage 2 | 3 | Once installed, `isort` exposes a command line utility for sorting, organizing, and formatting imports within Python and Cython source files. 4 | 5 | To verify the tool is installed correctly, run `isort` from the command line and you should be given the available commands and the version of isort installed. 6 | For a list of all CLI options type `isort --help` or view [the online configuration reference](https://pycqa.github.io/isort/docs/configuration/options): 7 | 8 | 9 | 10 | ## Formatting a Project 11 | 12 | In general, isort is most commonly utilized across an entire projects source at once. The simplest way to do this is `isort .` or if using a `src` directory `isort src`. isort will automatically find all Python source files recursively and pick-up a configuration file placed at the root of your project if present. This can be combined with any command line configuration customizations such as specifying a profile to use (`isort . --profile black`). 13 | 14 | 15 | 16 | ## Verifying a Project 17 | 18 | The second most common usage of isort is verifying that imports within a project are formatted correctly (often within the context of a CI/CD system). The simplest way to accomplish this is using the check command line option: `isort --check .`. To improve the usefulness of errors when they do occur, this can be combined with the diff option: `isort --check --diff .`. 19 | 20 | 21 | 22 | ## Single Source Files 23 | 24 | Finally, isort can just as easily be ran against individual source files. Simply pass in a single or multiple source files to sort or validate (Example: `isort setup.py`). 25 | 26 | 27 | 28 | ## Multiple Projects 29 | 30 | Running a single isort command across multiple projects, or source files spanning multiple projects, is highly discouraged. Instead it is recommended that an isort process (or command) is ran for each project independently. This is because isort creates an immutable config for each CLI instance. 31 | 32 | ``` 33 | # YES 34 | isort project1 35 | isort project2 36 | 37 | # Also YES 38 | isort project1/src project1/test 39 | isort project2/src project2/test 40 | 41 | # NO 42 | isort project1 project2 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/quick_start/3.-api.md: -------------------------------------------------------------------------------- 1 | # Programmatic Python API Usage 2 | 3 | In addition to the powerful command line interface, isort exposes a complete Python API. 4 | 5 | To use the Python API, `import isort` and then call the desired function call: 6 | 7 | 8 | 9 | Every function is fully type hinted and requires and returns only builtin Python objects. 10 | 11 | Highlights include: 12 | 13 | - `isort.code` - Takes a string containing code, and returns it with imports sorted. 14 | - `isort.check_code` - Takes a string containing code, and returns `True` if all imports are sorted correctly, otherwise, `False`. 15 | - `isort.stream` - Takes an input stream containing Python code and an output stream. Outputs code to output stream with all imports sorted. 16 | - `isort.check_stream` - Takes an input stream containing Python code and returns `True` if all imports in the stream are sorted correctly, otherwise, `False`. 17 | - `isort.file` - Takes the path of a Python source file and sorts the imports in-place. 18 | - `isort.check_file` - Takes the path of a Python source file and returns `True` if all imports contained within are sorted correctly, otherwise, `False`. 19 | - `isort.place_module` - Takes the name of a module as a string and returns the categorization determined for it. 20 | - `isort.place_module_with_reason` - Takes the name of a module as a string and returns the categorization determined for it and why that categorization was given. 21 | 22 | For a full definition of the API see the [API reference documentation](https://pycqa.github.io/isort/reference/isort/api) or try `help(isort)` from an interactive interpreter. 23 | -------------------------------------------------------------------------------- /docs/quick_start/interactive.css: -------------------------------------------------------------------------------- 1 | .editor { 2 | height: 400px; 3 | width: 49.7%; 4 | display: inline-block; 5 | } 6 | .configurator { 7 | height: 200px; 8 | width: 100%; 9 | clear: both; 10 | float: left; 11 | } 12 | #liveTester { 13 | background: black; 14 | color: white; 15 | } 16 | #sideBySide { 17 | white-space: nowrap; 18 | } 19 | #liveTester.fullscreen { 20 | position: absolute; 21 | top: 0px; 22 | left: 0px; 23 | right: 0px; 24 | z-index: 99; 25 | bottom: 0px; 26 | } 27 | -------------------------------------------------------------------------------- /docs/quick_start/interactive.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('load', () => { 2 | var input = ace.edit("inputEditor"); 3 | var output = ace.edit("outputEditor"); 4 | var configurator = ace.edit("configEditor"); 5 | 6 | [input, output, configurator].forEach((editor) => { 7 | editor.setTheme("ace/theme/monokai"); 8 | editor.session.setMode("ace/mode/python"); 9 | editor.resize(); 10 | }); 11 | 12 | configurator.session.setMode("ace/mode/json"); 13 | 14 | function updateOutput() { 15 | output.setValue(document.sort_code(input.getValue(), configurator.getValue())); 16 | } 17 | 18 | output.setReadOnly(true); 19 | input.session.on('change', updateOutput); 20 | configurator.session.on('change', updateOutput); 21 | 22 | document.updateOutput = updateOutput; 23 | }); 24 | 25 | 26 | languagePluginLoader.then(() => { 27 | return pyodide.loadPackage(['micropip']) 28 | }).then(() => { 29 | pyodide.runPython(` 30 | import micropip 31 | 32 | from js import document 33 | 34 | def use_isort(*args): 35 | import isort 36 | import json 37 | import textwrap 38 | 39 | print(f"Using {isort.__version__} of isort.") 40 | 41 | def sort_code(code, configuration): 42 | try: 43 | configuration = json.loads(configuration or "{}") 44 | except Exception as configuration_error: 45 | return "\\n".join(textwrap.wrap(f"Exception thrown trying to read given configuration {configuration_error}", 40)) 46 | try: 47 | return isort.code(code, **configuration) 48 | except Exception as isort_error: 49 | return "\\n".join(textwrap.wrap(f"Exception thrown trying to sort given imports {isort_error}", 40)) 50 | 51 | document.sort_code = sort_code 52 | document.updateOutput() 53 | 54 | micropip.install('isort').then(use_isort)`); 55 | }); 56 | -------------------------------------------------------------------------------- /docs/quick_start/isort-5.0.0-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/docs/quick_start/isort-5.0.0-py3-none-any.whl -------------------------------------------------------------------------------- /docs/quick_start/isort-5.0.1-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/docs/quick_start/isort-5.0.1-py3-none-any.whl -------------------------------------------------------------------------------- /docs/warning_and_error_codes/W0500.md: -------------------------------------------------------------------------------- 1 | # W0500 Warning Codes 2 | 3 | The W0500 error codes are reserved for warnings related to a major release of the isort project. 4 | Generally, the existence of any of these will trigger one additional warning listing the upgrade guide. 5 | 6 | For the most recent upgrade guide, see: [The 5.0.0 Upgrade Guide.](https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0.html). 7 | 8 | ## W0501: Deprecated CLI flags were included that will be ignored. 9 | 10 | This warning will be shown if a CLI flag is passed into the isort command that is no longer supported but can safely be ignored. 11 | Often, this happens because an argument used to be required to turn on a feature that then became the default. An example of this 12 | is `--recursive` which became the default behavior for all folders passed-in starting with 5.0.0. 13 | 14 | ## W0502: Deprecated CLI flags were included that will safely be remapped. 15 | 16 | This warning will be shown if a CLI flag is passed into the isort command that is no longer supported but can safely be remapped to the new version of the flag. If you encounter this warning, you must update the argument to match the new flag 17 | before the next major release. 18 | 19 | ## W0503: Deprecated config options were ignored. 20 | 21 | This warning will be shown if a deprecated config option is defined in the Project's isort config file, but can safely be ignored. 22 | This is similar to `W0500` but dealing with config files rather than CLI flags. 23 | -------------------------------------------------------------------------------- /example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/example.gif -------------------------------------------------------------------------------- /example_isort_formatting_plugin/example_isort_formatting_plugin.py: -------------------------------------------------------------------------------- 1 | import black 2 | 3 | import isort 4 | 5 | 6 | def black_format_import_section( 7 | contents: str, extension: str, config: isort.settings.Config 8 | ) -> str: 9 | """Formats the given import section using black.""" 10 | if extension.lower() not in ("pyi", "py"): 11 | return contents 12 | 13 | try: 14 | return black.format_file_contents( 15 | contents, 16 | fast=True, 17 | mode=black.FileMode( 18 | is_pyi=extension.lower() == "pyi", 19 | line_length=config.line_length, 20 | ), 21 | ) 22 | except black.NothingChanged: 23 | return contents 24 | -------------------------------------------------------------------------------- /example_isort_formatting_plugin/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "example_isort_formatting_plugin" 3 | version = "0.1.1" 4 | description = "An example plugin that modifies isort formatting using black." 5 | authors = [{name = "Timothy Crosley", email = "timothy.crosley@gmail.com"}, {name = "staticdev", email = "staticdev-support@proton.me"}] 6 | license = "MIT" 7 | requires-python = ">=3.9.0" 8 | dependencies = [ 9 | "isort>=5.13.2", 10 | "black>=24.3.0", 11 | ] 12 | 13 | [project.entry-points."isort.formatters"] 14 | example = "example_isort_formatting_plugin:black_format_import_section" 15 | 16 | [build-system] 17 | requires = ["hatchling"] 18 | build-backend = "hatchling.build" 19 | -------------------------------------------------------------------------------- /example_isort_sorting_plugin/example_isort_sorting_plugin.py: -------------------------------------------------------------------------------- 1 | from natsort import natsorted 2 | 3 | 4 | def natural_plus(*args, **kwargs) -> str: 5 | """An even more natural sorting order for isort using natsort.""" 6 | return natsorted(*args, **kwargs) 7 | -------------------------------------------------------------------------------- /example_isort_sorting_plugin/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "example_isort_sorting_plugin" 3 | version = "0.1.0" 4 | description = "An example plugin that modifies isorts sorting order to provide an even more natural sort by utilizing natsort." 5 | authors = [{name = "Timothy Crosley", email = "timothy.crosley@gmail.com"}, {name = "staticdev", email = "staticdev-support@proton.me"}] 6 | license = "MIT" 7 | requires-python = ">=3.9.0" 8 | dependencies = [ 9 | "natsort>=7.1.1" 10 | ] 11 | 12 | [project.entry-points."isort.sort_function"] 13 | natural_plus = "example_isort_sorting_plugin:natural_plus" 14 | 15 | [build-system] 16 | requires = ["hatchling"] 17 | build-backend = "hatchling.build" 18 | -------------------------------------------------------------------------------- /example_isort_sorting_plugin/uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.9.0" 3 | 4 | [[package]] 5 | name = "example-isort-sorting-plugin" 6 | version = "0.1.0" 7 | source = { editable = "." } 8 | dependencies = [ 9 | { name = "natsort" }, 10 | ] 11 | 12 | [package.metadata] 13 | requires-dist = [{ name = "natsort", specifier = ">=7.1.1" }] 14 | 15 | [[package]] 16 | name = "natsort" 17 | version = "8.4.0" 18 | source = { registry = "https://pypi.org/simple" } 19 | sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575 } 20 | wheels = [ 21 | { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268 }, 22 | ] 23 | -------------------------------------------------------------------------------- /example_shared_isort_profile/example_shared_isort_profile.py: -------------------------------------------------------------------------------- 1 | PROFILE = { 2 | "multi_line_output": 3, 3 | "include_trailing_comma": True, 4 | "force_grid_wrap": 0, 5 | "use_parentheses": True, 6 | "line_length": 100, 7 | } 8 | -------------------------------------------------------------------------------- /example_shared_isort_profile/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "example_shared_isort_profile" 3 | version = "0.1.0" 4 | description = "An example shared isort profile" 5 | authors = [{name = "Timothy Crosley", email = "timothy.crosley@gmail.com"}, {name = "staticdev", email = "staticdev-support@proton.me"}] 6 | license = "MIT" 7 | requires-python = ">=3.9.0" 8 | 9 | [project.entry-points."isort.profiles"] 10 | example = "example_shared_isort_profile:PROFILE" 11 | 12 | [build-system] 13 | requires = ["hatchling"] 14 | build-backend = "hatchling.build" 15 | -------------------------------------------------------------------------------- /example_shared_isort_profile/uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.9.0" 3 | 4 | [[package]] 5 | name = "example-shared-isort-profile" 6 | version = "0.1.0" 7 | source = { editable = "." } 8 | -------------------------------------------------------------------------------- /isort/__init__.py: -------------------------------------------------------------------------------- 1 | """Defines the public isort interface""" 2 | 3 | __all__ = ( 4 | "Config", 5 | "ImportKey", 6 | "__version__", 7 | "check_code", 8 | "check_file", 9 | "check_stream", 10 | "code", 11 | "file", 12 | "find_imports_in_code", 13 | "find_imports_in_file", 14 | "find_imports_in_paths", 15 | "find_imports_in_stream", 16 | "place_module", 17 | "place_module_with_reason", 18 | "settings", 19 | "stream", 20 | ) 21 | 22 | from . import settings 23 | from ._version import __version__ 24 | from .api import ImportKey 25 | from .api import check_code_string as check_code 26 | from .api import ( 27 | check_file, 28 | check_stream, 29 | find_imports_in_code, 30 | find_imports_in_file, 31 | find_imports_in_paths, 32 | find_imports_in_stream, 33 | place_module, 34 | place_module_with_reason, 35 | ) 36 | from .api import sort_code_string as code 37 | from .api import sort_file as file 38 | from .api import sort_stream as stream 39 | from .settings import Config 40 | -------------------------------------------------------------------------------- /isort/__main__.py: -------------------------------------------------------------------------------- 1 | from isort.main import main 2 | 3 | main() 4 | -------------------------------------------------------------------------------- /isort/_vendored/tomli/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Taneli Hukkinen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /isort/_vendored/tomli/__init__.py: -------------------------------------------------------------------------------- 1 | """A lil' TOML parser.""" 2 | 3 | __all__ = ("loads", "load", "TOMLDecodeError") 4 | __version__ = "1.2.0" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT 5 | 6 | from ._parser import TOMLDecodeError, load, loads 7 | -------------------------------------------------------------------------------- /isort/_vendored/tomli/_re.py: -------------------------------------------------------------------------------- 1 | import re 2 | from datetime import date, datetime, time, timedelta, timezone, tzinfo 3 | from functools import lru_cache 4 | from typing import TYPE_CHECKING, Any, Optional, Union 5 | 6 | if TYPE_CHECKING: 7 | from tomli._parser import ParseFloat 8 | 9 | # E.g. 10 | # - 00:32:00.999999 11 | # - 00:32:00 12 | _TIME_RE_STR = r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(?:\.([0-9]{1,6})[0-9]*)?" 13 | 14 | RE_NUMBER = re.compile( 15 | r""" 16 | 0 17 | (?: 18 | x[0-9A-Fa-f](?:_?[0-9A-Fa-f])* # hex 19 | | 20 | b[01](?:_?[01])* # bin 21 | | 22 | o[0-7](?:_?[0-7])* # oct 23 | ) 24 | | 25 | [+-]?(?:0|[1-9](?:_?[0-9])*) # dec, integer part 26 | (?P 27 | (?:\.[0-9](?:_?[0-9])*)? # optional fractional part 28 | (?:[eE][+-]?[0-9](?:_?[0-9])*)? # optional exponent part 29 | ) 30 | """, 31 | flags=re.VERBOSE, 32 | ) 33 | RE_LOCALTIME = re.compile(_TIME_RE_STR) 34 | RE_DATETIME = re.compile( 35 | rf""" 36 | ([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) # date, e.g. 1988-10-27 37 | (?: 38 | [T ] 39 | {_TIME_RE_STR} 40 | (?:(Z)|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))? # optional time offset 41 | )? 42 | """, 43 | flags=re.VERBOSE, 44 | ) 45 | 46 | 47 | def match_to_datetime(match: "re.Match") -> Union[datetime, date]: 48 | """Convert a `RE_DATETIME` match to `datetime.datetime` or `datetime.date`. 49 | 50 | Raises ValueError if the match does not correspond to a valid date 51 | or datetime. 52 | """ 53 | ( 54 | year_str, 55 | month_str, 56 | day_str, 57 | hour_str, 58 | minute_str, 59 | sec_str, 60 | micros_str, 61 | zulu_time, 62 | offset_sign_str, 63 | offset_hour_str, 64 | offset_minute_str, 65 | ) = match.groups() 66 | year, month, day = int(year_str), int(month_str), int(day_str) 67 | if hour_str is None: 68 | return date(year, month, day) 69 | hour, minute, sec = int(hour_str), int(minute_str), int(sec_str) 70 | micros = int(micros_str.ljust(6, "0")) if micros_str else 0 71 | if offset_sign_str: 72 | tz: Optional[tzinfo] = cached_tz(offset_hour_str, offset_minute_str, offset_sign_str) 73 | elif zulu_time: 74 | tz = timezone.utc 75 | else: # local date-time 76 | tz = None 77 | return datetime(year, month, day, hour, minute, sec, micros, tzinfo=tz) 78 | 79 | 80 | @lru_cache(maxsize=None) 81 | def cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone: 82 | sign = 1 if sign_str == "+" else -1 83 | return timezone( 84 | timedelta( 85 | hours=sign * int(hour_str), 86 | minutes=sign * int(minute_str), 87 | ) 88 | ) 89 | 90 | 91 | def match_to_localtime(match: "re.Match") -> time: 92 | hour_str, minute_str, sec_str, micros_str = match.groups() 93 | micros = int(micros_str.ljust(6, "0")) if micros_str else 0 94 | return time(int(hour_str), int(minute_str), int(sec_str), micros) 95 | 96 | 97 | def match_to_number(match: "re.Match", parse_float: "ParseFloat") -> Any: 98 | if match.group("floatpart"): 99 | return parse_float(match.group()) 100 | return int(match.group(), 0) 101 | -------------------------------------------------------------------------------- /isort/_vendored/tomli/py.typed: -------------------------------------------------------------------------------- 1 | # Marker file for PEP 561 2 | -------------------------------------------------------------------------------- /isort/_version.py: -------------------------------------------------------------------------------- 1 | from importlib import metadata 2 | 3 | __version__ = metadata.version("isort") 4 | -------------------------------------------------------------------------------- /isort/comments.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional, Tuple 2 | 3 | 4 | def parse(line: str) -> Tuple[str, str]: 5 | """Parses import lines for comments and returns back the 6 | import statement and the associated comment. 7 | """ 8 | comment_start = line.find("#") 9 | if comment_start != -1: 10 | return (line[:comment_start], line[comment_start + 1 :].strip()) 11 | 12 | return (line, "") 13 | 14 | 15 | def add_to_line( 16 | comments: Optional[List[str]], 17 | original_string: str = "", 18 | removed: bool = False, 19 | comment_prefix: str = "", 20 | ) -> str: 21 | """Returns a string with comments added if removed is not set.""" 22 | if removed: 23 | return parse(original_string)[0] 24 | 25 | if not comments: 26 | return original_string 27 | 28 | unique_comments: List[str] = [] 29 | for comment in comments: 30 | if comment not in unique_comments: 31 | unique_comments.append(comment) 32 | return f"{parse(original_string)[0]}{comment_prefix} {'; '.join(unique_comments)}" 33 | -------------------------------------------------------------------------------- /isort/deprecated/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/isort/deprecated/__init__.py -------------------------------------------------------------------------------- /isort/files.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | from typing import Iterable, Iterator, List, Set 4 | 5 | from isort.settings import Config 6 | 7 | 8 | def find( 9 | paths: Iterable[str], config: Config, skipped: List[str], broken: List[str] 10 | ) -> Iterator[str]: 11 | """Fines and provides an iterator for all Python source files defined in paths.""" 12 | visited_dirs: Set[Path] = set() 13 | 14 | for path in paths: 15 | if os.path.isdir(path): 16 | for dirpath, dirnames, filenames in os.walk( 17 | path, topdown=True, followlinks=config.follow_links 18 | ): 19 | base_path = Path(dirpath) 20 | for dirname in list(dirnames): 21 | full_path = base_path / dirname 22 | resolved_path = full_path.resolve() 23 | if config.is_skipped(full_path): 24 | skipped.append(dirname) 25 | dirnames.remove(dirname) 26 | else: 27 | if resolved_path in visited_dirs: # pragma: no cover 28 | dirnames.remove(dirname) 29 | visited_dirs.add(resolved_path) 30 | 31 | for filename in filenames: 32 | filepath = os.path.join(dirpath, filename) 33 | if config.is_supported_filetype(filepath): 34 | if config.is_skipped(Path(os.path.abspath(filepath))): 35 | skipped.append(filename) 36 | else: 37 | yield filepath 38 | elif not os.path.exists(path): 39 | broken.append(path) 40 | else: 41 | yield path 42 | -------------------------------------------------------------------------------- /isort/hooks.py: -------------------------------------------------------------------------------- 1 | """Defines a git hook to allow pre-commit warnings and errors about import order. 2 | 3 | usage: 4 | exit_code = git_hook(strict=True|False, modify=True|False) 5 | """ 6 | 7 | import os 8 | import subprocess # nosec 9 | from pathlib import Path 10 | from typing import List, Optional 11 | 12 | from isort import Config, api, exceptions 13 | 14 | 15 | def get_output(command: List[str]) -> str: 16 | """Run a command and return raw output 17 | 18 | :param str command: the command to run 19 | :returns: the stdout output of the command 20 | """ 21 | result = subprocess.run(command, stdout=subprocess.PIPE, check=True) # nosec 22 | return result.stdout.decode() 23 | 24 | 25 | def get_lines(command: List[str]) -> List[str]: 26 | """Run a command and return lines of output 27 | 28 | :param str command: the command to run 29 | :returns: list of whitespace-stripped lines output by command 30 | """ 31 | stdout = get_output(command) 32 | return [line.strip() for line in stdout.splitlines()] 33 | 34 | 35 | def git_hook( 36 | strict: bool = False, 37 | modify: bool = False, 38 | lazy: bool = False, 39 | settings_file: str = "", 40 | directories: Optional[List[str]] = None, 41 | ) -> int: 42 | """Git pre-commit hook to check staged files for isort errors 43 | 44 | :param bool strict - if True, return number of errors on exit, 45 | causing the hook to fail. If False, return zero so it will 46 | just act as a warning. 47 | :param bool modify - if True, fix the sources if they are not 48 | sorted properly. If False, only report result without 49 | modifying anything. 50 | :param bool lazy - if True, also check/fix unstaged files. 51 | This is useful if you frequently use ``git commit -a`` for example. 52 | If False, only check/fix the staged files for isort errors. 53 | :param str settings_file - A path to a file to be used as 54 | the configuration file for this run. 55 | When settings_file is the empty string, the configuration file 56 | will be searched starting at the directory containing the first 57 | staged file, if any, and going upward in the directory structure. 58 | :param list[str] directories - A list of directories to restrict the hook to. 59 | 60 | :return number of errors if in strict mode, 0 otherwise. 61 | """ 62 | # Get list of files modified and staged 63 | diff_cmd = ["git", "diff-index", "--cached", "--name-only", "--diff-filter=ACMRTUXB", "HEAD"] 64 | if lazy: 65 | diff_cmd.remove("--cached") 66 | if directories: 67 | diff_cmd.extend(directories) 68 | 69 | files_modified = get_lines(diff_cmd) 70 | if not files_modified: 71 | return 0 72 | 73 | errors = 0 74 | config = Config( 75 | settings_file=settings_file, 76 | settings_path=os.path.dirname(os.path.abspath(files_modified[0])), 77 | ) 78 | for filename in files_modified: 79 | if filename.endswith(".py"): 80 | # Get the staged contents of the file 81 | staged_cmd = ["git", "show", f":{filename}"] 82 | staged_contents = get_output(staged_cmd) 83 | 84 | try: 85 | if not api.check_code_string( 86 | staged_contents, file_path=Path(filename), config=config 87 | ): 88 | errors += 1 89 | if modify: 90 | api.sort_file(filename, config=config) 91 | except exceptions.FileSkipped: # pragma: no cover 92 | pass 93 | 94 | return errors if strict else 0 95 | -------------------------------------------------------------------------------- /isort/io.py: -------------------------------------------------------------------------------- 1 | """Defines any IO utilities used by isort""" 2 | 3 | import dataclasses 4 | import re 5 | import tokenize 6 | from contextlib import contextmanager 7 | from io import BytesIO, StringIO, TextIOWrapper 8 | from pathlib import Path 9 | from typing import Any, Callable, Iterator, TextIO, Union 10 | 11 | from isort.exceptions import UnsupportedEncoding 12 | 13 | _ENCODING_PATTERN = re.compile(rb"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)") 14 | 15 | 16 | @dataclasses.dataclass(frozen=True) 17 | class File: 18 | stream: TextIO 19 | path: Path 20 | encoding: str 21 | 22 | @staticmethod 23 | def detect_encoding(filename: Union[str, Path], readline: Callable[[], bytes]) -> str: 24 | try: 25 | return tokenize.detect_encoding(readline)[0] 26 | except Exception: 27 | raise UnsupportedEncoding(filename) 28 | 29 | @staticmethod 30 | def from_contents(contents: str, filename: str) -> "File": 31 | encoding = File.detect_encoding(filename, BytesIO(contents.encode("utf-8")).readline) 32 | return File(stream=StringIO(contents), path=Path(filename).resolve(), encoding=encoding) 33 | 34 | @property 35 | def extension(self) -> str: 36 | return self.path.suffix.lstrip(".") 37 | 38 | @staticmethod 39 | def _open(filename: Union[str, Path]) -> TextIOWrapper: 40 | """Open a file in read only mode using the encoding detected by 41 | detect_encoding(). 42 | """ 43 | buffer = open(filename, "rb") 44 | try: 45 | encoding = File.detect_encoding(filename, buffer.readline) 46 | buffer.seek(0) 47 | text = TextIOWrapper(buffer, encoding, line_buffering=True, newline="") 48 | text.mode = "r" # type: ignore 49 | return text 50 | except Exception: 51 | buffer.close() 52 | raise 53 | 54 | @staticmethod 55 | @contextmanager 56 | def read(filename: Union[str, Path]) -> Iterator["File"]: 57 | file_path = Path(filename).resolve() 58 | stream = None 59 | try: 60 | stream = File._open(file_path) 61 | yield File(stream=stream, path=file_path, encoding=stream.encoding) 62 | finally: 63 | if stream is not None: 64 | stream.close() 65 | 66 | 67 | class _EmptyIO(StringIO): 68 | def write(self, *args: Any, **kwargs: Any) -> None: # type: ignore # skipcq: PTC-W0049 69 | pass 70 | 71 | 72 | Empty = _EmptyIO() 73 | -------------------------------------------------------------------------------- /isort/literal.py: -------------------------------------------------------------------------------- 1 | import ast 2 | from pprint import PrettyPrinter 3 | from typing import Any, Callable, Dict, List, Set, Tuple 4 | 5 | from isort.exceptions import ( 6 | AssignmentsFormatMismatch, 7 | LiteralParsingFailure, 8 | LiteralSortTypeMismatch, 9 | ) 10 | from isort.settings import DEFAULT_CONFIG, Config 11 | 12 | 13 | class ISortPrettyPrinter(PrettyPrinter): 14 | """an isort customized pretty printer for sorted literals""" 15 | 16 | def __init__(self, config: Config): 17 | super().__init__(width=config.line_length, compact=True) 18 | 19 | 20 | type_mapping: Dict[str, Tuple[type, Callable[[Any, ISortPrettyPrinter], str]]] = {} 21 | 22 | 23 | def assignments(code: str) -> str: 24 | values = {} 25 | for line in code.splitlines(keepends=True): 26 | if not line.strip(): 27 | continue 28 | if " = " not in line: 29 | raise AssignmentsFormatMismatch(code) 30 | variable_name, value = line.split(" = ", 1) 31 | values[variable_name] = value 32 | 33 | return "".join( 34 | f"{variable_name} = {values[variable_name]}" for variable_name in sorted(values.keys()) 35 | ) 36 | 37 | 38 | def assignment(code: str, sort_type: str, extension: str, config: Config = DEFAULT_CONFIG) -> str: 39 | """Sorts the literal present within the provided code against the provided sort type, 40 | returning the sorted representation of the source code. 41 | """ 42 | if sort_type == "assignments": 43 | return assignments(code) 44 | if sort_type not in type_mapping: 45 | raise ValueError( 46 | "Trying to sort using an undefined sort_type. " 47 | f"Defined sort types are {', '.join(type_mapping.keys())}." 48 | ) 49 | 50 | variable_name, literal = code.split("=") 51 | variable_name = variable_name.strip() 52 | literal = literal.lstrip() 53 | try: 54 | value = ast.literal_eval(literal) 55 | except Exception as error: 56 | raise LiteralParsingFailure(code, error) 57 | 58 | expected_type, sort_function = type_mapping[sort_type] 59 | if type(value) is not expected_type: 60 | raise LiteralSortTypeMismatch(type(value), expected_type) 61 | 62 | printer = ISortPrettyPrinter(config) 63 | sorted_value_code = f"{variable_name} = {sort_function(value, printer)}" 64 | if config.formatting_function: 65 | sorted_value_code = config.formatting_function( 66 | sorted_value_code, extension, config 67 | ).rstrip() 68 | 69 | sorted_value_code += code[len(code.rstrip()) :] 70 | return sorted_value_code 71 | 72 | 73 | def register_type( 74 | name: str, kind: type 75 | ) -> Callable[[Callable[[Any, ISortPrettyPrinter], str]], Callable[[Any, ISortPrettyPrinter], str]]: 76 | """Registers a new literal sort type.""" 77 | 78 | def wrap( 79 | function: Callable[[Any, ISortPrettyPrinter], str] 80 | ) -> Callable[[Any, ISortPrettyPrinter], str]: 81 | type_mapping[name] = (kind, function) 82 | return function 83 | 84 | return wrap 85 | 86 | 87 | @register_type("dict", dict) 88 | def _dict(value: Dict[Any, Any], printer: ISortPrettyPrinter) -> str: 89 | return printer.pformat(dict(sorted(value.items(), key=lambda item: item[1]))) 90 | 91 | 92 | @register_type("list", list) 93 | def _list(value: List[Any], printer: ISortPrettyPrinter) -> str: 94 | return printer.pformat(sorted(value)) 95 | 96 | 97 | @register_type("unique-list", list) 98 | def _unique_list(value: List[Any], printer: ISortPrettyPrinter) -> str: 99 | return printer.pformat(sorted(set(value))) 100 | 101 | 102 | @register_type("set", set) 103 | def _set(value: Set[Any], printer: ISortPrettyPrinter) -> str: 104 | return "{" + printer.pformat(tuple(sorted(value)))[1:-1] + "}" 105 | 106 | 107 | @register_type("tuple", tuple) 108 | def _tuple(value: Tuple[Any, ...], printer: ISortPrettyPrinter) -> str: 109 | return printer.pformat(tuple(sorted(value))) 110 | 111 | 112 | @register_type("unique-tuple", tuple) 113 | def _unique_tuple(value: Tuple[Any, ...], printer: ISortPrettyPrinter) -> str: 114 | return printer.pformat(tuple(sorted(set(value)))) 115 | -------------------------------------------------------------------------------- /isort/logo.py: -------------------------------------------------------------------------------- 1 | from ._version import __version__ 2 | 3 | ASCII_ART = rf""" 4 | _ _ 5 | (_) ___ ___ _ __| |_ 6 | | |/ _/ / _ \/ '__ _/ 7 | | |\__ \/\_\/| | | |_ 8 | |_|\___/\___/\_/ \_/ 9 | 10 | isort your imports, so you don't have to. 11 | 12 | VERSION {__version__} 13 | """ 14 | 15 | __doc__ = f""" 16 | ```python 17 | {ASCII_ART} 18 | ``` 19 | """ 20 | -------------------------------------------------------------------------------- /isort/profiles.py: -------------------------------------------------------------------------------- 1 | """Common profiles are defined here to be easily used within a project using --profile {name}""" 2 | 3 | from typing import Any, Dict 4 | 5 | black = { 6 | "multi_line_output": 3, 7 | "include_trailing_comma": True, 8 | "split_on_trailing_comma": True, 9 | "force_grid_wrap": 0, 10 | "use_parentheses": True, 11 | "ensure_newline_before_comments": True, 12 | "line_length": 88, 13 | } 14 | django = { 15 | "combine_as_imports": True, 16 | "include_trailing_comma": True, 17 | "multi_line_output": 5, 18 | "line_length": 79, 19 | } 20 | pycharm = { 21 | "multi_line_output": 3, 22 | "force_grid_wrap": 2, 23 | "lines_after_imports": 2, 24 | } 25 | google = { 26 | "force_single_line": True, 27 | "force_sort_within_sections": True, 28 | "lexicographical": True, 29 | "line_length": 1000, 30 | "single_line_exclusions": ( 31 | "collections.abc", 32 | "six.moves", 33 | "typing", 34 | "typing_extensions", 35 | ), 36 | "order_by_type": False, 37 | "group_by_package": True, 38 | } 39 | open_stack = { 40 | "force_single_line": True, 41 | "force_sort_within_sections": True, 42 | "lexicographical": True, 43 | } 44 | plone = black.copy() 45 | plone.update( 46 | { 47 | "force_alphabetical_sort": True, 48 | "force_single_line": True, 49 | "lines_after_imports": 2, 50 | } 51 | ) 52 | attrs = { 53 | "atomic": True, 54 | "force_grid_wrap": 0, 55 | "include_trailing_comma": True, 56 | "lines_after_imports": 2, 57 | "lines_between_types": 1, 58 | "multi_line_output": 3, 59 | "use_parentheses": True, 60 | } 61 | hug = { 62 | "multi_line_output": 3, 63 | "include_trailing_comma": True, 64 | "force_grid_wrap": 0, 65 | "use_parentheses": True, 66 | "line_length": 100, 67 | } 68 | wemake = { 69 | "multi_line_output": 3, 70 | "include_trailing_comma": True, 71 | "use_parentheses": True, 72 | "line_length": 80, 73 | } 74 | appnexus = { 75 | **black, 76 | "force_sort_within_sections": True, 77 | "order_by_type": False, 78 | "case_sensitive": False, 79 | "reverse_relative": True, 80 | "sort_relative_in_force_sorted_sections": True, 81 | "sections": ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "APPLICATION", "LOCALFOLDER"], 82 | "no_lines_before": "LOCALFOLDER", 83 | } 84 | 85 | profiles: Dict[str, Dict[str, Any]] = { 86 | "black": black, 87 | "django": django, 88 | "pycharm": pycharm, 89 | "google": google, 90 | "open_stack": open_stack, 91 | "plone": plone, 92 | "attrs": attrs, 93 | "hug": hug, 94 | "wemake": wemake, 95 | "appnexus": appnexus, 96 | } 97 | -------------------------------------------------------------------------------- /isort/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/isort/py.typed -------------------------------------------------------------------------------- /isort/pylama_isort.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from contextlib import contextmanager 4 | from typing import Any, Dict, Iterator, List, Optional 5 | 6 | from pylama.lint import Linter as BaseLinter # type: ignore 7 | 8 | from isort.exceptions import FileSkipped 9 | 10 | from . import api 11 | 12 | 13 | @contextmanager 14 | def suppress_stdout() -> Iterator[None]: 15 | stdout = sys.stdout 16 | with open(os.devnull, "w") as devnull: 17 | sys.stdout = devnull 18 | yield 19 | sys.stdout = stdout 20 | 21 | 22 | class Linter(BaseLinter): # type: ignore 23 | def allow(self, path: str) -> bool: 24 | """Determine if this path should be linted.""" 25 | return path.endswith(".py") 26 | 27 | def run( 28 | self, path: str, params: Optional[Dict[str, Any]] = None, **meta: Any 29 | ) -> List[Dict[str, Any]]: 30 | """Lint the file. Return an array of error dicts if appropriate.""" 31 | with suppress_stdout(): 32 | try: 33 | if not api.check_file(path, disregard_skip=False, **params or {}): 34 | return [ 35 | { 36 | "lnum": 0, 37 | "col": 0, 38 | "text": "Incorrectly sorted imports.", 39 | "type": "ISORT", 40 | } 41 | ] 42 | except FileSkipped: 43 | pass 44 | 45 | return [] 46 | -------------------------------------------------------------------------------- /isort/sections.py: -------------------------------------------------------------------------------- 1 | """Defines all sections isort uses by default""" 2 | 3 | from typing import Tuple 4 | 5 | FUTURE: str = "FUTURE" 6 | STDLIB: str = "STDLIB" 7 | THIRDPARTY: str = "THIRDPARTY" 8 | FIRSTPARTY: str = "FIRSTPARTY" 9 | LOCALFOLDER: str = "LOCALFOLDER" 10 | DEFAULT: Tuple[str, ...] = (FUTURE, STDLIB, THIRDPARTY, FIRSTPARTY, LOCALFOLDER) 11 | -------------------------------------------------------------------------------- /isort/setuptools_commands.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import sys 4 | from typing import Any, Iterator 5 | from warnings import warn 6 | 7 | import setuptools 8 | 9 | from . import api 10 | from .settings import DEFAULT_CONFIG 11 | 12 | 13 | class ISortCommand(setuptools.Command): 14 | """The :class:`ISortCommand` class is used by setuptools to perform 15 | imports checks on registered modules. 16 | """ 17 | 18 | description = "Run isort on modules registered in setuptools" 19 | # Potentially unused variable - check if can be safely removed 20 | user_options: list[Any] = [] # type: ignore[misc] 21 | 22 | def initialize_options(self) -> None: 23 | default_settings = vars(DEFAULT_CONFIG).copy() 24 | for key, value in default_settings.items(): 25 | setattr(self, key, value) 26 | 27 | def finalize_options(self) -> None: 28 | """Get options from config files.""" 29 | self.arguments: dict[str, Any] = {} # skipcq: PYL-W0201 30 | self.arguments["settings_path"] = os.getcwd() 31 | 32 | def distribution_files(self) -> Iterator[str]: 33 | """Find distribution packages.""" 34 | # This is verbatim from flake8 35 | if self.distribution.packages: # pragma: no cover 36 | package_dirs = self.distribution.package_dir or {} 37 | for package in self.distribution.packages: 38 | pkg_dir = package 39 | if package in package_dirs: 40 | pkg_dir = package_dirs[package] 41 | elif "" in package_dirs: # pragma: no cover 42 | pkg_dir = package_dirs[""] + os.path.sep + pkg_dir 43 | yield pkg_dir.replace(".", os.path.sep) 44 | 45 | if self.distribution.py_modules: 46 | for filename in self.distribution.py_modules: 47 | yield f"{filename}.py" 48 | # Don't miss the setup.py file itself 49 | yield "setup.py" 50 | 51 | def run(self) -> None: 52 | arguments = self.arguments 53 | wrong_sorted_files = False 54 | for path in self.distribution_files(): 55 | for python_file in glob.iglob(os.path.join(path, "*.py")): 56 | try: 57 | if not api.check_file(python_file, **arguments): 58 | wrong_sorted_files = True # pragma: no cover 59 | except OSError as error: # pragma: no cover 60 | warn(f"Unable to parse file {python_file} due to {error}", stacklevel=2) 61 | if wrong_sorted_files: 62 | sys.exit(1) # pragma: no cover 63 | -------------------------------------------------------------------------------- /isort/stdlibs/__init__.py: -------------------------------------------------------------------------------- 1 | from . import all as _all 2 | from . import py2, py3, py27, py36, py37, py38, py39, py310, py311, py312, py313 3 | 4 | __all__ = ( 5 | "_all", 6 | "py2", 7 | "py3", 8 | "py27", 9 | "py36", 10 | "py37", 11 | "py38", 12 | "py39", 13 | "py310", 14 | "py311", 15 | "py312", 16 | "py313", 17 | ) 18 | -------------------------------------------------------------------------------- /isort/stdlibs/all.py: -------------------------------------------------------------------------------- 1 | from . import py2, py3 2 | 3 | stdlib = py2.stdlib | py3.stdlib 4 | -------------------------------------------------------------------------------- /isort/stdlibs/py2.py: -------------------------------------------------------------------------------- 1 | from . import py27 2 | 3 | stdlib = py27.stdlib 4 | -------------------------------------------------------------------------------- /isort/stdlibs/py3.py: -------------------------------------------------------------------------------- 1 | from . import py36, py37, py38, py39, py310, py311, py312, py313 2 | 3 | stdlib = ( 4 | py36.stdlib 5 | | py37.stdlib 6 | | py38.stdlib 7 | | py39.stdlib 8 | | py310.stdlib 9 | | py311.stdlib 10 | | py312.stdlib 11 | | py313.stdlib 12 | ) 13 | -------------------------------------------------------------------------------- /isort/stdlibs/py310.py: -------------------------------------------------------------------------------- 1 | """ 2 | File contains the standard library of Python 3.10. 3 | 4 | DO NOT EDIT. If the standard library changes, a new list should be created 5 | using the mkstdlibs.py script. 6 | """ 7 | 8 | stdlib = { 9 | "_ast", 10 | "abc", 11 | "aifc", 12 | "antigravity", 13 | "argparse", 14 | "array", 15 | "ast", 16 | "asynchat", 17 | "asyncio", 18 | "asyncore", 19 | "atexit", 20 | "audioop", 21 | "base64", 22 | "bdb", 23 | "binascii", 24 | "binhex", 25 | "bisect", 26 | "builtins", 27 | "bz2", 28 | "cProfile", 29 | "calendar", 30 | "cgi", 31 | "cgitb", 32 | "chunk", 33 | "cmath", 34 | "cmd", 35 | "code", 36 | "codecs", 37 | "codeop", 38 | "collections", 39 | "colorsys", 40 | "compileall", 41 | "concurrent", 42 | "configparser", 43 | "contextlib", 44 | "contextvars", 45 | "copy", 46 | "copyreg", 47 | "crypt", 48 | "csv", 49 | "ctypes", 50 | "curses", 51 | "dataclasses", 52 | "datetime", 53 | "dbm", 54 | "decimal", 55 | "difflib", 56 | "dis", 57 | "distutils", 58 | "doctest", 59 | "email", 60 | "encodings", 61 | "ensurepip", 62 | "enum", 63 | "errno", 64 | "faulthandler", 65 | "fcntl", 66 | "filecmp", 67 | "fileinput", 68 | "fnmatch", 69 | "fractions", 70 | "ftplib", 71 | "functools", 72 | "gc", 73 | "genericpath", 74 | "getopt", 75 | "getpass", 76 | "gettext", 77 | "glob", 78 | "graphlib", 79 | "grp", 80 | "gzip", 81 | "hashlib", 82 | "heapq", 83 | "hmac", 84 | "html", 85 | "http", 86 | "idlelib", 87 | "imaplib", 88 | "imghdr", 89 | "imp", 90 | "importlib", 91 | "inspect", 92 | "io", 93 | "ipaddress", 94 | "itertools", 95 | "json", 96 | "keyword", 97 | "lib2to3", 98 | "linecache", 99 | "locale", 100 | "logging", 101 | "lzma", 102 | "mailbox", 103 | "mailcap", 104 | "marshal", 105 | "math", 106 | "mimetypes", 107 | "mmap", 108 | "modulefinder", 109 | "msilib", 110 | "msvcrt", 111 | "multiprocessing", 112 | "netrc", 113 | "nis", 114 | "nntplib", 115 | "nt", 116 | "ntpath", 117 | "nturl2path", 118 | "numbers", 119 | "opcode", 120 | "operator", 121 | "optparse", 122 | "os", 123 | "ossaudiodev", 124 | "pathlib", 125 | "pdb", 126 | "pickle", 127 | "pickletools", 128 | "pipes", 129 | "pkgutil", 130 | "platform", 131 | "plistlib", 132 | "poplib", 133 | "posix", 134 | "posixpath", 135 | "pprint", 136 | "profile", 137 | "pstats", 138 | "pty", 139 | "pwd", 140 | "py_compile", 141 | "pyclbr", 142 | "pydoc", 143 | "pydoc_data", 144 | "pyexpat", 145 | "queue", 146 | "quopri", 147 | "random", 148 | "re", 149 | "readline", 150 | "reprlib", 151 | "resource", 152 | "rlcompleter", 153 | "runpy", 154 | "sched", 155 | "secrets", 156 | "select", 157 | "selectors", 158 | "shelve", 159 | "shlex", 160 | "shutil", 161 | "signal", 162 | "site", 163 | "smtpd", 164 | "smtplib", 165 | "sndhdr", 166 | "socket", 167 | "socketserver", 168 | "spwd", 169 | "sqlite3", 170 | "sre", 171 | "sre_compile", 172 | "sre_constants", 173 | "sre_parse", 174 | "ssl", 175 | "stat", 176 | "statistics", 177 | "string", 178 | "stringprep", 179 | "struct", 180 | "subprocess", 181 | "sunau", 182 | "symtable", 183 | "sys", 184 | "sysconfig", 185 | "syslog", 186 | "tabnanny", 187 | "tarfile", 188 | "telnetlib", 189 | "tempfile", 190 | "termios", 191 | "textwrap", 192 | "this", 193 | "threading", 194 | "time", 195 | "timeit", 196 | "tkinter", 197 | "token", 198 | "tokenize", 199 | "trace", 200 | "traceback", 201 | "tracemalloc", 202 | "tty", 203 | "turtle", 204 | "turtledemo", 205 | "types", 206 | "typing", 207 | "unicodedata", 208 | "unittest", 209 | "urllib", 210 | "uu", 211 | "uuid", 212 | "venv", 213 | "warnings", 214 | "wave", 215 | "weakref", 216 | "webbrowser", 217 | "winreg", 218 | "winsound", 219 | "wsgiref", 220 | "xdrlib", 221 | "xml", 222 | "xmlrpc", 223 | "xx", 224 | "xxlimited", 225 | "xxlimited_35", 226 | "xxsubtype", 227 | "zipapp", 228 | "zipfile", 229 | "zipimport", 230 | "zlib", 231 | "zoneinfo", 232 | } 233 | -------------------------------------------------------------------------------- /isort/stdlibs/py311.py: -------------------------------------------------------------------------------- 1 | """ 2 | File contains the standard library of Python 3.11. 3 | 4 | DO NOT EDIT. If the standard library changes, a new list should be created 5 | using the mkstdlibs.py script. 6 | """ 7 | 8 | stdlib = { 9 | "_ast", 10 | "abc", 11 | "aifc", 12 | "antigravity", 13 | "argparse", 14 | "array", 15 | "ast", 16 | "asynchat", 17 | "asyncio", 18 | "asyncore", 19 | "atexit", 20 | "audioop", 21 | "base64", 22 | "bdb", 23 | "binascii", 24 | "bisect", 25 | "builtins", 26 | "bz2", 27 | "cProfile", 28 | "calendar", 29 | "cgi", 30 | "cgitb", 31 | "chunk", 32 | "cmath", 33 | "cmd", 34 | "code", 35 | "codecs", 36 | "codeop", 37 | "collections", 38 | "colorsys", 39 | "compileall", 40 | "concurrent", 41 | "configparser", 42 | "contextlib", 43 | "contextvars", 44 | "copy", 45 | "copyreg", 46 | "crypt", 47 | "csv", 48 | "ctypes", 49 | "curses", 50 | "dataclasses", 51 | "datetime", 52 | "dbm", 53 | "decimal", 54 | "difflib", 55 | "dis", 56 | "distutils", 57 | "doctest", 58 | "email", 59 | "encodings", 60 | "ensurepip", 61 | "enum", 62 | "errno", 63 | "faulthandler", 64 | "fcntl", 65 | "filecmp", 66 | "fileinput", 67 | "fnmatch", 68 | "fractions", 69 | "ftplib", 70 | "functools", 71 | "gc", 72 | "genericpath", 73 | "getopt", 74 | "getpass", 75 | "gettext", 76 | "glob", 77 | "graphlib", 78 | "grp", 79 | "gzip", 80 | "hashlib", 81 | "heapq", 82 | "hmac", 83 | "html", 84 | "http", 85 | "idlelib", 86 | "imaplib", 87 | "imghdr", 88 | "imp", 89 | "importlib", 90 | "inspect", 91 | "io", 92 | "ipaddress", 93 | "itertools", 94 | "json", 95 | "keyword", 96 | "lib2to3", 97 | "linecache", 98 | "locale", 99 | "logging", 100 | "lzma", 101 | "mailbox", 102 | "mailcap", 103 | "marshal", 104 | "math", 105 | "mimetypes", 106 | "mmap", 107 | "modulefinder", 108 | "msilib", 109 | "msvcrt", 110 | "multiprocessing", 111 | "netrc", 112 | "nis", 113 | "nntplib", 114 | "nt", 115 | "ntpath", 116 | "nturl2path", 117 | "numbers", 118 | "opcode", 119 | "operator", 120 | "optparse", 121 | "os", 122 | "ossaudiodev", 123 | "pathlib", 124 | "pdb", 125 | "pickle", 126 | "pickletools", 127 | "pipes", 128 | "pkgutil", 129 | "platform", 130 | "plistlib", 131 | "poplib", 132 | "posix", 133 | "posixpath", 134 | "pprint", 135 | "profile", 136 | "pstats", 137 | "pty", 138 | "pwd", 139 | "py_compile", 140 | "pyclbr", 141 | "pydoc", 142 | "pydoc_data", 143 | "pyexpat", 144 | "queue", 145 | "quopri", 146 | "random", 147 | "re", 148 | "readline", 149 | "reprlib", 150 | "resource", 151 | "rlcompleter", 152 | "runpy", 153 | "sched", 154 | "secrets", 155 | "select", 156 | "selectors", 157 | "shelve", 158 | "shlex", 159 | "shutil", 160 | "signal", 161 | "site", 162 | "smtpd", 163 | "smtplib", 164 | "sndhdr", 165 | "socket", 166 | "socketserver", 167 | "spwd", 168 | "sqlite3", 169 | "sre", 170 | "sre_compile", 171 | "sre_constants", 172 | "sre_parse", 173 | "ssl", 174 | "stat", 175 | "statistics", 176 | "string", 177 | "stringprep", 178 | "struct", 179 | "subprocess", 180 | "sunau", 181 | "symtable", 182 | "sys", 183 | "sysconfig", 184 | "syslog", 185 | "tabnanny", 186 | "tarfile", 187 | "telnetlib", 188 | "tempfile", 189 | "termios", 190 | "textwrap", 191 | "this", 192 | "threading", 193 | "time", 194 | "timeit", 195 | "tkinter", 196 | "token", 197 | "tokenize", 198 | "tomllib", 199 | "trace", 200 | "traceback", 201 | "tracemalloc", 202 | "tty", 203 | "turtle", 204 | "turtledemo", 205 | "types", 206 | "typing", 207 | "unicodedata", 208 | "unittest", 209 | "urllib", 210 | "uu", 211 | "uuid", 212 | "venv", 213 | "warnings", 214 | "wave", 215 | "weakref", 216 | "webbrowser", 217 | "winreg", 218 | "winsound", 219 | "wsgiref", 220 | "xdrlib", 221 | "xml", 222 | "xmlrpc", 223 | "xx", 224 | "xxlimited", 225 | "xxlimited_35", 226 | "xxsubtype", 227 | "zipapp", 228 | "zipfile", 229 | "zipimport", 230 | "zlib", 231 | "zoneinfo", 232 | } 233 | -------------------------------------------------------------------------------- /isort/stdlibs/py312.py: -------------------------------------------------------------------------------- 1 | """ 2 | File contains the standard library of Python 3.12. 3 | 4 | DO NOT EDIT. If the standard library changes, a new list should be created 5 | using the mkstdlibs.py script. 6 | """ 7 | 8 | stdlib = { 9 | "_ast", 10 | "abc", 11 | "aifc", 12 | "antigravity", 13 | "argparse", 14 | "array", 15 | "ast", 16 | "asyncio", 17 | "atexit", 18 | "audioop", 19 | "base64", 20 | "bdb", 21 | "binascii", 22 | "bisect", 23 | "builtins", 24 | "bz2", 25 | "cProfile", 26 | "calendar", 27 | "cgi", 28 | "cgitb", 29 | "chunk", 30 | "cmath", 31 | "cmd", 32 | "code", 33 | "codecs", 34 | "codeop", 35 | "collections", 36 | "colorsys", 37 | "compileall", 38 | "concurrent", 39 | "configparser", 40 | "contextlib", 41 | "contextvars", 42 | "copy", 43 | "copyreg", 44 | "crypt", 45 | "csv", 46 | "ctypes", 47 | "curses", 48 | "dataclasses", 49 | "datetime", 50 | "dbm", 51 | "decimal", 52 | "difflib", 53 | "dis", 54 | "doctest", 55 | "email", 56 | "encodings", 57 | "ensurepip", 58 | "enum", 59 | "errno", 60 | "faulthandler", 61 | "fcntl", 62 | "filecmp", 63 | "fileinput", 64 | "fnmatch", 65 | "fractions", 66 | "ftplib", 67 | "functools", 68 | "gc", 69 | "genericpath", 70 | "getopt", 71 | "getpass", 72 | "gettext", 73 | "glob", 74 | "graphlib", 75 | "grp", 76 | "gzip", 77 | "hashlib", 78 | "heapq", 79 | "hmac", 80 | "html", 81 | "http", 82 | "idlelib", 83 | "imaplib", 84 | "imghdr", 85 | "importlib", 86 | "inspect", 87 | "io", 88 | "ipaddress", 89 | "itertools", 90 | "json", 91 | "keyword", 92 | "lib2to3", 93 | "linecache", 94 | "locale", 95 | "logging", 96 | "lzma", 97 | "mailbox", 98 | "mailcap", 99 | "marshal", 100 | "math", 101 | "mimetypes", 102 | "mmap", 103 | "modulefinder", 104 | "msilib", 105 | "msvcrt", 106 | "multiprocessing", 107 | "netrc", 108 | "nis", 109 | "nntplib", 110 | "nt", 111 | "ntpath", 112 | "nturl2path", 113 | "numbers", 114 | "opcode", 115 | "operator", 116 | "optparse", 117 | "os", 118 | "ossaudiodev", 119 | "pathlib", 120 | "pdb", 121 | "pickle", 122 | "pickletools", 123 | "pipes", 124 | "pkgutil", 125 | "platform", 126 | "plistlib", 127 | "poplib", 128 | "posix", 129 | "posixpath", 130 | "pprint", 131 | "profile", 132 | "pstats", 133 | "pty", 134 | "pwd", 135 | "py_compile", 136 | "pyclbr", 137 | "pydoc", 138 | "pydoc_data", 139 | "pyexpat", 140 | "queue", 141 | "quopri", 142 | "random", 143 | "re", 144 | "readline", 145 | "reprlib", 146 | "resource", 147 | "rlcompleter", 148 | "runpy", 149 | "sched", 150 | "secrets", 151 | "select", 152 | "selectors", 153 | "shelve", 154 | "shlex", 155 | "shutil", 156 | "signal", 157 | "site", 158 | "smtplib", 159 | "sndhdr", 160 | "socket", 161 | "socketserver", 162 | "spwd", 163 | "sqlite3", 164 | "sre", 165 | "sre_compile", 166 | "sre_constants", 167 | "sre_parse", 168 | "ssl", 169 | "stat", 170 | "statistics", 171 | "string", 172 | "stringprep", 173 | "struct", 174 | "subprocess", 175 | "sunau", 176 | "symtable", 177 | "sys", 178 | "sysconfig", 179 | "syslog", 180 | "tabnanny", 181 | "tarfile", 182 | "telnetlib", 183 | "tempfile", 184 | "termios", 185 | "textwrap", 186 | "this", 187 | "threading", 188 | "time", 189 | "timeit", 190 | "tkinter", 191 | "token", 192 | "tokenize", 193 | "tomllib", 194 | "trace", 195 | "traceback", 196 | "tracemalloc", 197 | "tty", 198 | "turtle", 199 | "turtledemo", 200 | "types", 201 | "typing", 202 | "unicodedata", 203 | "unittest", 204 | "urllib", 205 | "uu", 206 | "uuid", 207 | "venv", 208 | "warnings", 209 | "wave", 210 | "weakref", 211 | "webbrowser", 212 | "winreg", 213 | "winsound", 214 | "wsgiref", 215 | "xdrlib", 216 | "xml", 217 | "xmlrpc", 218 | "xx", 219 | "xxlimited", 220 | "xxlimited_35", 221 | "xxsubtype", 222 | "zipapp", 223 | "zipfile", 224 | "zipimport", 225 | "zlib", 226 | "zoneinfo", 227 | } 228 | -------------------------------------------------------------------------------- /isort/stdlibs/py313.py: -------------------------------------------------------------------------------- 1 | """ 2 | File contains the standard library of Python 3.13. 3 | 4 | DO NOT EDIT. If the standard library changes, a new list should be created 5 | using the mkstdlibs.py script. 6 | """ 7 | 8 | stdlib = { 9 | "_ast", 10 | "abc", 11 | "antigravity", 12 | "argparse", 13 | "array", 14 | "ast", 15 | "asyncio", 16 | "atexit", 17 | "base64", 18 | "bdb", 19 | "binascii", 20 | "bisect", 21 | "builtins", 22 | "bz2", 23 | "cProfile", 24 | "calendar", 25 | "cmath", 26 | "cmd", 27 | "code", 28 | "codecs", 29 | "codeop", 30 | "collections", 31 | "colorsys", 32 | "compileall", 33 | "concurrent", 34 | "configparser", 35 | "contextlib", 36 | "contextvars", 37 | "copy", 38 | "copyreg", 39 | "csv", 40 | "ctypes", 41 | "curses", 42 | "dataclasses", 43 | "datetime", 44 | "dbm", 45 | "decimal", 46 | "difflib", 47 | "dis", 48 | "doctest", 49 | "email", 50 | "encodings", 51 | "ensurepip", 52 | "enum", 53 | "errno", 54 | "faulthandler", 55 | "fcntl", 56 | "filecmp", 57 | "fileinput", 58 | "fnmatch", 59 | "fractions", 60 | "ftplib", 61 | "functools", 62 | "gc", 63 | "genericpath", 64 | "getopt", 65 | "getpass", 66 | "gettext", 67 | "glob", 68 | "graphlib", 69 | "grp", 70 | "gzip", 71 | "hashlib", 72 | "heapq", 73 | "hmac", 74 | "html", 75 | "http", 76 | "idlelib", 77 | "imaplib", 78 | "importlib", 79 | "inspect", 80 | "io", 81 | "ipaddress", 82 | "itertools", 83 | "json", 84 | "keyword", 85 | "linecache", 86 | "locale", 87 | "logging", 88 | "lzma", 89 | "mailbox", 90 | "marshal", 91 | "math", 92 | "mimetypes", 93 | "mmap", 94 | "modulefinder", 95 | "msvcrt", 96 | "multiprocessing", 97 | "netrc", 98 | "nt", 99 | "ntpath", 100 | "nturl2path", 101 | "numbers", 102 | "opcode", 103 | "operator", 104 | "optparse", 105 | "os", 106 | "pathlib", 107 | "pdb", 108 | "pickle", 109 | "pickletools", 110 | "pkgutil", 111 | "platform", 112 | "plistlib", 113 | "poplib", 114 | "posix", 115 | "posixpath", 116 | "pprint", 117 | "profile", 118 | "pstats", 119 | "pty", 120 | "pwd", 121 | "py_compile", 122 | "pyclbr", 123 | "pydoc", 124 | "pydoc_data", 125 | "pyexpat", 126 | "queue", 127 | "quopri", 128 | "random", 129 | "re", 130 | "readline", 131 | "reprlib", 132 | "resource", 133 | "rlcompleter", 134 | "runpy", 135 | "sched", 136 | "secrets", 137 | "select", 138 | "selectors", 139 | "shelve", 140 | "shlex", 141 | "shutil", 142 | "signal", 143 | "site", 144 | "smtplib", 145 | "socket", 146 | "socketserver", 147 | "sqlite3", 148 | "sre", 149 | "sre_compile", 150 | "sre_constants", 151 | "sre_parse", 152 | "ssl", 153 | "stat", 154 | "statistics", 155 | "string", 156 | "stringprep", 157 | "struct", 158 | "subprocess", 159 | "symtable", 160 | "sys", 161 | "sysconfig", 162 | "syslog", 163 | "tabnanny", 164 | "tarfile", 165 | "tempfile", 166 | "termios", 167 | "textwrap", 168 | "this", 169 | "threading", 170 | "time", 171 | "timeit", 172 | "tkinter", 173 | "token", 174 | "tokenize", 175 | "tomllib", 176 | "trace", 177 | "traceback", 178 | "tracemalloc", 179 | "tty", 180 | "turtle", 181 | "turtledemo", 182 | "types", 183 | "typing", 184 | "unicodedata", 185 | "unittest", 186 | "urllib", 187 | "uuid", 188 | "venv", 189 | "warnings", 190 | "wave", 191 | "weakref", 192 | "webbrowser", 193 | "winreg", 194 | "winsound", 195 | "wsgiref", 196 | "xml", 197 | "xmlrpc", 198 | "xx", 199 | "xxlimited", 200 | "xxlimited_35", 201 | "xxsubtype", 202 | "zipapp", 203 | "zipfile", 204 | "zipimport", 205 | "zlib", 206 | "zoneinfo", 207 | } 208 | -------------------------------------------------------------------------------- /isort/stdlibs/py36.py: -------------------------------------------------------------------------------- 1 | """ 2 | File contains the standard library of Python 3.6. 3 | 4 | DO NOT EDIT. If the standard library changes, a new list should be created 5 | using the mkstdlibs.py script. 6 | """ 7 | 8 | stdlib = { 9 | "_ast", 10 | "_dummy_thread", 11 | "_thread", 12 | "abc", 13 | "aifc", 14 | "argparse", 15 | "array", 16 | "ast", 17 | "asynchat", 18 | "asyncio", 19 | "asyncore", 20 | "atexit", 21 | "audioop", 22 | "base64", 23 | "bdb", 24 | "binascii", 25 | "binhex", 26 | "bisect", 27 | "builtins", 28 | "bz2", 29 | "cProfile", 30 | "calendar", 31 | "cgi", 32 | "cgitb", 33 | "chunk", 34 | "cmath", 35 | "cmd", 36 | "code", 37 | "codecs", 38 | "codeop", 39 | "collections", 40 | "colorsys", 41 | "compileall", 42 | "concurrent", 43 | "configparser", 44 | "contextlib", 45 | "copy", 46 | "copyreg", 47 | "crypt", 48 | "csv", 49 | "ctypes", 50 | "curses", 51 | "datetime", 52 | "dbm", 53 | "decimal", 54 | "difflib", 55 | "dis", 56 | "distutils", 57 | "doctest", 58 | "dummy_threading", 59 | "email", 60 | "encodings", 61 | "ensurepip", 62 | "enum", 63 | "errno", 64 | "faulthandler", 65 | "fcntl", 66 | "filecmp", 67 | "fileinput", 68 | "fnmatch", 69 | "formatter", 70 | "fpectl", 71 | "fractions", 72 | "ftplib", 73 | "functools", 74 | "gc", 75 | "getopt", 76 | "getpass", 77 | "gettext", 78 | "glob", 79 | "grp", 80 | "gzip", 81 | "hashlib", 82 | "heapq", 83 | "hmac", 84 | "html", 85 | "http", 86 | "imaplib", 87 | "imghdr", 88 | "imp", 89 | "importlib", 90 | "inspect", 91 | "io", 92 | "ipaddress", 93 | "itertools", 94 | "json", 95 | "keyword", 96 | "lib2to3", 97 | "linecache", 98 | "locale", 99 | "logging", 100 | "lzma", 101 | "macpath", 102 | "mailbox", 103 | "mailcap", 104 | "marshal", 105 | "math", 106 | "mimetypes", 107 | "mmap", 108 | "modulefinder", 109 | "msilib", 110 | "msvcrt", 111 | "multiprocessing", 112 | "netrc", 113 | "nis", 114 | "nntplib", 115 | "ntpath", 116 | "numbers", 117 | "operator", 118 | "optparse", 119 | "os", 120 | "ossaudiodev", 121 | "parser", 122 | "pathlib", 123 | "pdb", 124 | "pickle", 125 | "pickletools", 126 | "pipes", 127 | "pkgutil", 128 | "platform", 129 | "plistlib", 130 | "poplib", 131 | "posix", 132 | "posixpath", 133 | "pprint", 134 | "profile", 135 | "pstats", 136 | "pty", 137 | "pwd", 138 | "py_compile", 139 | "pyclbr", 140 | "pydoc", 141 | "queue", 142 | "quopri", 143 | "random", 144 | "re", 145 | "readline", 146 | "reprlib", 147 | "resource", 148 | "rlcompleter", 149 | "runpy", 150 | "sched", 151 | "secrets", 152 | "select", 153 | "selectors", 154 | "shelve", 155 | "shlex", 156 | "shutil", 157 | "signal", 158 | "site", 159 | "smtpd", 160 | "smtplib", 161 | "sndhdr", 162 | "socket", 163 | "socketserver", 164 | "spwd", 165 | "sqlite3", 166 | "sre", 167 | "sre_compile", 168 | "sre_constants", 169 | "sre_parse", 170 | "ssl", 171 | "stat", 172 | "statistics", 173 | "string", 174 | "stringprep", 175 | "struct", 176 | "subprocess", 177 | "sunau", 178 | "symbol", 179 | "symtable", 180 | "sys", 181 | "sysconfig", 182 | "syslog", 183 | "tabnanny", 184 | "tarfile", 185 | "telnetlib", 186 | "tempfile", 187 | "termios", 188 | "test", 189 | "textwrap", 190 | "threading", 191 | "time", 192 | "timeit", 193 | "tkinter", 194 | "token", 195 | "tokenize", 196 | "trace", 197 | "traceback", 198 | "tracemalloc", 199 | "tty", 200 | "turtle", 201 | "turtledemo", 202 | "types", 203 | "typing", 204 | "unicodedata", 205 | "unittest", 206 | "urllib", 207 | "uu", 208 | "uuid", 209 | "venv", 210 | "warnings", 211 | "wave", 212 | "weakref", 213 | "webbrowser", 214 | "winreg", 215 | "winsound", 216 | "wsgiref", 217 | "xdrlib", 218 | "xml", 219 | "xmlrpc", 220 | "zipapp", 221 | "zipfile", 222 | "zipimport", 223 | "zlib", 224 | } 225 | -------------------------------------------------------------------------------- /isort/stdlibs/py37.py: -------------------------------------------------------------------------------- 1 | """ 2 | File contains the standard library of Python 3.7. 3 | 4 | DO NOT EDIT. If the standard library changes, a new list should be created 5 | using the mkstdlibs.py script. 6 | """ 7 | 8 | stdlib = { 9 | "_ast", 10 | "_dummy_thread", 11 | "_thread", 12 | "abc", 13 | "aifc", 14 | "argparse", 15 | "array", 16 | "ast", 17 | "asynchat", 18 | "asyncio", 19 | "asyncore", 20 | "atexit", 21 | "audioop", 22 | "base64", 23 | "bdb", 24 | "binascii", 25 | "binhex", 26 | "bisect", 27 | "builtins", 28 | "bz2", 29 | "cProfile", 30 | "calendar", 31 | "cgi", 32 | "cgitb", 33 | "chunk", 34 | "cmath", 35 | "cmd", 36 | "code", 37 | "codecs", 38 | "codeop", 39 | "collections", 40 | "colorsys", 41 | "compileall", 42 | "concurrent", 43 | "configparser", 44 | "contextlib", 45 | "contextvars", 46 | "copy", 47 | "copyreg", 48 | "crypt", 49 | "csv", 50 | "ctypes", 51 | "curses", 52 | "dataclasses", 53 | "datetime", 54 | "dbm", 55 | "decimal", 56 | "difflib", 57 | "dis", 58 | "distutils", 59 | "doctest", 60 | "dummy_threading", 61 | "email", 62 | "encodings", 63 | "ensurepip", 64 | "enum", 65 | "errno", 66 | "faulthandler", 67 | "fcntl", 68 | "filecmp", 69 | "fileinput", 70 | "fnmatch", 71 | "formatter", 72 | "fractions", 73 | "ftplib", 74 | "functools", 75 | "gc", 76 | "getopt", 77 | "getpass", 78 | "gettext", 79 | "glob", 80 | "grp", 81 | "gzip", 82 | "hashlib", 83 | "heapq", 84 | "hmac", 85 | "html", 86 | "http", 87 | "imaplib", 88 | "imghdr", 89 | "imp", 90 | "importlib", 91 | "inspect", 92 | "io", 93 | "ipaddress", 94 | "itertools", 95 | "json", 96 | "keyword", 97 | "lib2to3", 98 | "linecache", 99 | "locale", 100 | "logging", 101 | "lzma", 102 | "macpath", 103 | "mailbox", 104 | "mailcap", 105 | "marshal", 106 | "math", 107 | "mimetypes", 108 | "mmap", 109 | "modulefinder", 110 | "msilib", 111 | "msvcrt", 112 | "multiprocessing", 113 | "netrc", 114 | "nis", 115 | "nntplib", 116 | "ntpath", 117 | "numbers", 118 | "operator", 119 | "optparse", 120 | "os", 121 | "ossaudiodev", 122 | "parser", 123 | "pathlib", 124 | "pdb", 125 | "pickle", 126 | "pickletools", 127 | "pipes", 128 | "pkgutil", 129 | "platform", 130 | "plistlib", 131 | "poplib", 132 | "posix", 133 | "posixpath", 134 | "pprint", 135 | "profile", 136 | "pstats", 137 | "pty", 138 | "pwd", 139 | "py_compile", 140 | "pyclbr", 141 | "pydoc", 142 | "queue", 143 | "quopri", 144 | "random", 145 | "re", 146 | "readline", 147 | "reprlib", 148 | "resource", 149 | "rlcompleter", 150 | "runpy", 151 | "sched", 152 | "secrets", 153 | "select", 154 | "selectors", 155 | "shelve", 156 | "shlex", 157 | "shutil", 158 | "signal", 159 | "site", 160 | "smtpd", 161 | "smtplib", 162 | "sndhdr", 163 | "socket", 164 | "socketserver", 165 | "spwd", 166 | "sqlite3", 167 | "sre", 168 | "sre_compile", 169 | "sre_constants", 170 | "sre_parse", 171 | "ssl", 172 | "stat", 173 | "statistics", 174 | "string", 175 | "stringprep", 176 | "struct", 177 | "subprocess", 178 | "sunau", 179 | "symbol", 180 | "symtable", 181 | "sys", 182 | "sysconfig", 183 | "syslog", 184 | "tabnanny", 185 | "tarfile", 186 | "telnetlib", 187 | "tempfile", 188 | "termios", 189 | "test", 190 | "textwrap", 191 | "threading", 192 | "time", 193 | "timeit", 194 | "tkinter", 195 | "token", 196 | "tokenize", 197 | "trace", 198 | "traceback", 199 | "tracemalloc", 200 | "tty", 201 | "turtle", 202 | "turtledemo", 203 | "types", 204 | "typing", 205 | "unicodedata", 206 | "unittest", 207 | "urllib", 208 | "uu", 209 | "uuid", 210 | "venv", 211 | "warnings", 212 | "wave", 213 | "weakref", 214 | "webbrowser", 215 | "winreg", 216 | "winsound", 217 | "wsgiref", 218 | "xdrlib", 219 | "xml", 220 | "xmlrpc", 221 | "zipapp", 222 | "zipfile", 223 | "zipimport", 224 | "zlib", 225 | } 226 | -------------------------------------------------------------------------------- /isort/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from functools import lru_cache 4 | from pathlib import Path 5 | from typing import Any, Dict, Optional, Tuple 6 | 7 | 8 | class TrieNode: 9 | def __init__(self, config_file: str = "", config_data: Optional[Dict[str, Any]] = None) -> None: 10 | if not config_data: 11 | config_data = {} 12 | 13 | self.nodes: Dict[str, TrieNode] = {} 14 | self.config_info: Tuple[str, Dict[str, Any]] = (config_file, config_data) 15 | 16 | 17 | class Trie: 18 | """ 19 | A prefix tree to store the paths of all config files and to search the nearest config 20 | associated with each file 21 | """ 22 | 23 | def __init__(self, config_file: str = "", config_data: Optional[Dict[str, Any]] = None) -> None: 24 | self.root: TrieNode = TrieNode(config_file, config_data) 25 | 26 | def insert(self, config_file: str, config_data: Dict[str, Any]) -> None: 27 | resolved_config_path_as_tuple = Path(config_file).parent.resolve().parts 28 | 29 | temp = self.root 30 | 31 | for path in resolved_config_path_as_tuple: 32 | if path not in temp.nodes: 33 | temp.nodes[path] = TrieNode() 34 | 35 | temp = temp.nodes[path] 36 | 37 | temp.config_info = (config_file, config_data) 38 | 39 | def search(self, filename: str) -> Tuple[str, Dict[str, Any]]: 40 | """ 41 | Returns the closest config relative to filename by doing a depth 42 | first search on the prefix tree. 43 | """ 44 | resolved_file_path_as_tuple = Path(filename).resolve().parts 45 | 46 | temp = self.root 47 | 48 | last_stored_config: Tuple[str, Dict[str, Any]] = ("", {}) 49 | 50 | for path in resolved_file_path_as_tuple: 51 | if temp.config_info[0]: 52 | last_stored_config = temp.config_info 53 | 54 | if path not in temp.nodes: 55 | break 56 | 57 | temp = temp.nodes[path] 58 | 59 | return last_stored_config 60 | 61 | 62 | @lru_cache(maxsize=1000) 63 | def exists_case_sensitive(path: str) -> bool: 64 | """Returns if the given path exists and also matches the case on Windows. 65 | 66 | When finding files that can be imported, it is important for the cases to match because while 67 | file os.path.exists("module.py") and os.path.exists("MODULE.py") both return True on Windows, 68 | Python can only import using the case of the real file. 69 | """ 70 | result = os.path.exists(path) 71 | if result and (sys.platform.startswith("win") or sys.platform == "darwin"): # pragma: no cover 72 | directory, basename = os.path.split(path) 73 | result = basename in os.listdir(directory) 74 | return result 75 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/logo.png -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | docs_dir: rtd 2 | site_name: "isort redirect" 3 | -------------------------------------------------------------------------------- /rtd/index.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | The latest isort docs are not on ReadTheDocs. This page contains a javascript redirect to the latest documentation. 6 | If this redirect doesn't work, please [click here for the latest documentation](https://pycqa.github.io/isort/). 7 | -------------------------------------------------------------------------------- /scripts/build_profile_docs.py: -------------------------------------------------------------------------------- 1 | #! /bin/env python 2 | import os 3 | from typing import Any, Dict 4 | 5 | from isort.profiles import profiles 6 | 7 | OUTPUT_FILE = os.path.abspath( 8 | os.path.join(os.path.dirname(os.path.abspath(__file__)), "../docs/configuration/profiles.md") 9 | ) 10 | 11 | HEADER = """Built-in Profile for isort 12 | ======== 13 | 14 | The following profiles are built into isort to allow easy interoperability with 15 | common projects and code styles. 16 | 17 | To use any of the listed profiles, use `isort --profile PROFILE_NAME` from the command line, or `profile=PROFILE_NAME` in your configuration file. 18 | 19 | """ 20 | 21 | 22 | def format_profile(profile_name: str, profile: Dict[str, Any]) -> str: 23 | options = "\n".join(f" - **{name}**: `{value!r}`" for name, value in profile.items()) 24 | return f""" 25 | #{profile_name} 26 | 27 | {profile.get('description', '')} 28 | {options} 29 | """ 30 | 31 | 32 | def document_text() -> str: 33 | return f"{HEADER}{''.join(format_profile(profile_name, profile) for profile_name, profile in profiles.items())}" 34 | 35 | 36 | def write_document(): 37 | with open(OUTPUT_FILE, "w") as output_file: 38 | output_file.write(document_text()) 39 | 40 | 41 | if __name__ == "__main__": 42 | write_document() 43 | -------------------------------------------------------------------------------- /scripts/check_acknowledgments.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import asyncio 3 | import sys 4 | from getpass import getpass 5 | from pathlib import Path 6 | from typing import Dict 7 | 8 | import httpx 9 | import hug 10 | 11 | IGNORED_AUTHOR_LOGINS = {"deepsource-autofix[bot]"} 12 | 13 | REPO = "pycqa/isort" 14 | GITHUB_API_CONTRIBUTORS = f"https://api.github.com/repos/{REPO}/contributors" 15 | GITHUB_USER_CONTRIBUTIONS = f"https://github.com/{REPO}/commits?author=" 16 | GITHUB_USER_TYPE = "User" 17 | USER_DELIMITER = "-" * 80 18 | PER_PAGE = 100 19 | 20 | _ACK_FILE = Path(__file__).parent.parent / "docs" / "contributing" / "4.-acknowledgements.md" 21 | ACKNOWLEDGEMENTS = _ACK_FILE.read_text().lower() 22 | 23 | 24 | def _user_info(user: Dict[str, str], verbose=False) -> str: 25 | login = "@" + user["login"] 26 | name = user.get("name") 27 | display_name = f"{name} ({login})" if name else login 28 | user_info = f"- {display_name}" 29 | if verbose: 30 | contributions = f" {GITHUB_USER_CONTRIBUTIONS}{user['login']}" 31 | user_info += "\n" + contributions 32 | return user_info 33 | 34 | 35 | @hug.cli() 36 | async def main(): 37 | auth = (input("Github Username: "), getpass()) 38 | async with httpx.AsyncClient() as client: 39 | page = 0 40 | results = [] 41 | contributors = [] 42 | while not page or len(results) == PER_PAGE: 43 | page += 1 44 | response = await client.get( 45 | f"{GITHUB_API_CONTRIBUTORS}?per_page={PER_PAGE}&page={page}", auth=auth 46 | ) 47 | results = response.json() 48 | contributors.extend( 49 | contributor 50 | for contributor in results 51 | if contributor["type"] == GITHUB_USER_TYPE 52 | and contributor["login"] not in IGNORED_AUTHOR_LOGINS 53 | and f"@{contributor['login'].lower()}" not in ACKNOWLEDGEMENTS 54 | ) 55 | 56 | unacknowledged_users = await asyncio.gather( 57 | *(client.get(contributor["url"], auth=auth) for contributor in contributors) 58 | ) 59 | unacknowledged_users = [request.json() for request in unacknowledged_users] 60 | 61 | if not unacknowledged_users: 62 | sys.exit() 63 | 64 | print("Found unacknowledged authors:") 65 | print() 66 | 67 | for user in unacknowledged_users: 68 | print(_user_info(user, verbose=True)) 69 | print(USER_DELIMITER) 70 | 71 | print() 72 | print("Printing again for easy inclusion in Markdown file:") 73 | print() 74 | for user in unacknowledged_users: 75 | print(_user_info(user)) 76 | 77 | sys.exit(1) 78 | 79 | 80 | if __name__ == "__main__": 81 | main.interface.cli() 82 | -------------------------------------------------------------------------------- /scripts/clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | uv run isort --profile hug isort/ tests/ scripts/ 5 | uv run isort --profile hug example_*/ 6 | uv run black isort/ tests/ scripts/ 7 | uv run black example_*/ 8 | -------------------------------------------------------------------------------- /scripts/docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ux 3 | 4 | result=0 5 | 6 | for ver in {3.9,3.10}; do 7 | # latest tag will override after each build, leaving only the newest python version tagged 8 | docker build ./ --build-arg VERSION=$ver -t "isort:$ver" -t "isort:latest" && docker run "isort:$ver" 9 | result=$(( $? + $result )) 10 | done 11 | 12 | exit $result 13 | -------------------------------------------------------------------------------- /scripts/done.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | ./scripts/clean.sh 5 | ./scripts/test.sh 6 | -------------------------------------------------------------------------------- /scripts/mkstdlibs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import re 3 | 4 | from stdlibs import py38, py39, py310, py311, py312, py313 5 | 6 | URL = "https://docs.python.org/{}/objects.inv" 7 | PATH = "isort/stdlibs/py{}.py" 8 | VERSIONS = [ 9 | py38, 10 | py39, 11 | py310, 12 | py311, 13 | py312, 14 | py313, 15 | ] 16 | 17 | DOCSTRING = """ 18 | File contains the standard library of Python {}. 19 | 20 | DO NOT EDIT. If the standard library changes, a new list should be created 21 | using the mkstdlibs.py script. 22 | """ 23 | 24 | 25 | class FakeConfig: 26 | intersphinx_timeout = None 27 | tls_verify = True 28 | user_agent = "" 29 | tls_cacerts = None 30 | 31 | 32 | class FakeApp: 33 | srcdir = "" 34 | config = FakeConfig() 35 | 36 | 37 | for version_module in VERSIONS: 38 | version_match = re.match( 39 | r"^stdlibs\.py(?P\d)(?P\d+)$", 40 | version_module.__name__, 41 | ) 42 | version_info = (version_match.groupdict()["major"], version_match.groupdict()["minor"]) 43 | 44 | # Any modules we want to enforce across Python versions stdlib can be included in set init 45 | modules = {"_ast", "posixpath", "ntpath", "sre_constants", "sre_parse", "sre_compile", "sre"} 46 | modules.update( 47 | { 48 | module_name 49 | for module_name in version_module.module_names 50 | if not module_name.startswith("_") 51 | } 52 | ) 53 | 54 | path = PATH.format("".join(version_info)) 55 | with open(path, "w") as stdlib_file: 56 | docstring = DOCSTRING.format(".".join(version_info)) 57 | stdlib_file.write(f'"""{docstring}"""\n\n') 58 | stdlib_file.write("stdlib = {\n") 59 | for module in sorted(modules): 60 | stdlib_file.write(f' "{module}",\n') 61 | stdlib_file.write("}\n") 62 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | uv run coverage run --parallel -m pytest tests/unit/ -s --ignore=tests/unit/test_deprecated_finders.py 5 | uv run coverage combine 6 | uv run coverage report 7 | uv run coverage xml 8 | -------------------------------------------------------------------------------- /scripts/test_integration.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | uv run pytest tests/integration/ -s 5 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/__init__.py -------------------------------------------------------------------------------- /tests/benchmark/test_api.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | import pytest 4 | 5 | from isort import api 6 | 7 | imperfect_content = "import b\nimport a\n" 8 | fixed_content = "import a\nimport b\n" 9 | 10 | 11 | @pytest.fixture 12 | def imperfect(tmpdir) -> Any: 13 | imperfect_file = tmpdir.join("test_needs_changes.py") 14 | imperfect_file.write_text(imperfect_content, "utf8") 15 | return imperfect_file 16 | 17 | 18 | def test_sort_file(benchmark, imperfect) -> None: 19 | def sort_file(): 20 | api.sort_file(imperfect) 21 | 22 | benchmark.pedantic(sort_file, iterations=10, rounds=100) 23 | assert imperfect.read() == fixed_content 24 | 25 | 26 | def test_sort_file_in_place(benchmark, imperfect) -> None: 27 | def sort_file(): 28 | api.sort_file(imperfect, overwrite_in_place=True) 29 | 30 | benchmark.pedantic(sort_file, iterations=10, rounds=100) 31 | assert imperfect.read() == fixed_content 32 | -------------------------------------------------------------------------------- /tests/integration/test_hypothesmith.py: -------------------------------------------------------------------------------- 1 | import ast 2 | from typing import get_type_hints 3 | 4 | import hypothesis 5 | import libcst 6 | from hypothesis import strategies as st 7 | from hypothesmith import from_grammar, from_node 8 | 9 | import isort 10 | 11 | 12 | def _as_config(kw) -> isort.Config: 13 | if "wrap_length" in kw and "line_length" in kw: 14 | kw["wrap_length"], kw["line_length"] = sorted([kw["wrap_length"], kw["line_length"]]) 15 | try: 16 | return isort.Config(**kw) 17 | except ValueError: 18 | kw["wrap_length"] = 0 19 | return isort.Config(**kw) 20 | 21 | 22 | def _record_targets(code: str, prefix: str = "") -> str: 23 | # target larger inputs - the Hypothesis engine will do a multi-objective 24 | # hill-climbing search using these scores to generate 'better' examples. 25 | nodes = list(ast.walk(ast.parse(code))) 26 | import_nodes = [n for n in nodes if isinstance(n, (ast.Import, ast.ImportFrom))] 27 | uniq_nodes = {type(n) for n in nodes} 28 | for value, label in [ 29 | (len(import_nodes), "total number of import nodes"), 30 | (len(uniq_nodes), "number of unique ast node types"), 31 | ]: 32 | hypothesis.target(float(value), label=prefix + label) 33 | return code 34 | 35 | 36 | def configs(**force_strategies: st.SearchStrategy) -> st.SearchStrategy: 37 | """Generate arbitrary Config objects.""" 38 | skip = { 39 | "line_ending", 40 | "sections", 41 | "known_future_library", 42 | "forced_separate", 43 | "lines_before_imports", 44 | "lines_after_imports", 45 | "lines_between_sections", 46 | "lines_between_types", 47 | "sources", 48 | "virtual_env", 49 | "conda_env", 50 | "directory", 51 | "formatter", 52 | "formatting_function", 53 | } 54 | inferred_kwargs = { 55 | k: st.from_type(v) 56 | for k, v in get_type_hints(isort.settings._Config).items() 57 | if k not in skip 58 | } 59 | specific = { 60 | "line_length": st.integers(0, 200), 61 | "wrap_length": st.integers(0, 200), 62 | "indent": st.integers(0, 20).map(lambda n: n * " "), 63 | "default_section": st.sampled_from(sorted(isort.settings.KNOWN_SECTION_MAPPING)), 64 | "force_grid_wrap": st.integers(0, 20), 65 | "profile": st.sampled_from(sorted(isort.settings.profiles)), 66 | "py_version": st.sampled_from(("auto", *isort.settings.VALID_PY_TARGETS)), 67 | } 68 | kwargs = {**inferred_kwargs, **specific, **force_strategies} 69 | return st.fixed_dictionaries({}, optional=kwargs).map(_as_config) 70 | 71 | 72 | st.register_type_strategy(isort.Config, configs()) 73 | 74 | 75 | @hypothesis.example("import A\nimportA\r\n\n", isort.Config(), False) 76 | @hypothesis.given( 77 | source_code=st.lists( 78 | from_grammar(auto_target=False) 79 | | from_node(auto_target=False) 80 | | from_node(libcst.Import, auto_target=False) 81 | | from_node(libcst.ImportFrom, auto_target=False), 82 | min_size=1, 83 | max_size=10, 84 | ).map("\n".join), 85 | config=st.builds(isort.Config), 86 | disregard_skip=st.booleans(), 87 | ) 88 | @hypothesis.seed(235738473415671197623909623354096762459) 89 | @hypothesis.settings( 90 | suppress_health_check=[hypothesis.HealthCheck.too_slow, hypothesis.HealthCheck.filter_too_much] 91 | ) 92 | def test_isort_is_idempotent(source_code: str, config: isort.Config, disregard_skip: bool) -> None: 93 | # NOTE: if this test finds a bug, please notify @Zac-HD so that it can be added to the 94 | # Hypothesmith trophy case. This really helps with research impact evaluations! 95 | _record_targets(source_code) 96 | result = isort.code(source_code, config=config, disregard_skip=disregard_skip) 97 | assert result == isort.code(result, config=config, disregard_skip=disregard_skip) 98 | -------------------------------------------------------------------------------- /tests/integration/test_literal.py: -------------------------------------------------------------------------------- 1 | """Tests that need installation of other packages.""" 2 | 3 | # TODO: find a way to install example-isort-formatting-plugin to pass tests 4 | # import isort.literal 5 | 6 | # from isort.settings import Config 7 | 8 | 9 | # def test_value_assignment_list(): 10 | # assert isort.literal.assignment("x = ['b', 'a']", "list", "py") == "x = ['a', 'b']" 11 | # assert ( 12 | # isort.literal.assignment("x = ['b', 'a']", "list", "py", Config(formatter="example")) 13 | # == 'x = ["a", "b"]' 14 | # ) 15 | -------------------------------------------------------------------------------- /tests/integration/test_projects_using_isort.py: -------------------------------------------------------------------------------- 1 | """Tests projects that use isort to see if any differences are found between 2 | their current imports and what isort suggest on the develop branch. 3 | This is an important early warning signal of regressions. 4 | 5 | NOTE: If you use isort within a public repository, please feel empowered to add your project here! 6 | It is important to isort that as few regressions as possible are experienced by our users. 7 | Having your project tested here is the most sure way to keep those regressions form ever happening. 8 | """ 9 | 10 | from __future__ import annotations 11 | 12 | from pathlib import Path 13 | from subprocess import check_call 14 | from typing import Generator, Sequence 15 | 16 | import pytest 17 | 18 | from isort.main import main 19 | 20 | 21 | def git_clone(repository_url: str, directory: Path): 22 | """Clones the given repository into the given directory path""" 23 | check_call(["git", "clone", "--depth", "1", repository_url, str(directory)]) 24 | 25 | 26 | def run_isort(arguments: Generator[str, None, None] | Sequence[str]): 27 | """Runs isort in diff and check mode with the given arguments""" 28 | main(["--check-only", "--diff", *arguments]) 29 | 30 | 31 | @pytest.mark.xfail( 32 | reason="Project is incorrectly formatted after PR #2236, should be fixed " 33 | "after a release and the project formatting again." 34 | ) 35 | def test_django(tmpdir): 36 | git_clone("https://github.com/django/django.git", tmpdir) 37 | run_isort( 38 | str(target_dir) for target_dir in (tmpdir / "django", tmpdir / "tests", tmpdir / "scripts") 39 | ) 40 | 41 | 42 | def test_plone(tmpdir): 43 | git_clone("https://github.com/plone/plone.app.multilingualindexes.git", tmpdir) 44 | run_isort([str(tmpdir / "src"), "--skip", "languagefallback.py"]) 45 | 46 | 47 | def test_pandas(tmpdir): 48 | git_clone("https://github.com/pandas-dev/pandas.git", tmpdir) 49 | run_isort((str(tmpdir / "pandas"), "--skip", "__init__.py")) 50 | 51 | 52 | def test_habitat_lab(tmpdir): 53 | git_clone("https://github.com/facebookresearch/habitat-lab.git", tmpdir) 54 | run_isort([str(tmpdir)]) 55 | 56 | 57 | def test_pylint(tmpdir): 58 | git_clone("https://github.com/PyCQA/pylint.git", tmpdir) 59 | run_isort([str(tmpdir), "--skip", "bad.py"]) 60 | 61 | 62 | def test_hypothesis(tmpdir): 63 | git_clone("https://github.com/HypothesisWorks/hypothesis.git", tmpdir) 64 | run_isort( 65 | ( 66 | str(tmpdir), 67 | "--skip", 68 | "tests", 69 | "--profile", 70 | "black", 71 | "--ca", 72 | "--project", 73 | "hypothesis", 74 | "--project", 75 | "hypothesistooling", 76 | ) 77 | ) 78 | 79 | 80 | def test_pyramid(tmpdir): 81 | git_clone("https://github.com/Pylons/pyramid.git", tmpdir) 82 | run_isort( 83 | str(target_dir) 84 | for target_dir in (tmpdir / "src" / "pyramid", tmpdir / "tests", tmpdir / "setup.py") 85 | ) 86 | 87 | 88 | def test_products_zopetree(tmpdir): 89 | git_clone("https://github.com/jugmac00/Products.ZopeTree.git", tmpdir) 90 | run_isort([str(tmpdir)]) 91 | 92 | 93 | def test_dobby(tmpdir): 94 | git_clone("https://github.com/rocketDuck/dobby.git", tmpdir) 95 | run_isort([str(tmpdir / "tests"), str(tmpdir / "src")]) 96 | 97 | 98 | def test_zope(tmpdir): 99 | git_clone("https://github.com/zopefoundation/Zope.git", tmpdir) 100 | run_isort([str(tmpdir), "--skip", "util.py"]) 101 | -------------------------------------------------------------------------------- /tests/integration/test_ticketed_features.py: -------------------------------------------------------------------------------- 1 | """Tests that need installation of other packages.""" 2 | 3 | # TODO: find a way to install example-isort-formatting-plugin to pass tests 4 | # from io import StringIO 5 | 6 | # import pytest 7 | 8 | # import isort 9 | # from isort import api, exceptions 10 | 11 | 12 | # def test_isort_supports_formatting_plugins(): 13 | # """Test to ensure isort provides a way to create and share formatting plugins. 14 | # See: https://github.com/pycqa/isort/issues/1353. 15 | # """ 16 | # # formatting plugin 17 | # assert isort.code("import a", formatter="example") == "import a\n" 18 | # # non-existent plugin 19 | # with pytest.raises(exceptions.FormattingPluginDoesNotExist): 20 | # assert isort.code("import a", formatter="madeupfake") == "import a\n" 21 | 22 | 23 | # def test_isort_literals_issue_1358(): 24 | # assert ( 25 | # isort.code( 26 | # """ 27 | # import x 28 | # import a 29 | 30 | 31 | # # isort: list 32 | # __all__ = ["b", "a", "b"] 33 | 34 | # # isort: unique-list 35 | # __all__ = ["b", "a", "b"] 36 | 37 | # # isort: tuple 38 | # __all__ = ("b", "a", "b") 39 | 40 | # # isort: unique-tuple 41 | # __all__ = ("b", "a", "b") 42 | 43 | # # isort: set 44 | # __all__ = {"b", "a", "b"} 45 | 46 | 47 | # def method(): 48 | # # isort: list 49 | # x = ["b", "a"] 50 | 51 | 52 | # # isort: dict 53 | # y = {"z": "z", "b": "b", "b": "c"}""" 54 | # ) 55 | # == """ 56 | # import a 57 | # import x 58 | 59 | # # isort: list 60 | # __all__ = ['a', 'b', 'b'] 61 | 62 | # # isort: unique-list 63 | # __all__ = ['a', 'b'] 64 | 65 | # # isort: tuple 66 | # __all__ = ('a', 'b', 'b') 67 | 68 | # # isort: unique-tuple 69 | # __all__ = ('a', 'b') 70 | 71 | # # isort: set 72 | # __all__ = {'a', 'b'} 73 | 74 | 75 | # def method(): 76 | # # isort: list 77 | # x = ['a', 'b'] 78 | 79 | 80 | # # isort: dict 81 | # y = {'b': 'c', 'z': 'z'}""" 82 | # ) 83 | # assert ( 84 | # isort.code( 85 | # """ 86 | # import x 87 | # import a 88 | 89 | 90 | # # isort: list 91 | # __all__ = ["b", "a", "b"] 92 | 93 | # # isort: unique-list 94 | # __all__ = ["b", "a", "b"] 95 | 96 | # # isort: tuple 97 | # __all__ = ("b", "a", "b") 98 | 99 | # # isort: unique-tuple 100 | # __all__ = ("b", "a", "b") 101 | 102 | # # isort: set 103 | # __all__ = {"b", "a", "b"} 104 | 105 | 106 | # def method(): 107 | # # isort: list 108 | # x = ["b", "a"] 109 | 110 | 111 | # # isort: assignments 112 | # d = 1 113 | # b = 2 114 | # a = 3 115 | 116 | # # isort: dict 117 | # y = {"z": "z", "b": "b", "b": "c"}""", 118 | # formatter="example", 119 | # ) 120 | # == """ 121 | # import a 122 | # import x 123 | 124 | # # isort: list 125 | # __all__ = ["a", "b", "b"] 126 | 127 | # # isort: unique-list 128 | # __all__ = ["a", "b"] 129 | 130 | # # isort: tuple 131 | # __all__ = ("a", "b", "b") 132 | 133 | # # isort: unique-tuple 134 | # __all__ = ("a", "b") 135 | 136 | # # isort: set 137 | # __all__ = {"a", "b"} 138 | 139 | 140 | # def method(): 141 | # # isort: list 142 | # x = ["a", "b"] 143 | 144 | 145 | # # isort: assignments 146 | # a = 3 147 | # b = 2 148 | # d = 1 149 | 150 | # # isort: dict 151 | # y = {"b": "c", "z": "z"}""" 152 | # ) 153 | # assert api.sort_stream( 154 | # input_stream=StringIO( 155 | # """ 156 | # import a 157 | # import x 158 | 159 | # # isort: list 160 | # __all__ = ["b", "a", "b"] 161 | 162 | # # isort: unique-list 163 | # __all__ = ["b", "a", "b"] 164 | 165 | # # isort: tuple 166 | # __all__ = ("b", "a", "b") 167 | 168 | # # isort: unique-tuple 169 | # __all__ = ("b", "a", "b") 170 | 171 | # # isort: set 172 | # __all__ = {"b", "a", "b"} 173 | 174 | 175 | # def method(): 176 | # # isort: list 177 | # x = ["b", "a"] 178 | 179 | 180 | # # isort: assignments 181 | # d = 1 182 | # b = 2 183 | # a = 3 184 | 185 | # # isort: dict 186 | # y = {"z": "z", "b": "b", "b": "c"}""", 187 | # ), 188 | # output_stream=StringIO(), 189 | # ) 190 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/__init__.py -------------------------------------------------------------------------------- /tests/unit/conftest.py: -------------------------------------------------------------------------------- 1 | """isort test wide fixtures and configuration""" 2 | 3 | import os 4 | from pathlib import Path 5 | 6 | import pytest 7 | 8 | TEST_DIR = os.path.dirname(os.path.abspath(__file__)) 9 | SRC_DIR = os.path.abspath(os.path.join(TEST_DIR, "../../isort/")) 10 | 11 | 12 | @pytest.fixture 13 | def test_dir(): 14 | return TEST_DIR 15 | 16 | 17 | @pytest.fixture 18 | def src_dir(): 19 | return SRC_DIR 20 | 21 | 22 | @pytest.fixture 23 | def test_path(): 24 | return Path(TEST_DIR).resolve() 25 | 26 | 27 | @pytest.fixture 28 | def src_path(): 29 | return Path(SRC_DIR).resolve() 30 | 31 | 32 | @pytest.fixture 33 | def examples_path(): 34 | return Path(TEST_DIR).resolve() / "example_projects" 35 | -------------------------------------------------------------------------------- /tests/unit/example_crlf_file.py: -------------------------------------------------------------------------------- 1 | import b 2 | import a 3 | 4 | 5 | def func(): 6 | x = 1 7 | y = 2 8 | z = 3 9 | c = 4 10 | return x + y + z + c 11 | -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/almost-implicit/.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | src_paths=root 3 | -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/almost-implicit/root/nested/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/example_projects/namespaces/almost-implicit/root/nested/__init__.py -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/almost-implicit/root/nested/x.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/example_projects/namespaces/almost-implicit/root/nested/x.py -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/almost-implicit/root/y.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/example_projects/namespaces/almost-implicit/root/y.py -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/implicit/.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | src_paths=root 3 | -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/implicit/root/nested/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/example_projects/namespaces/implicit/root/nested/__init__.py -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/implicit/root/nested/x.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/example_projects/namespaces/implicit/root/nested/x.py -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/none/.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | src_paths=root 3 | -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/none/root/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/example_projects/namespaces/none/root/__init__.py -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/none/root/nested/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/example_projects/namespaces/none/root/nested/__init__.py -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/pkg_resource/.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | src_paths=root 3 | -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/pkg_resource/root/__init__.py: -------------------------------------------------------------------------------- 1 | __import__("pkg_resources").declare_namespace(__name__) 2 | -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/pkg_resource/root/nested/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/example_projects/namespaces/pkg_resource/root/nested/__init__.py -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/pkg_resource/root/nested/x.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/example_projects/namespaces/pkg_resource/root/nested/x.py -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/pkgutil/.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | src_paths=root 3 | -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/pkgutil/root/__init__.py: -------------------------------------------------------------------------------- 1 | __path__ = __import__("pkgutil").extend_path(__path__, __name__) 2 | -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/pkgutil/root/nested/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/example_projects/namespaces/pkgutil/root/nested/__init__.py -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/pkgutil/root/nested/x.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/example_projects/namespaces/pkgutil/root/nested/x.py -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/weird_encoding/.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | src_paths=root 3 | -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/weird_encoding/root/__init__.py: -------------------------------------------------------------------------------- 1 | description = "基于FastAPI + Mysql的 TodoList" # Exception: UnicodeDecodeError 2 | -------------------------------------------------------------------------------- /tests/unit/example_projects/namespaces/weird_encoding/root/nested/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/example_projects/namespaces/weird_encoding/root/nested/__init__.py -------------------------------------------------------------------------------- /tests/unit/profiles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/isort/342642e36417dfb71c74a2b1de1f6bcfba5b4253/tests/unit/profiles/__init__.py -------------------------------------------------------------------------------- /tests/unit/profiles/test_attrs.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | from ..utils import isort_test 4 | 5 | attrs_isort_test = partial(isort_test, profile="attrs") 6 | 7 | 8 | def test_attrs_code_snippet_one(): 9 | attrs_isort_test( 10 | """from __future__ import absolute_import, division, print_function 11 | 12 | import sys 13 | 14 | from functools import partial 15 | 16 | from . import converters, exceptions, filters, setters, validators 17 | from ._config import get_run_validators, set_run_validators 18 | from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types 19 | from ._make import ( 20 | NOTHING, 21 | Attribute, 22 | Factory, 23 | attrib, 24 | attrs, 25 | fields, 26 | fields_dict, 27 | make_class, 28 | validate, 29 | ) 30 | from ._version_info import VersionInfo 31 | 32 | 33 | __version__ = "20.2.0.dev0" 34 | """ 35 | ) 36 | 37 | 38 | def test_attrs_code_snippet_two(): 39 | attrs_isort_test( 40 | """from __future__ import absolute_import, division, print_function 41 | 42 | import copy 43 | import linecache 44 | import sys 45 | import threading 46 | import uuid 47 | import warnings 48 | 49 | from operator import itemgetter 50 | 51 | from . import _config, setters 52 | from ._compat import ( 53 | PY2, 54 | isclass, 55 | iteritems, 56 | metadata_proxy, 57 | ordered_dict, 58 | set_closure_cell, 59 | ) 60 | from .exceptions import ( 61 | DefaultAlreadySetError, 62 | FrozenInstanceError, 63 | NotAnAttrsClassError, 64 | PythonTooOldError, 65 | UnannotatedAttributeError, 66 | ) 67 | 68 | 69 | # This is used at least twice, so cache it here. 70 | _obj_setattr = object.__setattr__ 71 | """ 72 | ) 73 | 74 | 75 | def test_attrs_code_snippet_three(): 76 | attrs_isort_test( 77 | ''' 78 | """ 79 | Commonly useful validators. 80 | """ 81 | 82 | from __future__ import absolute_import, division, print_function 83 | 84 | import re 85 | 86 | from ._make import _AndValidator, and_, attrib, attrs 87 | from .exceptions import NotCallableError 88 | 89 | 90 | __all__ = [ 91 | "and_", 92 | "deep_iterable", 93 | "deep_mapping", 94 | "in_", 95 | "instance_of", 96 | "is_callable", 97 | "matches_re", 98 | "optional", 99 | "provides", 100 | ] 101 | ''' 102 | ) 103 | -------------------------------------------------------------------------------- /tests/unit/profiles/test_django.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | from ..utils import isort_test 4 | 5 | django_isort_test = partial(isort_test, profile="django", known_first_party=["django"]) 6 | 7 | 8 | def test_django_snippet_one(): 9 | django_isort_test( 10 | """import copy 11 | import inspect 12 | import warnings 13 | from functools import partialmethod 14 | from itertools import chain 15 | 16 | from django.apps import apps 17 | from django.conf import settings 18 | from django.core import checks 19 | from django.core.exceptions import ( 20 | NON_FIELD_ERRORS, FieldDoesNotExist, FieldError, MultipleObjectsReturned, 21 | ObjectDoesNotExist, ValidationError, 22 | ) 23 | from django.db import ( 24 | DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY, DatabaseError, connection, 25 | connections, router, transaction, 26 | ) 27 | from django.db.models import ( 28 | NOT_PROVIDED, ExpressionWrapper, IntegerField, Max, Value, 29 | ) 30 | from django.db.models.constants import LOOKUP_SEP 31 | from django.db.models.constraints import CheckConstraint 32 | from django.db.models.deletion import CASCADE, Collector 33 | from django.db.models.fields.related import ( 34 | ForeignObjectRel, OneToOneField, lazy_related_operation, resolve_relation, 35 | ) 36 | from django.db.models.functions import Coalesce 37 | from django.db.models.manager import Manager 38 | from django.db.models.options import Options 39 | from django.db.models.query import Q 40 | from django.db.models.signals import ( 41 | class_prepared, post_init, post_save, pre_init, pre_save, 42 | ) 43 | from django.db.models.utils import make_model_tuple 44 | from django.utils.encoding import force_str 45 | from django.utils.hashable import make_hashable 46 | from django.utils.text import capfirst, get_text_list 47 | from django.utils.translation import gettext_lazy as _ 48 | from django.utils.version import get_version 49 | 50 | 51 | class Deferred: 52 | def __repr__(self): 53 | return '' 54 | 55 | def __str__(self): 56 | return ''""" 57 | ) 58 | 59 | 60 | def test_django_snippet_two(): 61 | django_isort_test( 62 | '''from django.utils.version import get_version 63 | 64 | VERSION = (3, 2, 0, 'alpha', 0) 65 | 66 | __version__ = get_version(VERSION) 67 | 68 | 69 | def setup(set_prefix=True): 70 | """ 71 | Configure the settings (this happens as a side effect of accessing the 72 | first setting), configure logging and populate the app registry. 73 | Set the thread-local urlresolvers script prefix if `set_prefix` is True. 74 | """ 75 | from django.apps import apps 76 | from django.conf import settings 77 | from django.urls import set_script_prefix 78 | from django.utils.log import configure_logging 79 | 80 | configure_logging(settings.LOGGING_CONFIG, settings.LOGGING) 81 | if set_prefix: 82 | set_script_prefix( 83 | '/' if settings.FORCE_SCRIPT_NAME is None else settings.FORCE_SCRIPT_NAME 84 | ) 85 | apps.populate(settings.INSTALLED_APPS)''' 86 | ) 87 | 88 | 89 | def test_django_snippet_three(): 90 | django_isort_test( 91 | """import cgi 92 | import codecs 93 | import copy 94 | import warnings 95 | from io import BytesIO 96 | from itertools import chain 97 | from urllib.parse import quote, urlencode, urljoin, urlsplit 98 | 99 | from django.conf import settings 100 | from django.core import signing 101 | from django.core.exceptions import ( 102 | DisallowedHost, ImproperlyConfigured, RequestDataTooBig, 103 | ) 104 | from django.core.files import uploadhandler 105 | from django.http.multipartparser import MultiPartParser, MultiPartParserError 106 | from django.utils.datastructures import ( 107 | CaseInsensitiveMapping, ImmutableList, MultiValueDict, 108 | ) 109 | from django.utils.deprecation import RemovedInDjango40Warning 110 | from django.utils.encoding import escape_uri_path, iri_to_uri 111 | from django.utils.functional import cached_property 112 | from django.utils.http import is_same_domain, limited_parse_qsl 113 | from django.utils.regex_helper import _lazy_re_compile 114 | 115 | from .multipartparser import parse_header 116 | 117 | RAISE_ERROR = object() 118 | 119 | 120 | class UnreadablePostError(OSError): 121 | pass""" 122 | ) 123 | -------------------------------------------------------------------------------- /tests/unit/profiles/test_hug.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | from ..utils import isort_test 4 | 5 | hug_isort_test = partial(isort_test, profile="hug", known_first_party=["hug"]) 6 | 7 | 8 | def test_hug_code_snippet_one(): 9 | hug_isort_test( 10 | ''' 11 | from __future__ import absolute_import 12 | 13 | import asyncio 14 | import sys 15 | from collections import OrderedDict, namedtuple 16 | from distutils.util import strtobool 17 | from functools import partial 18 | from itertools import chain 19 | from types import ModuleType 20 | from wsgiref.simple_server import make_server 21 | 22 | import falcon 23 | from falcon import HTTP_METHODS 24 | 25 | import hug.defaults 26 | import hug.output_format 27 | from hug import introspect 28 | from hug._version import current 29 | 30 | INTRO = """ 31 | /#######################################################################\\ 32 | `.----``..-------..``.----. 33 | :/:::::--:---------:--::::://. 34 | .+::::----##/-/oo+:-##----::::// 35 | `//::-------/oosoo-------::://. ## ## ## ## ##### 36 | .-:------./++o/o-.------::-` ``` ## ## ## ## ## 37 | `----.-./+o+:..----. `.:///. ######## ## ## ## 38 | ``` `----.-::::::------ `.-:::://. ## ## ## ## ## #### 39 | ://::--.``` -:``...-----...` `:--::::::-.` ## ## ## ## ## ## 40 | :/:::::::::-:- ````` .:::::-.` ## ## #### ###### 41 | ``.--:::::::. .:::.` 42 | ``..::. .:: EMBRACE THE APIs OF THE FUTURE 43 | ::- .:- 44 | -::` ::- VERSION {0} 45 | `::- -::` 46 | -::-` -::- 47 | \\########################################################################/ 48 | Copyright (C) 2016 Timothy Edmund Crosley 49 | Under the MIT License 50 | """.format( 51 | current 52 | )''' 53 | ) 54 | 55 | 56 | def test_hug_code_snippet_two(): 57 | hug_isort_test( 58 | """from __future__ import absolute_import 59 | 60 | import functools 61 | from collections import namedtuple 62 | 63 | from falcon import HTTP_METHODS 64 | 65 | import hug.api 66 | import hug.defaults 67 | import hug.output_format 68 | from hug import introspect 69 | from hug.format import underscore 70 | 71 | 72 | def default_output_format( 73 | content_type="application/json", apply_globally=False, api=None, cli=False, http=True 74 | ): 75 | """ 76 | ) 77 | 78 | 79 | def test_hug_code_snippet_three(): 80 | hug_isort_test( 81 | """from __future__ import absolute_import 82 | 83 | import argparse 84 | import asyncio 85 | import os 86 | import sys 87 | from collections import OrderedDict 88 | from functools import lru_cache, partial, wraps 89 | 90 | import falcon 91 | from falcon import HTTP_BAD_REQUEST 92 | 93 | import hug._empty as empty 94 | import hug.api 95 | import hug.output_format 96 | import hug.types as types 97 | from hug import introspect 98 | from hug.exceptions import InvalidTypeData 99 | from hug.format import parse_content_type 100 | from hug.types import ( 101 | MarshmallowInputSchema, 102 | MarshmallowReturnSchema, 103 | Multiple, 104 | OneOf, 105 | SmartBoolean, 106 | Text, 107 | text, 108 | ) 109 | 110 | DOC_TYPE_MAP = {str: "String", bool: "Boolean", list: "Multiple", int: "Integer", float: "Float"} 111 | """ 112 | ) 113 | -------------------------------------------------------------------------------- /tests/unit/profiles/test_open_stack.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | from ..utils import isort_test 4 | 5 | open_stack_isort_test = partial(isort_test, profile="open_stack") 6 | 7 | 8 | def test_open_stack_code_snippet_one(): 9 | open_stack_isort_test( 10 | """import httplib 11 | import logging 12 | import random 13 | import StringIO 14 | import time 15 | import unittest 16 | 17 | import eventlet 18 | import webob.exc 19 | 20 | import nova.api.ec2 21 | from nova.api import manager 22 | from nova.api import openstack 23 | from nova.auth import users 24 | from nova.endpoint import cloud 25 | import nova.flags 26 | from nova.i18n import _ 27 | from nova.i18n import _LC 28 | from nova import test 29 | """, 30 | known_first_party=["nova"], 31 | py_version="2", 32 | order_by_type=False, 33 | ) 34 | 35 | 36 | def test_open_stack_code_snippet_two(): 37 | open_stack_isort_test( 38 | """# Copyright 2011 VMware, Inc 39 | # All Rights Reserved. 40 | # 41 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 42 | # not use this file except in compliance with the License. You may obtain 43 | # a copy of the License at 44 | # 45 | # http://www.apache.org/licenses/LICENSE-2.0 46 | # 47 | # Unless required by applicable law or agreed to in writing, software 48 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 49 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 50 | # License for the specific language governing permissions and limitations 51 | # under the License. 52 | 53 | import inspect 54 | import os 55 | import random 56 | 57 | from neutron_lib.callbacks import events 58 | from neutron_lib.callbacks import registry 59 | from neutron_lib.callbacks import resources 60 | from neutron_lib import context 61 | from neutron_lib.db import api as session 62 | from neutron_lib.plugins import directory 63 | from neutron_lib import rpc as n_rpc 64 | from oslo_concurrency import processutils 65 | from oslo_config import cfg 66 | from oslo_log import log as logging 67 | from oslo_messaging import server as rpc_server 68 | from oslo_service import loopingcall 69 | from oslo_service import service as common_service 70 | from oslo_utils import excutils 71 | from oslo_utils import importutils 72 | import psutil 73 | 74 | from neutron.common import config 75 | from neutron.common import profiler 76 | from neutron.conf import service 77 | from neutron import worker as neutron_worker 78 | from neutron import wsgi 79 | 80 | service.register_service_opts(service.SERVICE_OPTS) 81 | """, 82 | known_first_party=["neutron"], 83 | ) 84 | 85 | 86 | def test_open_stack_code_snippet_three(): 87 | open_stack_isort_test( 88 | """ 89 | # Copyright 2013 Red Hat, Inc. 90 | # 91 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 92 | # not use this file except in compliance with the License. You may obtain 93 | # a copy of the License at 94 | # 95 | # http://www.apache.org/licenses/LICENSE-2.0 96 | # 97 | # Unless required by applicable law or agreed to in writing, software 98 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 99 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 100 | # License for the specific language governing permissions and limitations 101 | # under the License. 102 | 103 | import functools 104 | 105 | from oslo_log import log as logging 106 | import oslo_messaging as messaging 107 | from oslo_messaging.rpc import dispatcher 108 | from oslo_serialization import jsonutils 109 | from oslo_service import periodic_task 110 | from oslo_utils import importutils 111 | import six 112 | 113 | import nova.conf 114 | import nova.context 115 | import nova.exception 116 | from nova.i18n import _ 117 | 118 | __all__ = [ 119 | 'init', 120 | 'cleanup', 121 | 'set_defaults', 122 | 'add_extra_exmods', 123 | 'clear_extra_exmods', 124 | 'get_allowed_exmods', 125 | 'RequestContextSerializer', 126 | 'get_client', 127 | 'get_server', 128 | 'get_notifier', 129 | ] 130 | 131 | profiler = importutils.try_import("osprofiler.profiler") 132 | """, 133 | known_first_party=["nova"], 134 | ) 135 | -------------------------------------------------------------------------------- /tests/unit/profiles/test_plone.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | from ..utils import isort_test 4 | 5 | plone_isort_test = partial(isort_test, profile="plone") 6 | 7 | 8 | def test_plone_code_snippet_one(): 9 | plone_isort_test( 10 | """# -*- coding: utf-8 -*- 11 | from plone.app.multilingual.testing import PLONE_APP_MULTILINGUAL_PRESET_FIXTURE # noqa 12 | from plone.app.robotframework.testing import REMOTE_LIBRARY_BUNDLE_FIXTURE 13 | from plone.app.testing import FunctionalTesting 14 | from plone.app.testing import IntegrationTesting 15 | from plone.app.testing import PloneWithPackageLayer 16 | from plone.testing import z2 17 | 18 | import plone.app.multilingualindexes 19 | 20 | 21 | PAMI_FIXTURE = PloneWithPackageLayer( 22 | bases=(PLONE_APP_MULTILINGUAL_PRESET_FIXTURE,), 23 | name="PAMILayer:Fixture", 24 | gs_profile_id="plone.app.multilingualindexes:default", 25 | zcml_package=plone.app.multilingualindexes, 26 | zcml_filename="configure.zcml", 27 | additional_z2_products=["plone.app.multilingualindexes"], 28 | ) 29 | """ 30 | ) 31 | 32 | 33 | def test_plone_code_snippet_two(): 34 | plone_isort_test( 35 | """# -*- coding: utf-8 -*- 36 | from Acquisition import aq_base 37 | from App.class_init import InitializeClass 38 | from App.special_dtml import DTMLFile 39 | from BTrees.OOBTree import OOTreeSet 40 | from logging import getLogger 41 | from plone import api 42 | from plone.app.multilingual.events import ITranslationRegisteredEvent 43 | from plone.app.multilingual.interfaces import ITG 44 | from plone.app.multilingual.interfaces import ITranslatable 45 | from plone.app.multilingual.interfaces import ITranslationManager 46 | from plone.app.multilingualindexes.utils import get_configuration 47 | from plone.indexer.interfaces import IIndexableObject 48 | from Products.CMFPlone.utils import safe_hasattr 49 | from Products.DateRecurringIndex.index import DateRecurringIndex 50 | from Products.PluginIndexes.common.UnIndex import UnIndex 51 | from Products.ZCatalog.Catalog import Catalog 52 | from ZODB.POSException import ConflictError 53 | from zope.component import getMultiAdapter 54 | from zope.component import queryAdapter 55 | from zope.globalrequest import getRequest 56 | 57 | 58 | logger = getLogger(__name__) 59 | """ 60 | ) 61 | 62 | 63 | def test_plone_code_snippet_three(): 64 | plone_isort_test( 65 | """# -*- coding: utf-8 -*- 66 | from plone.app.querystring.interfaces import IQueryModifier 67 | from zope.interface import provider 68 | 69 | import logging 70 | 71 | 72 | logger = logging.getLogger(__name__) 73 | 74 | """ 75 | ) 76 | -------------------------------------------------------------------------------- /tests/unit/profiles/test_pycharm.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | from ..utils import isort_test 4 | 5 | pycharm_isort_test = partial(isort_test, profile="pycharm") 6 | 7 | 8 | def test_pycharm_snippet_one(): 9 | pycharm_isort_test( 10 | """import shutil 11 | import sys 12 | from io import StringIO 13 | from pathlib import Path 14 | from typing import ( 15 | Optional, 16 | TextIO, 17 | Union, 18 | cast 19 | ) 20 | from warnings import warn 21 | 22 | from isort import core 23 | 24 | from . import io 25 | from .exceptions import ( 26 | ExistingSyntaxErrors, 27 | FileSkipComment, 28 | FileSkipSetting, 29 | IntroducedSyntaxErrors 30 | ) 31 | from .format import ( 32 | ask_whether_to_apply_changes_to_file, 33 | create_terminal_printer, 34 | show_unified_diff 35 | ) 36 | from .io import Empty 37 | from .place import module as place_module # noqa: F401 38 | from .place import module_with_reason as place_module_with_reason # noqa: F401 39 | from .settings import ( 40 | DEFAULT_CONFIG, 41 | Config 42 | ) 43 | 44 | 45 | def sort_code_string( 46 | code: str, 47 | extension: Optional[str] = None, 48 | config: Config = DEFAULT_CONFIG, 49 | file_path: Optional[Path] = None, 50 | disregard_skip: bool = False, 51 | show_diff: Union[bool, TextIO] = False, 52 | **config_kwargs, 53 | ): 54 | """ 55 | ) 56 | -------------------------------------------------------------------------------- /tests/unit/profiles/test_wemake.py: -------------------------------------------------------------------------------- 1 | """A set of test cases for the wemake isort profile. 2 | 3 | Snippets are taken directly from the wemake-python-styleguide project here: 4 | https://github.com/wemake-services/wemake-python-styleguide 5 | """ 6 | 7 | from functools import partial 8 | 9 | from ..utils import isort_test 10 | 11 | wemake_isort_test = partial( 12 | isort_test, profile="wemake", known_first_party=["wemake_python_styleguide"] 13 | ) 14 | 15 | 16 | def test_wemake_snippet_one(): 17 | wemake_isort_test( 18 | """ 19 | import ast 20 | import tokenize 21 | import traceback 22 | from typing import ClassVar, Iterator, Sequence, Type 23 | 24 | from flake8.options.manager import OptionManager 25 | from typing_extensions import final 26 | 27 | from wemake_python_styleguide import constants, types 28 | from wemake_python_styleguide import version as pkg_version 29 | from wemake_python_styleguide.options.config import Configuration 30 | from wemake_python_styleguide.options.validation import validate_options 31 | from wemake_python_styleguide.presets.types import file_tokens as tokens_preset 32 | from wemake_python_styleguide.presets.types import filename as filename_preset 33 | from wemake_python_styleguide.presets.types import tree as tree_preset 34 | from wemake_python_styleguide.transformations.ast_tree import transform 35 | from wemake_python_styleguide.violations import system 36 | from wemake_python_styleguide.visitors import base 37 | 38 | VisitorClass = Type[base.BaseVisitor] 39 | """ 40 | ) 41 | 42 | 43 | def test_wemake_snippet_two(): 44 | wemake_isort_test( 45 | """ 46 | from collections import defaultdict 47 | from typing import ClassVar, DefaultDict, List 48 | 49 | from flake8.formatting.base import BaseFormatter 50 | from flake8.statistics import Statistics 51 | from flake8.style_guide import Violation 52 | from pygments import highlight 53 | from pygments.formatters import TerminalFormatter 54 | from pygments.lexers import PythonLexer 55 | from typing_extensions import Final 56 | 57 | from wemake_python_styleguide.version import pkg_version 58 | 59 | #: That url is generated and hosted by Sphinx. 60 | DOCS_URL_TEMPLATE: Final = ( 61 | 'https://wemake-python-stylegui.de/en/{0}/pages/usage/violations/' 62 | ) 63 | """ 64 | ) 65 | 66 | 67 | def test_wemake_snippet_three(): 68 | wemake_isort_test( 69 | """ 70 | import ast 71 | 72 | from pep8ext_naming import NamingChecker 73 | from typing_extensions import final 74 | 75 | from wemake_python_styleguide.transformations.ast.bugfixes import ( 76 | fix_async_offset, 77 | fix_line_number, 78 | ) 79 | from wemake_python_styleguide.transformations.ast.enhancements import ( 80 | set_if_chain, 81 | set_node_context, 82 | ) 83 | 84 | 85 | @final 86 | class _ClassVisitor(ast.NodeVisitor): ... 87 | """ 88 | ) 89 | 90 | 91 | def test_wemake_snippet_four(): 92 | """80 line length should not be fixed""" 93 | wemake_isort_test( 94 | """ 95 | from typing import Iterable, Iterator, Optional, Sequence, Tuple, TypeVar, Union 96 | """, 97 | """ 98 | from typing import Iterable, Iterator, Optional, Sequence, Tuple, TypeVar, Union 99 | """, 100 | ) 101 | 102 | 103 | def test_wemake_snippet_five(): 104 | """81 line length should be fixed""" 105 | wemake_isort_test( 106 | """ 107 | from typing import Iterable, Iterator, Optional, Sequence, Tuple, TypeVar, Union1 108 | """, 109 | """ 110 | from typing import ( 111 | Iterable, 112 | Iterator, 113 | Optional, 114 | Sequence, 115 | Tuple, 116 | TypeVar, 117 | Union1, 118 | ) 119 | """, 120 | ) 121 | -------------------------------------------------------------------------------- /tests/unit/test_action_comments.py: -------------------------------------------------------------------------------- 1 | """Tests for isort action comments, such as isort: skip""" 2 | 3 | import isort 4 | 5 | 6 | def test_isort_off_and_on(): 7 | """Test so ensure isort: off action comment and associated on action comment work together""" 8 | 9 | # as top of file comment 10 | assert ( 11 | isort.code( 12 | """# isort: off 13 | import a 14 | import a 15 | 16 | # isort: on 17 | import a 18 | import a 19 | """ 20 | ) 21 | == """# isort: off 22 | import a 23 | import a 24 | 25 | # isort: on 26 | import a 27 | """ 28 | ) 29 | # as middle comment 30 | assert ( 31 | isort.code( 32 | """ 33 | import a 34 | import a 35 | 36 | # isort: off 37 | import a 38 | import a 39 | """ 40 | ) 41 | == """ 42 | import a 43 | 44 | # isort: off 45 | import a 46 | import a 47 | """ 48 | ) 49 | -------------------------------------------------------------------------------- /tests/unit/test_api.py: -------------------------------------------------------------------------------- 1 | """Tests the isort API module""" 2 | 3 | import os 4 | from io import StringIO 5 | from unittest.mock import MagicMock, patch 6 | 7 | import pytest 8 | 9 | from isort import ImportKey, api 10 | from isort.settings import Config 11 | 12 | imperfect_content = "import b\nimport a\n" 13 | fixed_content = "import a\nimport b\n" 14 | fixed_diff = "+import a\n import b\n-import a\n" 15 | 16 | 17 | @pytest.fixture 18 | def imperfect(tmpdir): 19 | imperfect_file = tmpdir.join("test_needs_changes.py") 20 | imperfect_file.write_text(imperfect_content, "utf8") 21 | return imperfect_file 22 | 23 | 24 | def test_sort_file_with_bad_syntax(tmpdir) -> None: 25 | tmp_file = tmpdir.join("test_bad_syntax.py") 26 | tmp_file.write_text("""print('mismatching quotes")""", "utf8") 27 | with pytest.warns(UserWarning): 28 | api.sort_file(tmp_file, atomic=True) 29 | with pytest.warns(UserWarning): 30 | api.sort_file(tmp_file, atomic=True, write_to_stdout=True) 31 | 32 | 33 | def test_sort_file(imperfect) -> None: 34 | assert api.sort_file(imperfect) 35 | assert imperfect.read() == fixed_content 36 | 37 | 38 | def test_sort_file_in_place(imperfect) -> None: 39 | assert api.sort_file(imperfect, overwrite_in_place=True) 40 | assert imperfect.read() == fixed_content 41 | 42 | 43 | def test_sort_file_to_stdout(capsys, imperfect) -> None: 44 | assert api.sort_file(imperfect, write_to_stdout=True) 45 | out, _ = capsys.readouterr() 46 | assert out == fixed_content.replace("\n", os.linesep) 47 | 48 | 49 | def test_other_ask_to_apply(imperfect) -> None: 50 | # First show diff, but ensure change won't get written by asking to apply 51 | # and ensuring answer is no. 52 | with patch("isort.format.input", MagicMock(return_value="n")): 53 | assert not api.sort_file(imperfect, ask_to_apply=True) 54 | assert imperfect.read() == imperfect_content 55 | 56 | # Then run again, but apply the change (answer is yes) 57 | with patch("isort.format.input", MagicMock(return_value="y")): 58 | assert api.sort_file(imperfect, ask_to_apply=True) 59 | assert imperfect.read() == fixed_content 60 | 61 | 62 | def test_check_file_no_changes(capsys, tmpdir) -> None: 63 | perfect = tmpdir.join("test_no_changes.py") 64 | perfect.write_text("import a\nimport b\n", "utf8") 65 | assert api.check_file(perfect, show_diff=True) 66 | out, _ = capsys.readouterr() 67 | assert not out 68 | 69 | 70 | def test_check_file_with_changes(capsys, imperfect) -> None: 71 | assert not api.check_file(imperfect, show_diff=True) 72 | out, _ = capsys.readouterr() 73 | assert fixed_diff.replace("\n", os.linesep) in out 74 | 75 | 76 | def test_sorted_imports_multiple_configs() -> None: 77 | with pytest.raises(ValueError, match="You can either specify custom configuration options"): 78 | api.sort_code_string("import os", config=Config(line_length=80), line_length=80) 79 | 80 | 81 | def test_diff_stream() -> None: 82 | output = StringIO() 83 | assert api.sort_stream(StringIO("import b\nimport a\n"), output, show_diff=True) 84 | output.seek(0) 85 | assert fixed_diff in output.read() 86 | 87 | 88 | def test_sort_code_string_mixed_newlines(): 89 | assert api.sort_code_string("import A\n\r\nimportA\n\n") == "import A\r\n\r\nimportA\r\n\n" 90 | 91 | 92 | def test_find_imports_in_file(imperfect): 93 | found_imports = list(api.find_imports_in_file(imperfect)) 94 | assert "b" in [found_import.module for found_import in found_imports] 95 | 96 | 97 | def test_find_imports_in_file_error(tmpdir): 98 | test_path = tmpdir.join("test_path.py") 99 | test_path.mkdir() 100 | with pytest.warns(UserWarning): 101 | assert not list(api.find_imports_in_file(test_path)) 102 | 103 | 104 | def test_find_imports_in_code(): 105 | code = """ 106 | from x.y import z as a 107 | from x.y import z as a 108 | from x.y import z 109 | import x.y 110 | import x 111 | """ 112 | assert len(list(api.find_imports_in_code(code))) == 5 113 | assert len(list(api.find_imports_in_code(code, unique=True))) == 4 114 | assert len(list(api.find_imports_in_code(code, unique=ImportKey.ATTRIBUTE))) == 3 115 | assert len(list(api.find_imports_in_code(code, unique=ImportKey.MODULE))) == 2 116 | assert len(list(api.find_imports_in_code(code, unique=ImportKey.PACKAGE))) == 1 117 | -------------------------------------------------------------------------------- /tests/unit/test_comments.py: -------------------------------------------------------------------------------- 1 | from hypothesis import given 2 | from hypothesis import strategies as st 3 | 4 | import isort.comments 5 | 6 | 7 | def test_add_to_line(): 8 | assert ( 9 | isort.comments.add_to_line([], "import os # comment", removed=True).strip() == "import os" 10 | ) 11 | 12 | 13 | # These tests were written by the `hypothesis.extra.ghostwriter` module 14 | # and is provided under the Creative Commons Zero public domain dedication. 15 | 16 | 17 | @given( 18 | comments=st.one_of(st.none(), st.lists(st.text())), 19 | original_string=st.text(), 20 | removed=st.booleans(), 21 | comment_prefix=st.text(), 22 | ) 23 | def test_fuzz_add_to_line(comments, original_string, removed, comment_prefix): 24 | isort.comments.add_to_line( 25 | comments=comments, 26 | original_string=original_string, 27 | removed=removed, 28 | comment_prefix=comment_prefix, 29 | ) 30 | 31 | 32 | @given(line=st.text()) 33 | def test_fuzz_parse(line): 34 | isort.comments.parse(line=line) 35 | -------------------------------------------------------------------------------- /tests/unit/test_exceptions.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | 3 | from isort import exceptions 4 | 5 | 6 | class TestISortError: 7 | def setup_class(self): 8 | self.instance = exceptions.ISortError() 9 | 10 | def test_init(self): 11 | assert isinstance(self.instance, exceptions.ISortError) 12 | 13 | def test_pickleable(self): 14 | assert isinstance(pickle.loads(pickle.dumps(self.instance)), exceptions.ISortError) 15 | 16 | 17 | class TestExistingSyntaxErrors(TestISortError): 18 | def setup_class(self): 19 | self.instance: exceptions.ExistingSyntaxErrors = exceptions.ExistingSyntaxErrors( 20 | "file_path" 21 | ) 22 | 23 | def test_variables(self): 24 | assert self.instance.file_path == "file_path" 25 | 26 | 27 | class TestIntroducedSyntaxErrors(TestISortError): 28 | def setup_class(self): 29 | self.instance: exceptions.IntroducedSyntaxErrors = exceptions.IntroducedSyntaxErrors( 30 | "file_path" 31 | ) 32 | 33 | def test_variables(self): 34 | assert self.instance.file_path == "file_path" 35 | 36 | 37 | class TestFileSkipped(TestISortError): 38 | def setup_class(self): 39 | self.instance: exceptions.FileSkipped = exceptions.FileSkipped("message", "file_path") 40 | 41 | def test_variables(self): 42 | assert self.instance.file_path == "file_path" 43 | assert str(self.instance) == "message" 44 | 45 | 46 | class TestFileSkipComment(TestISortError): 47 | def setup_class(self): 48 | self.instance: exceptions.FileSkipComment = exceptions.FileSkipComment("file_path") 49 | 50 | def test_variables(self): 51 | assert self.instance.file_path == "file_path" 52 | 53 | 54 | class TestFileSkipSetting(TestISortError): 55 | def setup_class(self): 56 | self.instance: exceptions.FileSkipSetting = exceptions.FileSkipSetting("file_path") 57 | 58 | def test_variables(self): 59 | assert self.instance.file_path == "file_path" 60 | 61 | 62 | class TestProfileDoesNotExist(TestISortError): 63 | def setup_class(self): 64 | self.instance: exceptions.ProfileDoesNotExist = exceptions.ProfileDoesNotExist("profile") 65 | 66 | def test_variables(self): 67 | assert self.instance.profile == "profile" 68 | 69 | 70 | class TestSortingFunctionDoesNotExist(TestISortError): 71 | def setup_class(self): 72 | self.instance: exceptions.SortingFunctionDoesNotExist = ( 73 | exceptions.SortingFunctionDoesNotExist("round", ["square", "peg"]) 74 | ) 75 | 76 | def test_variables(self): 77 | assert self.instance.sort_order == "round" 78 | assert self.instance.available_sort_orders == ["square", "peg"] 79 | 80 | 81 | class TestLiteralParsingFailure(TestISortError): 82 | def setup_class(self): 83 | self.instance: exceptions.LiteralParsingFailure = exceptions.LiteralParsingFailure( 84 | "x = [", SyntaxError 85 | ) 86 | 87 | def test_variables(self): 88 | assert self.instance.code == "x = [" 89 | assert self.instance.original_error is SyntaxError 90 | 91 | 92 | class TestLiteralSortTypeMismatch(TestISortError): 93 | def setup_class(self): 94 | self.instance: exceptions.LiteralSortTypeMismatch = exceptions.LiteralSortTypeMismatch( 95 | tuple, list 96 | ) 97 | 98 | def test_variables(self): 99 | assert self.instance.kind is tuple 100 | assert self.instance.expected_kind is list 101 | 102 | 103 | class TestAssignmentsFormatMismatch(TestISortError): 104 | def setup_class(self): 105 | self.instance: exceptions.AssignmentsFormatMismatch = exceptions.AssignmentsFormatMismatch( 106 | "print x" 107 | ) 108 | 109 | def test_variables(self): 110 | assert self.instance.code == "print x" 111 | 112 | 113 | class TestUnsupportedSettings(TestISortError): 114 | def setup_class(self): 115 | self.instance: exceptions.UnsupportedSettings = exceptions.UnsupportedSettings( 116 | {"apply": {"value": "true", "source": "/"}} 117 | ) 118 | 119 | def test_variables(self): 120 | assert self.instance.unsupported_settings == {"apply": {"value": "true", "source": "/"}} 121 | 122 | 123 | class TestUnsupportedEncoding(TestISortError): 124 | def setup_class(self): 125 | self.instance: exceptions.UnsupportedEncoding = exceptions.UnsupportedEncoding("file.py") 126 | 127 | def test_variables(self): 128 | assert self.instance.filename == "file.py" 129 | -------------------------------------------------------------------------------- /tests/unit/test_files.py: -------------------------------------------------------------------------------- 1 | from isort import files 2 | from isort.settings import DEFAULT_CONFIG 3 | 4 | 5 | def test_find(tmpdir): 6 | tmp_file = tmpdir.join("file.py") 7 | tmp_file.write("import os, sys\n") 8 | assert tuple(files.find((tmp_file,), DEFAULT_CONFIG, [], [])) == (tmp_file,) 9 | -------------------------------------------------------------------------------- /tests/unit/test_hooks.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | from unittest.mock import MagicMock, patch 4 | 5 | from isort import exceptions, hooks 6 | 7 | 8 | def test_git_hook(src_dir): 9 | """Simple smoke level testing of git hooks""" 10 | 11 | # Ensure correct subprocess command is called 12 | with patch("subprocess.run", MagicMock()) as run_mock: 13 | hooks.git_hook() 14 | run_mock.assert_called_once() 15 | assert run_mock.call_args[0][0] == [ 16 | "git", 17 | "diff-index", 18 | "--cached", 19 | "--name-only", 20 | "--diff-filter=ACMRTUXB", 21 | "HEAD", 22 | ] 23 | 24 | with patch("subprocess.run", MagicMock()) as run_mock: 25 | hooks.git_hook(lazy=True) 26 | run_mock.assert_called_once() 27 | assert run_mock.call_args[0][0] == [ 28 | "git", 29 | "diff-index", 30 | "--name-only", 31 | "--diff-filter=ACMRTUXB", 32 | "HEAD", 33 | ] 34 | 35 | # Test that non python files aren't processed 36 | with patch( 37 | "isort.hooks.get_lines", 38 | MagicMock(return_value=["README.md", "setup.cfg", "LICDENSE", "mkdocs.yml", "test"]), 39 | ): 40 | with patch("subprocess.run", MagicMock()) as run_mock: 41 | hooks.git_hook(modify=True) 42 | run_mock.assert_not_called() 43 | 44 | mock_main_py = MagicMock(return_value=[os.path.join(src_dir, "main.py")]) 45 | 46 | mock_imperfect = MagicMock() 47 | mock_imperfect.return_value.stdout = b"import b\nimport a" 48 | 49 | # Test with incorrectly sorted file returned from git 50 | with patch("isort.hooks.get_lines", mock_main_py): 51 | with patch("subprocess.run", mock_imperfect): 52 | with patch("isort.api.sort_file", MagicMock(return_value=False)) as api_mock: 53 | hooks.git_hook(modify=True) 54 | api_mock.assert_called_once() 55 | assert api_mock.call_args[0][0] == mock_main_py.return_value[0] 56 | 57 | # Test with sorted file returned from git and modify=False 58 | with patch("isort.hooks.get_lines", mock_main_py): 59 | with patch("subprocess.run", mock_imperfect): 60 | with patch("isort.api.sort_file", MagicMock(return_value=False)) as api_mock: 61 | hooks.git_hook(modify=False) 62 | api_mock.assert_not_called() 63 | 64 | # Test with skipped file returned from git 65 | with patch( 66 | "isort.hooks.get_lines", MagicMock(return_value=[os.path.join(src_dir, "main.py")]) 67 | ) as run_mock: 68 | 69 | class FakeProcessResponse: 70 | stdout = b"# isort: skip-file\nimport b\nimport a\n" 71 | 72 | with patch("subprocess.run", MagicMock(return_value=FakeProcessResponse())) as run_mock: 73 | with patch("isort.api", MagicMock(side_effect=exceptions.FileSkipped("", ""))): 74 | hooks.git_hook(modify=True) 75 | 76 | 77 | def test_git_hook_uses_the_configuration_file_specified_in_settings_path(tmp_path: Path) -> None: 78 | subdirectory_path = tmp_path / "subdirectory" 79 | configuration_file_path = subdirectory_path / ".isort.cfg" 80 | 81 | # Inserting the modified file in the parent directory of the configuration file ensures that it 82 | # will not be found by the normal search routine 83 | modified_file_path = configuration_file_path.parent.parent / "somefile.py" 84 | 85 | # This section will be used to check that the configuration file was indeed loaded 86 | section = "testsection" 87 | 88 | os.mkdir(subdirectory_path) 89 | with open(configuration_file_path, "w") as fd: 90 | fd.write("[isort]\n") 91 | fd.write(f"sections={section}") 92 | 93 | with open(modified_file_path, "w") as fd: 94 | pass 95 | 96 | files_modified = [str(modified_file_path.absolute())] 97 | with patch("isort.hooks.get_lines", MagicMock(return_value=files_modified)): 98 | with patch("isort.hooks.get_output", MagicMock(return_value="")): 99 | with patch("isort.api.check_code_string", MagicMock()) as run_mock: 100 | hooks.git_hook(settings_file=str(configuration_file_path)) 101 | 102 | assert run_mock.call_args[1]["config"].sections == (section,) 103 | -------------------------------------------------------------------------------- /tests/unit/test_importable.py: -------------------------------------------------------------------------------- 1 | """Basic set of tests to ensure entire code base is importable""" 2 | 3 | import pytest 4 | 5 | 6 | def test_importable(): 7 | """Simple smoketest to ensure all isort modules are importable""" 8 | 9 | import isort 10 | import isort._version 11 | import isort.api 12 | import isort.comments 13 | import isort.deprecated.finders 14 | import isort.exceptions 15 | import isort.format 16 | import isort.hooks 17 | import isort.logo 18 | import isort.main 19 | import isort.output 20 | import isort.parse 21 | import isort.place 22 | import isort.profiles 23 | import isort.pylama_isort 24 | import isort.sections 25 | import isort.settings 26 | import isort.setuptools_commands 27 | import isort.sorting 28 | import isort.stdlibs 29 | import isort.stdlibs.all 30 | import isort.stdlibs.py2 31 | import isort.stdlibs.py3 32 | import isort.stdlibs.py27 33 | import isort.stdlibs.py36 34 | import isort.stdlibs.py37 35 | import isort.stdlibs.py38 36 | import isort.stdlibs.py39 37 | import isort.stdlibs.py310 38 | import isort.stdlibs.py311 39 | import isort.stdlibs.py312 40 | import isort.stdlibs.py313 41 | import isort.utils 42 | import isort.wrap 43 | import isort.wrap_modes 44 | 45 | with pytest.raises(SystemExit): 46 | import isort.__main__ # noqa: F401 47 | -------------------------------------------------------------------------------- /tests/unit/test_io.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from unittest.mock import patch 3 | 4 | import pytest 5 | 6 | from isort import io 7 | from isort.exceptions import UnsupportedEncoding 8 | 9 | 10 | class TestFile: 11 | @pytest.mark.skipif(sys.platform == "win32", reason="Can't run file encoding test in AppVeyor") 12 | def test_read(self, tmpdir): 13 | test_file_content = """# -*- encoding: ascii -*- 14 | 15 | import Ὡ 16 | """ 17 | test_file = tmpdir.join("file.py") 18 | test_file.write(test_file_content) 19 | with pytest.raises(UnicodeDecodeError): 20 | with io.File.read(str(test_file)) as file_handler: 21 | file_handler.stream.read() 22 | 23 | def test_from_content(self, tmpdir): 24 | test_file = tmpdir.join("file.py") 25 | test_file.write_text("import os", "utf8") 26 | file_obj = io.File.from_contents("import os", filename=str(test_file)) 27 | assert file_obj 28 | assert file_obj.extension == "py" 29 | 30 | def test_open(self, tmpdir): 31 | with pytest.raises(FileNotFoundError): 32 | io.File._open("THISCANTBEAREALFILEὩὩὩὩὩὩὩὩὩὩὩὩ.ὩὩὩὩὩ") 33 | 34 | def raise_arbitrary_exception(*args, **kwargs): 35 | raise RuntimeError("test") 36 | 37 | test_file = tmpdir.join("file.py") 38 | test_file.write("import os") 39 | assert io.File._open(str(test_file)) 40 | 41 | # correctly responds to error determining encoding 42 | with patch("tokenize.detect_encoding", raise_arbitrary_exception): 43 | with pytest.raises(UnsupportedEncoding): 44 | io.File._open(str(test_file)) 45 | -------------------------------------------------------------------------------- /tests/unit/test_literal.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import isort.literal 4 | from isort import exceptions 5 | 6 | 7 | def test_value_mismatch(): 8 | with pytest.raises(exceptions.LiteralSortTypeMismatch): 9 | isort.literal.assignment("x = [1, 2, 3]", "set", "py") 10 | 11 | 12 | def test_invalid_syntax(): 13 | with pytest.raises(exceptions.LiteralParsingFailure): 14 | isort.literal.assignment("x = [1, 2, 3", "list", "py") 15 | 16 | 17 | def test_invalid_sort_type(): 18 | with pytest.raises(ValueError, match="Trying to sort using an undefined sort_type. Defined"): 19 | isort.literal.assignment("x = [1, 2, 3", "tuple-list-not-exist", "py") 20 | 21 | 22 | def test_value_assignment_assignments(): 23 | assert isort.literal.assignment("b = 1\na = 2\n", "assignments", "py") == "a = 2\nb = 1\n" 24 | 25 | 26 | def test_assignments_invalid_section(): 27 | with pytest.raises(exceptions.AssignmentsFormatMismatch): 28 | isort.literal.assignment("\n\nx = 1\nx++", "assignments", "py") 29 | -------------------------------------------------------------------------------- /tests/unit/test_output.py: -------------------------------------------------------------------------------- 1 | from hypothesis import given, reject 2 | from hypothesis import strategies as st 3 | 4 | import isort.comments 5 | 6 | 7 | @given( 8 | comments=st.one_of(st.none(), st.lists(st.text())), 9 | original_string=st.text(), 10 | removed=st.booleans(), 11 | comment_prefix=st.text(), 12 | ) 13 | def test_fuzz_add_to_line(comments, original_string, removed, comment_prefix): 14 | try: 15 | isort.comments.add_to_line( 16 | comments=comments, 17 | original_string=original_string, 18 | removed=removed, 19 | comment_prefix=comment_prefix, 20 | ) 21 | except ValueError: 22 | reject() 23 | -------------------------------------------------------------------------------- /tests/unit/test_parse.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from hypothesis import given 3 | from hypothesis import strategies as st 4 | 5 | from isort import parse 6 | from isort.settings import Config 7 | 8 | TEST_CONTENTS = """ 9 | import xyz 10 | import abc 11 | import (\\ # one 12 | one as \\ # two 13 | three) 14 | import \\ 15 | zebra as \\ # one 16 | not_bacon 17 | from x import (\\ # one 18 | one as \\ # two 19 | three) 20 | 21 | 22 | def function(): 23 | pass 24 | """ 25 | 26 | 27 | def test_file_contents(): 28 | ( 29 | in_lines, 30 | out_lines, 31 | import_index, 32 | _, 33 | _, 34 | _, 35 | _, 36 | _, 37 | change_count, 38 | original_line_count, 39 | _, 40 | _, 41 | _, 42 | _, 43 | ) = parse.file_contents(TEST_CONTENTS, config=Config(default_section="")) 44 | assert "\n".join(in_lines) == TEST_CONTENTS 45 | assert "import" not in "\n".join(out_lines) 46 | assert import_index == 1 47 | assert change_count == -11 48 | assert original_line_count == len(in_lines) 49 | 50 | 51 | # These tests were written by the `hypothesis.extra.ghostwriter` module 52 | # and is provided under the Creative Commons Zero public domain dedication. 53 | 54 | 55 | @given(contents=st.text()) 56 | def test_fuzz__infer_line_separator(contents): 57 | parse._infer_line_separator(contents=contents) 58 | 59 | 60 | @given(import_string=st.text()) 61 | def test_fuzz__strip_syntax(import_string): 62 | parse.strip_syntax(import_string=import_string) 63 | 64 | 65 | @given(line=st.text(), config=st.builds(Config)) 66 | def test_fuzz_import_type(line, config): 67 | parse.import_type(line=line, config=config) 68 | 69 | 70 | @given( 71 | line=st.text(), 72 | in_quote=st.text(), 73 | index=st.integers(), 74 | section_comments=st.lists(st.text()), 75 | needs_import=st.booleans(), 76 | ) 77 | def test_fuzz_skip_line(line, in_quote, index, section_comments, needs_import): 78 | parse.skip_line( 79 | line=line, 80 | in_quote=in_quote, 81 | index=index, 82 | section_comments=section_comments, 83 | needs_import=needs_import, 84 | ) 85 | 86 | 87 | @pytest.mark.parametrize( 88 | ("raw_line", "expected"), 89 | [ 90 | ("from . cimport a", "from . cimport a"), 91 | ("from.cimport a", "from . cimport a"), 92 | ("from..cimport a", "from .. cimport a"), 93 | ("from . import a", "from . import a"), 94 | ("from.import a", "from . import a"), 95 | ("from..import a", "from .. import a"), 96 | ("import *", "import *"), 97 | ("import*", "import *"), 98 | ("from . import a", "from . import a"), # noqa: PT014 99 | ("from .import a", "from . import a"), 100 | ("from ..import a", "from .. import a"), 101 | ("from . cimport a", "from . cimport a"), # noqa: PT014 102 | ("from .cimport a", "from . cimport a"), 103 | ("from ..cimport a", "from .. cimport a"), 104 | ("from\t.\timport a", "from . import a"), 105 | ], 106 | ) 107 | def test_normalize_line(raw_line, expected): 108 | line, returned_raw_line = parse.normalize_line(raw_line) 109 | assert line == expected 110 | assert returned_raw_line == raw_line 111 | -------------------------------------------------------------------------------- /tests/unit/test_place.py: -------------------------------------------------------------------------------- 1 | """Tests for the isort import placement module""" 2 | 3 | from functools import partial 4 | 5 | from isort import place, sections 6 | from isort.settings import Config 7 | 8 | 9 | def test_module(src_path): 10 | place_tester = partial(place.module, config=Config(src_paths=[src_path])) 11 | assert place_tester("isort") == sections.FIRSTPARTY 12 | assert place_tester("os") == sections.STDLIB 13 | assert place_tester(".deprecated") == sections.LOCALFOLDER 14 | assert place_tester("__future__") == sections.FUTURE 15 | assert place_tester("hug") == sections.THIRDPARTY 16 | 17 | 18 | def test_extra_standard_library(src_path): 19 | place_tester = partial( 20 | place.module, config=Config(src_paths=[src_path], extra_standard_library=["hug"]) 21 | ) 22 | assert place_tester("os") == sections.STDLIB 23 | assert place_tester("hug") == sections.STDLIB 24 | 25 | 26 | def test_no_standard_library_placement(): 27 | assert place.module_with_reason( 28 | "pathlib", config=Config(sections=["THIRDPARTY"], default_section="THIRDPARTY") 29 | ) == ("THIRDPARTY", "Default option in Config or universal default.") 30 | assert place.module("pathlib") == "STDLIB" 31 | 32 | 33 | def test_namespace_package_placement(examples_path): 34 | namespace_examples = examples_path / "namespaces" 35 | 36 | implicit = namespace_examples / "implicit" 37 | pkg_resource = namespace_examples / "pkg_resource" 38 | pkgutil = namespace_examples / "pkgutil" 39 | for namespace_test in (implicit, pkg_resource, pkgutil): 40 | print(namespace_test) 41 | config = Config(settings_path=namespace_test) 42 | no_namespaces = Config(settings_path=namespace_test, auto_identify_namespace_packages=False) 43 | namespace_override = Config(settings_path=namespace_test, known_firstparty=["root.name"]) 44 | assert place.module("root.name", config=config) == "THIRDPARTY" 45 | assert place.module("root.nested", config=config) == "FIRSTPARTY" 46 | assert place.module("root.name", config=no_namespaces) == "FIRSTPARTY" 47 | assert place.module("root.name", config=namespace_override) == "FIRSTPARTY" 48 | 49 | no_namespace = namespace_examples / "none" 50 | almost_implicit = namespace_examples / "almost-implicit" 51 | weird_encoding = namespace_examples / "weird_encoding" 52 | for lacks_namespace in (no_namespace, almost_implicit, weird_encoding): 53 | config = Config(settings_path=lacks_namespace) 54 | manual_namespace = Config(settings_path=lacks_namespace, namespace_packages=["root"]) 55 | assert place.module("root.name", config=config) == "FIRSTPARTY" 56 | assert place.module("root.nested", config=config) == "FIRSTPARTY" 57 | assert place.module("root.name", config=manual_namespace) == "THIRDPARTY" 58 | assert place.module("root.nested", config=config) == "FIRSTPARTY" 59 | -------------------------------------------------------------------------------- /tests/unit/test_pylama_isort.py: -------------------------------------------------------------------------------- 1 | from isort.pylama_isort import Linter 2 | 3 | 4 | class TestLinter: 5 | instance = Linter() 6 | 7 | def test_allow(self): 8 | assert not self.instance.allow("test_case.pyc") 9 | assert not self.instance.allow("test_case.c") 10 | assert self.instance.allow("test_case.py") 11 | 12 | def test_run(self, tmpdir): 13 | correct = tmpdir.join("incorrect.py") 14 | correct.write("import a\nimport b\n") 15 | assert not self.instance.run(str(correct)) 16 | 17 | incorrect = tmpdir.join("incorrect.py") 18 | incorrect.write("import b\nimport a\n") 19 | assert self.instance.run(str(incorrect)) 20 | 21 | def test_skip(self, tmpdir): 22 | incorrect = tmpdir.join("incorrect.py") 23 | incorrect.write("# isort: skip_file\nimport b\nimport a\n") 24 | assert not self.instance.run(str(incorrect)) 25 | -------------------------------------------------------------------------------- /tests/unit/test_setuptools_command.py: -------------------------------------------------------------------------------- 1 | from isort import setuptools_commands 2 | 3 | 4 | def test_isort_command_smoke(src_dir): 5 | """A basic smoke test for the setuptools_commands command""" 6 | from setuptools.dist import Distribution 7 | 8 | command = setuptools_commands.ISortCommand(Distribution()) 9 | command.distribution.packages = ["isort"] 10 | command.distribution.package_dir = {"isort": src_dir} 11 | command.initialize_options() 12 | command.finalize_options() 13 | try: 14 | command.run() 15 | except SystemExit: 16 | pass 17 | 18 | command.distribution.package_dir = {"": "isort"} 19 | command.distribution.py_modules = ["one", "two"] 20 | command.initialize_options() 21 | command.finalize_options() 22 | command.run() 23 | 24 | command.distribution.packages = ["not_a_file"] 25 | command.distribution.package_dir = {"not_a_file": src_dir} 26 | command.initialize_options() 27 | command.finalize_options() 28 | try: 29 | command.run() 30 | except SystemExit: 31 | pass 32 | -------------------------------------------------------------------------------- /tests/unit/test_utils.py: -------------------------------------------------------------------------------- 1 | from isort.utils import Trie 2 | 3 | 4 | def test_trie(): 5 | trie_root = Trie("default", {"line_length": 70}) 6 | 7 | trie_root.insert("/temp/config1/.isort.cfg", {"line_length": 71}) 8 | trie_root.insert("/temp/config2/setup.cfg", {"line_length": 72}) 9 | trie_root.insert("/temp/config3/pyproject.toml", {"line_length": 73}) 10 | 11 | # Ensure that appropriate configs are resolved for files in different directories 12 | config1 = trie_root.search("/temp/config1/subdir/file1.py") 13 | assert config1[0] == "/temp/config1/.isort.cfg" 14 | assert config1[1] == {"line_length": 71} 15 | 16 | config1_2 = trie_root.search("/temp/config1/file1_2.py") 17 | assert config1_2[0] == "/temp/config1/.isort.cfg" 18 | assert config1_2[1] == {"line_length": 71} 19 | 20 | config2 = trie_root.search("/temp/config2/subdir/subsubdir/file2.py") 21 | assert config2[0] == "/temp/config2/setup.cfg" 22 | assert config2[1] == {"line_length": 72} 23 | 24 | config2_2 = trie_root.search("/temp/config2/subdir/file2_2.py") 25 | assert config2_2[0] == "/temp/config2/setup.cfg" 26 | assert config2_2[1] == {"line_length": 72} 27 | 28 | config3 = trie_root.search("/temp/config3/subdir/subsubdir/subsubsubdir/file3.py") 29 | assert config3[0] == "/temp/config3/pyproject.toml" 30 | assert config3[1] == {"line_length": 73} 31 | 32 | config3_2 = trie_root.search("/temp/config3/file3.py") 33 | assert config3_2[0] == "/temp/config3/pyproject.toml" 34 | assert config3_2[1] == {"line_length": 73} 35 | 36 | config_outside = trie_root.search("/temp/file.py") 37 | assert config_outside[0] == "default" 38 | assert config_outside[1] == {"line_length": 70} 39 | -------------------------------------------------------------------------------- /tests/unit/test_wrap.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from isort import wrap 4 | from isort.settings import Config 5 | from isort.wrap_modes import WrapModes 6 | 7 | 8 | def test_import_statement(): 9 | assert wrap.import_statement("", [], []) == "" 10 | assert ( 11 | wrap.import_statement("from x import ", ["y"], [], config=Config(balanced_wrapping=True)) 12 | == "from x import (y)" 13 | ) 14 | assert ( 15 | wrap.import_statement("from long_import ", ["verylong"] * 10, []) 16 | == """from long_import (verylong, verylong, verylong, verylong, verylong, verylong, 17 | verylong, verylong, verylong, verylong)""" 18 | ) 19 | assert wrap.import_statement("from x import ", ["y", "z"], [], explode=True) == ( 20 | "from x import (\n y,\n z,\n)" 21 | ) 22 | 23 | 24 | @pytest.mark.parametrize( 25 | ("multi_line_output", "expected"), 26 | [ 27 | ( 28 | WrapModes.VERTICAL_HANGING_INDENT, # type: ignore 29 | """from a import ( 30 | b as c # comment that is long enough that this import doesn't fit in one line (parens) 31 | )""", 32 | ), 33 | ( 34 | WrapModes.VERTICAL, # type: ignore 35 | """from a import ( 36 | b as c) # comment that is long enough that this import doesn't fit in one line (parens)""", 37 | ), 38 | ], 39 | ) 40 | def test_line__comment_with_brackets__expects_unchanged_comment(multi_line_output, expected): 41 | content = ( 42 | "from a import b as c " 43 | "# comment that is long enough that this import doesn't fit in one line (parens)" 44 | ) 45 | config = Config( 46 | multi_line_output=multi_line_output, 47 | use_parentheses=True, 48 | ) 49 | 50 | assert wrap.line(content=content, line_separator="\n", config=config) == expected 51 | -------------------------------------------------------------------------------- /tests/unit/utils.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO, StringIO, TextIOWrapper 2 | 3 | import isort 4 | 5 | 6 | class UnseekableTextIOWrapper(TextIOWrapper): 7 | def seek(self, *args, **kwargs): 8 | raise ValueError("underlying stream is not seekable") 9 | 10 | 11 | class UnreadableStream(StringIO): 12 | def readable(self, *args, **kwargs) -> bool: 13 | return False 14 | 15 | 16 | def as_stream(text: str) -> UnseekableTextIOWrapper: 17 | return UnseekableTextIOWrapper(BytesIO(text.encode("utf8"))) 18 | 19 | 20 | def isort_test(code: str, expected_output: str = "", **config): 21 | """Runs isort against the given code snippet and ensures that it 22 | gives consistent output across multiple runs, and if an expected_output 23 | is given - that it matches that. 24 | """ 25 | expected_output = expected_output or code 26 | 27 | output = isort.code(code, **config) 28 | assert output == expected_output 29 | 30 | assert output == isort.code(output, **config) 31 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | env_list = 3 | coverage_erase 4 | # When updating the list of Python versions to test, 5 | # use search-and-replace to ensure that intra-environment 6 | # dependencies are maintained for parallel execution. 7 | py{3.13, 3.12, 3.11, 3.10, 3.9} 8 | coverage_report 9 | mypy 10 | 11 | 12 | [testenv:coverage_erase] 13 | description = Erase coverage files 14 | dependency_groups = 15 | dev 16 | commands = 17 | coverage erase 18 | 19 | 20 | [testenv] 21 | runner = uv-venv-lock-runner 22 | dependency_groups = 23 | dev 24 | depends = 25 | py{3.13, 3.12, 3.11, 3.10, 3.9}: coverage_erase 26 | commands = 27 | coverage run -m pytest tests/unit/ -s --ignore=tests/unit/test_deprecated_finders.py 28 | 29 | 30 | [testenv:coverage_report{,-ci}] 31 | description = Create a coverage report 32 | depends = 33 | py{3.13, 3.12, 3.11, 3.10, 3.9} 34 | dependency_groups = 35 | dev 36 | commands_pre = 37 | # `coverage combine` will fail if run twice in a row. 38 | # The '-' ignores failures. 39 | - coverage combine 40 | # Only write an HTML reports when NOT running in CI. 41 | # Also, ignore the exit code if coverage falls below the fail_under value. 42 | !ci: - coverage html 43 | commands = 44 | coverage report 45 | commands_post = 46 | # Only write an XML report when running in CI. 47 | ci: coverage xml 48 | 49 | 50 | [testenv:lint] 51 | description = Lint the project 52 | skip_install = true 53 | dependency_groups = 54 | dev 55 | commands = 56 | cruft check 57 | mypy -p isort -p tests 58 | black --target-version py39 --check . 59 | isort --profile hug --check --diff isort/ tests/ 60 | isort --profile hug --check --diff example_isort_formatting_plugin/ 61 | isort --profile hug --check --diff example_isort_sorting_plugin/ 62 | isort --profile hug --check --diff example_shared_isort_profile/ 63 | flake8 isort/ tests/ 64 | ruff check 65 | bandit -r isort/ -x isort/_vendored 66 | --------------------------------------------------------------------------------