isort-9.0.1/.codecov.yml0000644000000000000000000000022213615410400012020 0ustar00coverage: status: project: yes patch: yes changes: yes comment: layout: "header, diff" behavior: default require_changes: no isort-9.0.1/.deepsource.toml0000644000000000000000000000035613615410400012716 0ustar00version = 1 test_patterns = ["tests/**"] exclude_patterns = [ "tests/**", "scripts/**", "isort/_vendored/**", ] [[analyzers]] name = "python" enabled = true [analyzers.meta] runtime_version = "3.x.x" max_line_length = 100 isort-9.0.1/.dockerignore0000644000000000000000000000035613615410400012261 0ustar00# project build medadata that can't be used in docker Dockerfile .appveyor.yml .dockerignore .pre-commit-hooks.yaml .travis.yml .git* # documentation not needed CHANGELOG.md LICENSE MANIFEST.in .undertake example.gif logo.png art/ docs/ isort-9.0.1/.editorconfig0000644000000000000000000000046113615410400012257 0ustar00root = true [*.py] max_line_length = 100 indent_style = space indent_size = 4 known_first_party = isort known_third_party = kate ignore_frosted_errors = E103 skip = build,.tox,venv balanced_wrapping = true [*.{rst,ini}] indent_style = space indent_size = 4 [*.yml] indent_style = space indent_size = 2 isort-9.0.1/.pre-commit-config.yaml0000644000000000000000000000013513615410400014061 0ustar00repos: - repo: https://github.com/pycqa/isort rev: 5.13.2 hooks: - id: isort isort-9.0.1/.pre-commit-hooks.yaml0000644000000000000000000000040313615410400013735 0ustar00- id: isort name: isort entry: isort stages: [pre-commit, pre-merge-commit, pre-push, manual] require_serial: true language: python types_or: [cython, pyi, python] args: ['--filter-files'] minimum_pre_commit_version: '3.2.0' isort-9.0.1/.readthedocs.yaml0000644000000000000000000000070013615410400013025 0ustar00version: 2 sphinx: configuration: docs/conf.py fail_on_warning: true build: os: ubuntu-lts-latest tools: python: latest jobs: pre_create_environment: - asdf plugin add uv - asdf install uv latest - asdf global uv latest create_environment: - uv venv "${READTHEDOCS_VIRTUALENV_PATH}" install: - UV_PROJECT_ENVIRONMENT="${READTHEDOCS_VIRTUALENV_PATH}" uv sync --frozen --group docs isort-9.0.1/CHANGELOG.md0000644000000000000000000012713413615410400011422 0ustar00Changelog ========= NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](docs/major_releases/release_policy.md). Please find a complete overview of all releases on [Github](https://github.com/PyCQA/isort/releases). ## Releases ### Unreleased ### 9.0.0 August 26 2026 - Remove logic for deprecated options (#2498) @DanielNoord - Sort lazy import statements (#2503) @DanielNoord - Add initial support for Python 3.15 (#2466) @DanielNoord - Compile with `mypyc` by @DanielNoord in https://github.com/PyCQA/isort/pull/2586 - Cache calls to `posixpath.abspath` by @DanielNoord in https://github.com/PyCQA/isort/pull/2606 - Consider private `stdlib` modules to be `stdlib` (#2295) @devdanzin - Add CLI Flag for --forced-separate (https://github.com/PyCQA/isort/pull/2367) @hirak99 - Add separate_packages option (#2313) @alex-liang3 - Fix inline comment duplication across merged `from X import` lines (#2499) @copilot-swe-agent - Fix src glob patterns passed via CLI (#2497) @ReinerBRO - Fix opening-line comment moving to alias attribute line on wrapped imports (#2491) @copilot-swe-agent - Fix multi_line_output=3/5 ignored when wrapping single imports with inline comments (#2474) @copilot-swe-agent - Fix false positive in `check_code` when using `float_to_top` + `add_imports` (#2492) @copilot-swe-agent - Fix: preserve bare `#` inline comments on imports (#2488) @copilot-swe-agent - Fix grouping of non-aliased imports when mixed with aliased imports from the same module (#2470) @copilot-swe-agent - Fix https://github.com/PyCQA/isort/issues/2500: trusted publishing by @staticdev in https://github.com/PyCQA/isort/pull/2521 - Fix git_hook lazy=True option by @sparrowt in https://github.com/PyCQA/isort/pull/2542 - Honor `# isort: off/on/split` during `float_to_top` preprocessing with CRLF input by @DanielNoord with @Copilot in https://github.com/PyCQA/isort/pull/2553 - Fix --sort-reexports crash with non-seekable streams (e.g. stdin) by @Abdu-Ahmed in https://github.com/PyCQA/isort/pull/2547 - Fix non-idempotent output with split_on_trailing_comma and a non-default wrap mode by @sarathfrancis90 in https://github.com/PyCQA/isort/pull/2554 - Fix non-idempotent NOQA wrap mode with imports that carry their own comment by @sarathfrancis90 in https://github.com/PyCQA/isort/pull/2556 - Fix NOQA wrap mode accumulating spaces before the comment on each run by @sarathfrancis90 in https://github.com/PyCQA/isort/pull/2558 - Fix NOQA not added to long imports in several output paths by @urayoru113 in https://github.com/PyCQA/isort/pull/2568 - Honor `# isort: skip` when a `__future__` import is present (#2092) by @apoorvdarshan in https://github.com/PyCQA/isort/pull/2574 - fix: make sorted `__all__` and literals black-compatible (#2280) by @lord-haffi in https://github.com/PyCQA/isort/pull/2576 - Honor skip comments when sorting reexports by @sakshichitnis27 in https://github.com/PyCQA/isort/pull/2581 - Keep aliased import when the plain name carries a comment by @sarathfrancis90 in https://github.com/PyCQA/isort/pull/2567 - fix: don't crash on --config-root without --resolve-all-configs by @Gooh456 in https://github.com/PyCQA/isort/pull/2597 - fix: check_code misses lines_before_imports-only changes (#2242) by @gaoflow in https://github.com/PyCQA/isort/pull/2563 - Fix preservation of form-feed blank lines by @utkarshalpha in https://github.com/PyCQA/isort/pull/2599 - Small clean up and fix inconsistency in handling of `*` imports by @DanielNoord in https://github.com/PyCQA/isort/pull/2619 - Fix word-wrapping of 'from ... import *' into invalid Python (#2267) by @sudorm-rf0 in https://github.com/PyCQA/isort/pull/2624 - Fix pylint disable-next comments at the start of imports by @Neallin-917 in https://github.com/PyCQA/isort/pull/2628 - Keep add_imports below a prefixed module docstring by @Eljees in https://github.com/PyCQA/isort/pull/2626 - Add read the docs configuration (#2504) @DanielNoord - Remove unused and broken dependencies (#2517) @DanielNoord - Remove `Any` from `parse.py` (#2516) @DanielNoord - Bring documentation in line with old documentation (#2507) @DanielNoord - Sync profile docs with implementation (#2495) @copilot-swe-agent - Fix the playground (#2494) @DanielNoord7) @hirak99 - Remove unused _ENCODING_PATTERN regex and re import by @duriantaco in https://github.com/PyCQA/isort/pull/2525 - Remove references to defunct `git_ignore` config by @sparrowt in https://github.com/PyCQA/isort/pull/2531 - Fix broken relative links in README and CHANGELOG for both GitHub and Sphinx by @rohitshinde08 in https://github.com/PyCQA/isort/pull/2530 - Bump vendored `tomli` by @DanielNoord in https://github.com/PyCQA/isort/pull/2579 - Document temporary .isorted files by @sapunyangkut in https://github.com/PyCQA/isort/pull/2580 - Remove `cruft` by @DanielNoord in https://github.com/PyCQA/isort/pull/2610 - Small fixes in preparation for `mypyc` compiled wheels by @DanielNoord in https://github.com/PyCQA/isort/pull/2623 - Small fixes in preparation for compiling with `mypyc` by @DanielNoord in https://github.com/PyCQA/isort/pull/2625 - Fix `test_importable` for local dev by @DanielNoord in https://github.com/PyCQA/isort/pull/2635 ### 8.0.1 February 28 2026 - Fix #2461: Added compression to stdlibs for Python 3.14 in isort/stdlibs/py314.py (#2463) @FinlayTheBerry - Fix unindented comments being corrupted in indented blocks (#2459) @worksbyfriday ### 8.0.0 February 19 2026 - Removed `--old-finders` and `--magic-placement` flags and `old_finders` configuration option. The legacy finder logic that relied on environment introspection has been removed (#2445) @joao-faria-dev - Update the `plone` profile to not clash with `black` (#2456) @ale-rt ### 6.1.0 October 1 2025 - Add python 3.14 classifier and badge (#2409) @staticdev - Drop use of non-standard pkg_resources API (#2405) @dvarrazzo ### 6.0.1 Febuary 26 2025 - Add OSError handling in find_imports_in_file (#2331) @kobarity ### 6.0.0 January 27 2025 - Remove support for Python 3.8 (#2327) @DanielNoord - Python 3.13 support (#2306) @mayty - Speed up exists_case_sensitive calls (#2264) @correctmost - Ensure that split_on_trailing_comma works with as imports (#2340) @DanielNoord - Black profile: enable magic comma (#2236) @MrMino - Update line_length and single_line_exclusions in google profile (#2149) @jagapiou - Allow --diff to be used with --jobs (#2302) @mnakama - Fix wemake profile to have correct character limit (#2241) @sobolevn - Fix sort_reexports code mangling (#2283) @Helveg - Fix correct group by package tokenization (#2136) @glasnt ### 5.13.2 December 13 2023 - Apply the bracket fix from issue #471 only for use_parentheses=True (#2184) @bp72 - Confine pre-commit to stages (#2213) @davidculley - Fixed colors extras (#2212) @staticdev ### 5.13.1 December 11 2023 - Fixed integration tests (#2208) @bp72 - Fixed normalizing imports from more than one level of parent modules (issue/2152) (#2191) @bp72 - Remove optional dependencies without extras (#2207) @staticdev ### 5.13.0 December 9 2023 - Cleanup deprecated extras (#2089) @staticdev - Fixed #1989: settings lookup when working in stream based mode - Fixed 80 line length for wemake linter (#2183) @skatromb - Add support for Python 3.12 (#2175) @hugovk - Fixed: add newest version to pre-commit docs (#2190) @AzulGarza - Fixed assertions in test_git_hook (#2196) @mgorny - Removed check for include_trailing_comma for the Hanging Indent wrap mode (#2192) @bp72 - Use the standard library tomllib on sufficiently new python (#2202) @eli-schwartz - Update pre-commit.md version number (#2197) @nicobako - doc: Update black_compatibility.md (#2177) @JSS95 - Fixed safety sept 2023 (#2178) @staticdev - docs: fix black profile documentation (#2163) @nijel - Fixed typo: indended -> indented (#2161) @vadimkerr - Docs(configuration/options.md): fix missing trailing spaces for hard linebreak (#2157) @JoeyTeng - Update pre-commit.md (#2148) @godiard - chore: move configurations to pyproject.toml (#2115) @SauravMaheshkar - Fixed typo in README (#2112) @stefmolin - Update version in pre-commit setup to avoid installation issue with poetry (#2103) @stefmolin - Skip .pytype directory by default. (#2098) @manueljacob - Fixed a tip block styling in the Config Files section (#2097) @Klavionik - Do not cache configuration files (#1995) @kaste - Derive settings_path from --filename (#1992) @kaste - Fixed year of version 5.12.0 in CHANGELOG.md (#2082) @DjLegolas ### 5.12.0 January 28 2023 - Removed support for Python 3.7 - Fixed incompatiblity with latest poetry version - Added support for directory limitations within built in git hook ### 5.11.5 January 30 2023 [hotfix] - Fixed incompatiblity with latest poetry version ### 5.11.4 December 21 2022 - Fixed #2038 (again): stop installing documentation files to top-level site-packages (#2057) @mgorny - CI: only run release workflows for upstream (#2052) @hugovk - Tests: remove obsolete toml import from the test suite (#1978) @mgorny - CI: bump Poetry 1.3.1 (#2058) @staticdev ### 5.11.3 December 16 2022 - Fixed #2007: settings for py3.11 (#2040) @staticdev - Fixed #2038: packaging pypoetry (#2042) @staticdev - Docs: renable portray (#2043) @timothycrosley - Ci: add minimum GitHub token permissions for workflows (#1969) @varunsh-coder - Ci: general CI improvements (#2041) @staticdev - Ci: add release workflow (#2026) @staticdev ### 5.11.2 December 12 2022 - Hotfix #2034: isort --version is not accurate on 5.11.x releases (#2034) @gschaffner ### 5.11.1 December 12 2022 - Hotfix #2031: only call `colorama.init` if `colorama` is available (#2032) @tomaarsen ### 5.11.0 December 12 2022 - Added official support for Python 3.11 (#1996, #2008, #2011) @staticdev - Dropped support for Python 3.6 (#2019) @barrelful - Fixed problematic tests (#2021, #2022) @staticdev - Fixed #1960: Rich compatibility (#1961) @ofek - Fixed #1945, #1986: Python 4.0 upper bound dependency resolving issues @staticdev - Fixed Pyodide CDN URL (#1991) @andersk - Docs: clarify description of use_parentheses (#1941) @mgedmin - Fixed #1976: `black` compatibility for `.pyi` files @XuehaiPan - Implemented #1683: magic trailing comma option (#1876) @legau - Add missing space in unrecoverable exception message (#1933) @andersk - Fixed #1895: skip-gitignore: use allow list, not deny list @bmalehorn - Fixed #1917: infinite loop for unmatched parenthesis (#1919) @anirudnits - Docs: shared profiles (#1896) @matthewhughes934 - Fixed build-backend values in the example plugins (#1892) @mgorny - Remove reference to jamescurtin/isort-action (#1885) @AndrewLane - Split long cython import lines (#1931) @davidcollins001 - Update plone profile: copy of `black`, plus three settings. (#1926) @mauritsvanrees - Fixed #1815, #1862: Add a command-line flag to sort all re-exports (#1863) @parafoxia - Fixed #1854: `lines_before_imports` appending lines after comments (#1861) @legau - Remove redundant `multi_line_output = 3` from "Compatibility with black" (#1858) @jdufresne - Add tox config example (#1856) @umonaca - Docs: add examples for frozenset and tuple settings (#1822) @sgaist - Docs: add multiple config documentation (#1850) @anirudnits ### 5.10.1 November 8 2021 - Fixed #1819: Occasional inconsistency with multiple src paths. - Fixed #1840: skip_file ignored when on the first docstring line ### 5.10.0 November 3 2021 - Implemented #1796: Switch to `tomli` for pyproject.toml configuration loader. - Fixed #1801: CLI bug (--exend-skip-glob, overrides instead of extending). - Fixed #1802: respect PATH customization in nested calls to git. - Fixed #1838: Append only with certain code snippets incorrectly adds imports. - Added official support for Python 3.10 #### Potentially breaking changes: - Fixed #1785: `_ast` module incorrectly excluded from stdlib definition. ### 5.9.3 July 28 2021 - Improved text of skipped file message to mention gitignore feature. - Made all exceptions pickleable. - Fixed #1779: Pylama integration ignores pylama specific isort config overrides. - Fixed #1781: `--from-first` CLI flag shouldn't take any arguments. - Fixed #1792: Sorting literals sometimes ignored when placed on first few lines of file. - Fixed #1777: extend_skip is not honored wit a git submodule when skip_gitignore=true. ### 5.9.2 July 8th 2021 - Improved behavior of `isort --check --atomic` against Cython files. - Fixed #1769: Future imports added below assignments when no other imports present. - Fixed #1772: skip-gitignore will check files not in the git repository. - Fixed #1762: in some cases when skip-gitignore is set, isort fails to skip any files. - Fixed #1767: Encoding issues surfacing when invalid characters set in `__init__.py` files during placement. - Fixed #1771: Improved handling of skips against named streamed in content. ### 5.9.1 June 21st 2021 [hotfix] - Fixed #1758: projects with many files and skip_ignore set can lead to a command-line overload. ### 5.9.0 June 21st 2021 - Improved CLI startup time. - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. - Implemented #1737: Support for using action comments to avoid adding imports to individual files. - Implemented #1750: Ability to customize output format lines. - Implemented #1732: Support for custom sort functions. - Implemented #1722: Improved behavior for running isort in atomic mode over Cython source files. - Fixed (https://github.com/PyCQA/isort/pull/1695): added imports being added to doc string in some cases. - Fixed (https://github.com/PyCQA/isort/pull/1714): in rare cases line continuation combined with tabs can output invalid code. - Fixed (https://github.com/PyCQA/isort/pull/1726): isort ignores reverse_sort when force_sort_within_sections is true. - Fixed #1741: comments in hanging indent modes can lead to invalid code. - Fixed #1744: repeat noqa comments dropped when * import and non * imports exist from the same package. - Fixed #1721: repeat noqa comments on separate from lines with force-single-line set, sometimes get dropped. #### Goal Zero (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): - Implemented #1394: 100% branch coverage (in addition to line coverage) enforced. - Implemented #1751: Strict typing enforcement (turned on mypy strict mode). ### 5.8.0 March 20th 2021 - Fixed #1631: as import comments can in some cases be duplicated. - Fixed #1667: extra newline added with float-to-top, after skip, in some cases. - Fixed #1594: incorrect placement of noqa comments with multiple from imports. - Fixed #1566: in some cases different length limits for dos based line endings. - Implemented #1648: Export MyPY type hints. - Implemented #1641: Identified import statements now return runnable code. - Implemented #1661: Added "wemake" profile. - Implemented #1669: Parallel (`-j`) now defaults to number of CPU cores if no value is provided. - Implemented #1668: Added a safeguard against accidental usage against /. - Implemented #1638 / #1644: Provide a flag `--overwrite-in-place` to ensure same file handle is used after sorting. - Implemented #1684: Added support for extending skips with `--extend-skip` and `--extend-skip-glob`. - Implemented #1688: Auto identification and skipping of some invalid import statements. - Implemented #1645: Ability to reverse the import sorting order. - Implemented #1504: Added ability to push star imports to the top to avoid overriding explicitly defined imports. - Documented #1685: Skip doesn't support plain directory names, but skip_glob does. ### 5.7.0 December 30th 2020 - Fixed #1612: In rare circumstances an extra comma is added after import and before comment. - Fixed #1593: isort encounters bug in Python 3.6.0. - Implemented #1596: Provide ways for extension formatting and file paths to be specified when using streaming input from CLI. - Implemented #1583: Ability to output and diff within a single API call to `isort.file`. - Implemented #1562, #1592 & #1593: Better more useful fatal error messages. - Implemented #1575: Support for automatically fixing mixed indentation of import sections. - Implemented #1582: Added a CLI option for skipping symlinks. - Implemented #1603: Support for disabling float_to_top from the command line. - Implemented #1604: Allow toggling section comments on and off for indented import sections. ### 5.6.4 October 12, 2020 - Fixed #1556: Empty line added between imports that should be skipped. ### 5.6.3 October 11, 2020 - Improved packaging of test files alongside source distribution (see: https://github.com/PyCQA/isort/pull/1555). ### 5.6.2 October 10, 2020 - Fixed #1548: On rare occasions an unecessary empty line can be added when an import is marked as skipped. - Fixed #1542: Bug in VERTICAL_PREFIX_FROM_MODULE_IMPORT wrap mode. - Fixed #1552: Pylama test dependent on source layout. #### Goal Zero: (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): - Zope added to integration test suite - Additional testing of CLI (simulate unseekable streams) ### 5.6.1 [Hotfix] October 8, 2020 - Fixed #1546: Unstable (non-idempotent) behavior with certain src trees. ### 5.6.0 October 7, 2020 - Implemented #1433: Provide helpful feedback in case a custom config file is specified without a configuration. - Implemented #1494: Default to sorting imports within `.pxd` files. - Implemented #1502: Improved float-to-top behavior when there is an existing import section present at top-of-file. - Implemented #1511: Support for easily seeing all files isort will be ran against using `isort . --show-files`. - Implemented #1487: Improved handling of encoding errors. - Improved handling of unsupported configuration option errors (see #1475). - Fixed #1463: Better interactive documentation for future option. - Fixed #1461: Quiet config option not respected by file API in some circumstances. - Fixed #1482: pylama integration is not working correctly out-of-the-box. - Fixed #1492: --check does not work with stdin source. - Fixed #1499: isort gets confused by single line, multi-line style comments when using float-to-top. - Fixed #1525: Some warnings can't be disabled with --quiet. - Fixed #1523: in rare cases isort can ignore direct from import if as import is also on same line. #### Potentially breaking changes: - Implemented #1540: Officially support Python 3.9 stdlib imports by default. - Fixed #1443: Incorrect third vs first party categorization - namespace packages. - Fixed #1486: "Google" profile is not quite Google style. - Fixed "PyCharm" profile to always add 2 lines to be consistent with what PyCharm "Optimize Imports" does. #### Goal Zero: (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): - Implemented #1472: Full testing of stdin CLI Options - Added additional branch coverage. - More projects added to integration test suite. ### 5.5.5 [Hotfix] October 7, 2020 - Fixed #1539: in extremely rare cases isort 5.5.4 introduces syntax error by removing closing paren. ### 5.5.4 [Hotfix] September 29, 2020 - Fixed #1507: in rare cases isort changes the content of multiline strings after a yield statement. - Fixed #1505: Support case where known_SECTION points to a section not listed in sections. ### 5.5.3 [Hotfix] September 20, 2020 - Fixed #1488: in rare cases isort can mangle `yield from` or `raise from` statements. ### 5.5.2 [Hotfix] September 9, 2020 - Fixed #1469: --diff option is ignored when input is from stdin. ### 5.5.1 September 4, 2020 - Fixed #1454: Ensure indented import sections with import heading and a preceding comment don't cause import sorting loops. - Fixed #1453: isort error when float to top on almost empty file. - Fixed #1456 and #1415: noqa comment moved to where flake8 cant see it. - Fixed #1460: .svn missing from default ignore list. ### 5.5.0 September 3, 2020 - Fixed #1398: isort: off comment doesn't work, if it's the top comment in the file. - Fixed #1395: reverse_relative setting doesn't have any effect when combined with force_sort_within_sections. - Fixed #1399: --skip can error in the case of projects that contain recursive symlinks. - Fixed #1389: ensure_newline_before_comments doesn't work if comment is at top of section and sections don't have lines between them. - Fixed #1396: comments in imports with ";" can keep isort from recognizing import line. - Fixed #1380: As imports removed when `combine_star` is set. - Fixed #1382: --float-to-top has no effect if no import is already at the top. - Fixed #1420: isort never settles on module docstring + add import. - Fixed #1421: Error raised when repo contains circular symlinks. - Fixed #1427: noqa comment is moved from star import to constant import. - Fixed #1444 & 1445: Incorrect placement of import additions. - Fixed #1447: isort5 throws error when stdin used on Windows with deprecated args. - Implemented #1397: Added support for specifying config file when using git hook (thanks @diseraluca!). - Implemented #1405: Added support for coloring diff output. - Implemented #1434: New multi-line grid mode without parentheses. #### Goal Zero (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): - Implemented #1392: Extensive profile testing. - Implemented #1393: Proprety based testing applied to code snippets. - Implemented #1391: Create automated integration test that includes full code base of largest OpenSource isort users. #### Potentially breaking changes: - Fixed #1429: --check doesn't print to stderr as the documentation says. This means if you were looking for `ERROR:` messages for files that contain incorrect imports within stdout you will now need to look in stderr. ### 5.4.2 Aug 14, 2020 - Fixed #1383: Known other does not work anymore with .editorconfig. - Fixed: Regression in first known party path expansion. ### 5.4.1 [Hotfix] Aug 13, 2020 - Fixed #1381: --combine-as loses # noqa in different circumstances. ### 5.4.0 Aug 12, 2020 - Implemented #1373: support for length sort only of direct (AKA straight) imports. - Fixed #1321: --combine-as loses # noqa. - Fixed #1375: --dont-order-by-type CLI broken. ### 5.3.2 [Hotfix] Aug 7, 2020 - Fixed incorrect warning code (W503->W0503). ### 5.3.1 Aug 7, 2020 - Improve upgrade warnings to be less noisy and point to error codes for easy interoperability with Visual Studio Code (see: #1363). ### 5.3.0 Aug 4, 2020 - Implemented ability to treat all or select comments as code (issue #1357) - Implemented ability to use different configs for different file extensions (issue #1162) - Implemented ability to specify the types of imports (issue #1181) - Implemented ability to dedup import headings (issue #953) - Added experimental support for sorting literals (issue #1358) - Added experimental support for sorting and deduping groupings of assignments. - Improved handling of deprecated single line variables for usage with Visual Studio Code (issue #1363) - Improved handling of mixed newline forms within same source file. - Improved error handling for known sections. - Improved API consistency, returning a boolean value for all modification API calls to indicate if changes were made. - Fixed #1366: spurious errors when combining skip with --gitignore. - Fixed #1359: --skip-gitignore does not honor ignored symlink #### Internal Development: - Initial hypothesmith powered test to help catch unexpected syntax parsing and output errors (thanks @Zac-HD!) ### 5.2.2 July 30, 2020 - Fixed #1356: return status when arguments are passed in without files or a content stream. ### 5.2.1 July 28, 2020 - Update precommit to default to filtering files that are defined in skip. - Improved relative path detection for `skip` config usage. - Added recursive symbolic link protection. - Implemented #1177: Support for color output using `--color`. - Implemented recursive symlink detection support. ### 5.2.0 July 27, 2020 - Implemented #1335: Official API for diff capturing. - Implemented #1331: Warn when sections don't match up. - Implemented #1261: By popular demand, `filter_files` can now be set in the config option. - Implemented #960: Support for respecting git ignore via "--gitignore" or "skip_gitignore=True". - Implemented #727: Ability to only add imports if existing imports exist. - Implemented #970: Support for custom sharable isort profiles. - Implemented #1214: Added support for git_hook lazy option (Thanks @sztamas!) - Implemented #941: Added an additional `multi_line_output` mode for more compact formatting (Thanks @sztamas!) - Implemented #1020: Option for LOCALFOLDER. - Implemented #1353: Added support for output formatting plugins. - `# isort: split` can now be used at the end of an import line. - Fixed #1339: Extra indent is not preserved when isort:skip is used in nested imports. - Fixed #1348: `--diff` works incorrectly with files that have CRLF line endings. - Improved code repositories usage of pylint tags (#1350). ### 5.1.4 July 19, 2020 - Fixed issue #1333: Use of wrap_length raises an exception about it not being lower or equal to line_length. - Fixed issue #1330: Ensure stdout can be stubbed dynamically for `show_unified_diff` function. ### 5.1.3 July 18, 2020 - Fixed issue #1329: Fix comments duplicated when --fass option is set. ### 5.1.2 July 17, 2020 - Fixed issue #1219 / #1326: Comments not wrapped for long lines - Fixed issue #1156: Bug related to isort:skip usage followed by a multiline comment block ### 5.1.1 July 15, 2020 - Fixed issue #1322: Occasionally two extra newlines before comment with `-n` & `--fss`. - Fixed issue #1189: `--diff` broken when reading from standard input. ### 5.1.0 July 14, 2020 - isort now throws an exception if an invalid settings path is given (issue #1174). - Implemented support for automatic redundant alias removal (issue #1281). - Implemented experimental support for floating all imports to the top of a file (issue #1228) - Fixed #1178: support for semicolons in decorators. - Fixed #1315: Extra newline before comment with -n + --fss. - Fixed #1192: `-k` or `--keep-direct-and-as-imports` option has been deprecated as it is now always on. #### Formatting changes implied: - Fixed #1280: rewrite of as imports changes the behavior of the imports. ### 5.0.9 July 11, 2020 - Fixed #1301: Import headings in nested sections leads to check errors ### 5.0.8 July 11, 2020 - Fixed #1277 & #1278: New line detection issues on Windows. - Fixed #1294: Fix bundled git hook. ### 5.0.7 July 9, 2020 - Fixed #1306: unexpected --diff behavior. - Fixed #1279: Fixed NOQA comment regression. ### 5.0.6 July 8, 2020 - Fixed #1302: comments and --trailing-comma can generate invalid code. - Fixed #1293: extra new line in indented imports, when immediately followed by a comment. - Fixed #1304: isort 5 no longer recognises `sre_parse` as a stdlib module. - Fixed #1300: add_imports moves comments following import section. - Fixed #1276: Fix a bug that creates only one line after triple quotes. ### 5.0.5 July 7, 2020 - Fixed #1285: packaging issue with bundling tests via poetry. - Fixed #1284: Regression when sorting `.pyi` files from CLI using black profile. - Fixed #1275 & #1283: Blank line after docstring removed. - Fixed #1298: CLI Help out of date with isort 5. - Fixed #1290: Unecessary blank lines above nested imports when import comments turned on. - Fixed #1297: Usage of `--add-imports` alongside `--check` is broken. - Fixed #1289: Stream usage no longer auto picking up config file from current working directory. - Fixed #1296: Force_single_line setting removes immediately following comment line. - Fixed #1295: `ensure_newline_before_comments` doesnt work with `force_sort_within_sections`. - Setting not_skip will no longer immediately fail but instead give user a warning and direct to upgrade docs. ### 5.0.4 July 6, 2020 - Fixed #1264: a regression with comment handling and `force_sort_within_sections` config option - Added warning for deprecated CLI flags and linked to upgrade guide. ### 5.0.3 - July 4, 2020 - Fixed setup.py command incorrectly passing check=True as a configuration parameter (see: https://github.com/pycqa/isort/issues/1258) - Fixed missing patch version - Fixed issue #1253: Atomic fails when passed in not readable output stream ### 5.0.2 - July 4, 2020 - Ensured black profile was complete, adding missing line_length definition. ### 5.0.1 - July 4, 2020 - Fixed a runtime error in a vendored dependency (toml). ### 5.0.0 Penny - July 4, 2020 **Breaking changes:** - isort now requires Python 3.6+ to run but continues to support formatting on ALL versions of python including Python 2 code. - isort deprecates official support for Python 3.4, removing modules only in this release from known_standard_library: - user - Config files are no longer composed on-top of each-other. Instead the first config file found is used. - Since there is no longer composition negative form settings (such as --dont-skip or it's config file variant `not_skip`) are no longer required and have been removed. - Two-letter shortened setting names (like `ac` for `atomic`) now require two dashes to avoid ambiguity: `--ac`. - For consistency with other tools `-v` now is shorthand for verbose and `-V` is shorthand for version. See Issue: #1067. - `length_sort_{section_name}` config usage has been deprecated. Instead `length_sort_sections` list can be used to specify a list of sections that need to be length sorted. - `safety_excludes` and `unsafe` have been deprecated - Config now includes as default full set of safety directories defined by safety excludes. - `--recursive` option has been removed. Directories passed in are now automatically sorted recursive. - `--apply` option has been removed as it is the default behaviour. - isort now does nothing, beyond giving instructions and exiting status code 0, when ran with no arguments. - a new `--interactive` flag has been added to enable the old style behaviour. - isort now works on contiguous sections of imports, instead of one whole file at a time. - ~~isort now formats all nested "as" imports in the "from" form. `import x.y as a` becomes `from x import y as a`.~~ NOTE: This was undone in version 5.1.0 due to feedback it caused issues with some project conventions. - `keep_direct_and_as_imports` option now defaults to `True`. - `appdirs` is no longer supported. Unless manually specified, config should be project config only. - `toml` is now installed as a vendorized module, meaning pyproject.toml based config is always supported. - Completely new Python API, old version is removed and no longer accessible. - New module placement logic and module fully replaces old finders. Old approach is still available via `--old-finders`. Internal: - isort now utilizes mypy and typing to filter out typing related issues before deployment. - isort now utilizes black internally to ensure more consistent formatting. - profile support for common project types (black, django, google, etc) - Much much more. There is some difficulty in fully capturing the extent of changes in this release - just because of how all encompassing the release is. See: [Github Issues](https://github.com/pycqa/isort/issues?q=is%3Aissue+is%3Aclosed) for more. ### 4.3.21 - June 25, 2019 - hot fix release - Fixed issue #957 - Long aliases and use_parentheses generates invalid syntax ### 4.3.20 - May 14, 2019 - hot fix release - Fixed issue #948 - Pipe redirection broken on Python2.7 ### 4.3.19 - May 12, 2019 - hot fix release - Fixed issue #942 - correctly handle pyi (Python Template Files) to match `black` output ### 4.3.18 - May 1, 2019 - hot fix release - Fixed an issue with parsing files that contain unicode characters in Python 2 - Fixed issue #924 - Pulling in pip internals causes depreciation warning - Fixed issue #938 - Providing a way to filter explicitly passed in files via configuration settings (`--filter-files`) - Improved interoperability with toml configuration files ### 4.3.17 - April 7, 2019 - hot fix release - Fixed issue #905 & #919: Import section headers behaving strangely ### 4.3.16 - March 23, 2019 - hot fix release - Fixed issue #909 - skip and skip-glob are not enforced when using settings-path. - Fixed issue #907 - appdirs optional requirement does not correctly specify version - Fixed issue #902 - Too broad warning about missing toml package - Fixed issue #778 - remove `user` from known standard library as it's no longer in any supported Python version. ### 4.3.15 - March 10, 2019 - hot fix release - Fixed a regression with handling streaming input from pipes (Issue #895) - Fixed handling of \x0c whitespace character (Issue #811) - Improved CLI documentation ### 4.3.14 - March 9, 2019 - hot fix release - Fixed a regression with */directory/*.py style patterns ### 4.3.13 - March 8, 2019 - hot fix release - Fixed the inability to accurately determine import section when a mix of conda and virtual environments are used. - Fixed some output being printed even when --quiet mode is enabled. - Fixed issue #890 interoperability with PyCharm by allowing case sensitive non type grouped sorting. - Fixed issue #889 under some circumstances isort will incorrectly add a new line at the beginning of a file. - Fixed issue #885 many files not being skipped according to set skip settings. - Fixed issue #842 streaming encoding improvements. ### 4.3.12 - March 6, 2019 - hot fix release - Fix error caused when virtual environment not detected ### 4.3.11 - March 6, 2019 - hot fix release - Fixed issue #876: confused by symlinks pointing to virtualenv gives FIRSTPARTY not THIRDPARTY - Fixed issue #873: current version skips every file on travis - Additional caching to reduce performance regression introduced in 4.3.5 ### 4.3.10 - March 2, 2019 - hot fix release - Fixed Windows incompatibilities (Issue #835) - Fixed relative import sorting bug (Issue #417) - Fixed "no_lines_before" to also be respected from previous empty sections. - Fixed slow-down introduced by finders mechanism by adding a LRU cache (issue #848) - Fixed issue #842 default encoding not-set in Python2 - Restored Windows automated testing - Added Mac automated testing ### 4.3.9 - February 25, 2019 - hot fix release - Fixed a bug that led to an incompatibility with black: #831 ### 4.3.8 - February 25, 2019 - hot fix release - Fixed a bug that led to the recursive option not always been available from the command line. ### 4.3.7 - February 25, 2019 - hot fix release - Expands the finder failsafe to occur on the creation of the finder objects. ### 4.3.6 - February 24, 2019 - hot fix release - Fixes a fatal error that occurs if a single finder throws an exception. Important as we add more finders that utilize third party libraries. ### 4.3.5 - February 24, 2019 - last Python 2.7 Maintenance Release This is the final Python 2.x release of isort, and includes the following major changes: Potentially Interface Breaking: - The `-r` option for removing imports has been renamed `-rm` to avoid accidental deletions and confusion with the `-rc` recursive option. - `__init__.py` has been removed from the default ignore list. The default ignore list is now empty - with all items needing to be explicitly ignored. - Isort will now by default ignore .tox / venv folders in an effort to be "safe". You can disable this behaviour by setting the "--unsafe" flag, this is separate from any skip or not skip rules you may have in place. - Isort now allows for files missing closing newlines in whitespace check - `distutils` support has been removed to simplify setup.py New: - Official Python 3.7 Compatibility. - Support for using requirements files to auto determine third-paty section if pipreqs & requirementslib are installed. - Added support for using pyproject.toml if toml is installed. - Added support for XDG_HOME if appdirs is installed. - An option has been added to enable ignoring trailing comments ('ignore_comments') defaulting to False. - Added support to enable line length sorting for only specific sections - Added a `correctly_sorted` property on the SortsImport to enable more intuitive programmatic checking. Fixes: - Improved black compatibility. - Isort will now detect files in the CWD as first-party. - Fixed several cases where '-ns' or 'not_skip' was being incorrectly ignored. - Fixed sorting of relative path imports ('.', '..', '...', etc). - Fixed bugs caused by a failure to maintain order when loading iterables from config files. - Correctly handle CPython compiled imports and others that need EXT_SUFFIX to correctly identify. - Fixed handling of Symbolic Links to follow them when walking the path. - Fixed handling of relative known_paths. - Fixed lack of access to all wrap modes from the CLI. - Fixed handling of FIFO files. - Fixed a bug that could result in multiple imports being inserted on the same line. ### 4.3.4 - February 12, 2018 - hotfix release - Fixed issue #671: isort is corrupting CRLF files ### 4.3.3 - Feburary 5, 2018 - hotfix release - Fixed issue #665: Tabs turned into single spaces ### 4.3.2 - Feburary 4, 2018 - hotfix release - Fixed issue #651: Add imports option is broken - Fixed issue #662: An error generated by rewriting `.imports` to `. imoprts` ### 4.3.1 - Feburary 2, 2018 - hotfix release - Fixed setup.py errors - Fixed issue #654: Trailing comma count error - Fixed issue #650: Wrong error message displayed ### 4.3.0 - January 31, 2018 - Fixed #557: `force_alphabetical_sort` and `force_sort_within_sections` can now be utilized together without extra new lines - Fix case-sensitive path existence check in Mac OS X - Added `--no-lines-before` for more granular control over section output - Fixed #493: Unwanted conversion to Windows line endings - Fixed #590: Import `as` mucks with alphabetical sorting - Implemented `--version-number` to retrieve just the version number without the isort logo - Breaking changes - Python 2.7+ only (dropped 2.6) allowing various code simplifications and improvements. ### 4.2.15 - June 6, 2017 - hotfix release IMPORTANT NOTE: This will be the last release with Python 2.6 support, subsequent releases will be 2.7+ only - Fixed certain one line imports not being successfully wrapped ### 4.2.14 - June 5, 2017 - hotfix release - Fixed #559 & #565: Added missing standard library imports ### 4.2.13 - June 2, 2017 - hotfix release - Fixed #553: Check only and --diff now work together again ### 4.2.12 - June 1, 2017 - hotfix release - Fixed wheel distribution bug ### 4.2.11 - June 1, 2017 - hotfix release - Fixed #546: Can't select y/n/c after latest update - Fixed #545: Incorrectly moves __future__ imports above encoding comments ### 4.2.9 - June 1, 2017 - hotfix release - Fixed #428: Check only modifies sorting - Fixed #540: Not correctly identifying stdlib modules ### 4.2.8 - May 31, 2017 - Added `--virtual-env` switch command line option - Added --enforce-whitespace option to go along with --check-only for more exact checks (issue #423) - Fixed imports with a tailing '\' and no space in-between getting removed (issue #425) - Fixed issue #299: long lines occasionally not wrapped - Fixed issue #432: No longer add import inside class when class starts at top of file after encoding comment - Fixed issue #440: Added missing `--use-parentheses` option to command line tool and documentation - Fixed issue #496: import* imports now get successfully identified and reformatted instead of deleted - Fixed issue #491: Non ending parentheses withing single line comments no longer cause formatting issues - Fixed issue #471: Imports that wrap the maximum line length and contain comments on the last line are no longer rendered incorrectly - Fixed issue #436: Force sort within section no longer rearranges comments - Fixed issue #473: Force_to_top and force_sort_within_sections now work together - Fixed issue #484 & #472: Consistent output with imports of same spelling but different case - Fixed issue #433: No longer incorrectly add an extra new-line when comment between imports and function definition - Fixed issue #419: Path specification for skipped paths is not Unix/Windows inter-operable. Breaking Changes: - Fixed issue #511: All command line options with an underscore, have had the underscore replaced with a dash for consistency. This effects: multi-line, add-import, remove-import, force-adds, --force-single-line-imports, and length-sort. - Replaced the `--enforce-whitespace` option with `--ignore-whitespace` to restore original behavior of strict whitespace by default ### 4.2.5 - Fixed an issue that caused modules to inccorectly be matched as thirdparty when they simply had `src` in the leading path, even if they weren't withing $VIRTUALENV/src #414 ### 4.2.4 - Fixed an issue that caused module that contained functions before doc strings, to incorrectly place imports - Fixed regression in how `force_alphabetical_sort` was being interpretted (issue #409) - Fixed stray print statement printing skipped files (issue #411) - Added option for forcing imports into a single bucket: `no_sections` - Added option for new lines between import types (from, straight): `lines_between_sections` ### 4.2.3 - Fixed a large number of priority bugs - bug fix only release ### 4.2.2 - Give an error message when isort is unable to determine where to place a module - Allow imports to be sorted by module, independent of import_type, when `force_sort_within_sections` option is set - Fixed an issue that caused Python files with 2 top comments not to be sorted ### 4.2.1 - Hot fix release to fix code error when skipping globs ### 4.2.0 - Added option "NOQA" Do not wrap lines, but add a noqa statement at the end - Added support for running isort recursively, simply with a standalone `isort` command - Added support to run isort library as a module - Added compatibility for Python 3.5 - Fixed performance issue (#338) when running on project with lots of skipped directories - Fixed issue #328: extra new can occasionally occur when using alphabetical-only sort - Fixed custom sections parsing from config file (unicode string -> list) - Updated pylama extension to the correct entry point - Skip files even when file_contents is provided if they are explicitly in skip list - Removed always showing isort banner, keeping it for when the version is requested, verbose is used, or show_logo setting is set. ### 4.1.2 - Fixed issue #323: Accidental default configuration change introduced ### 4.1.1 - Added support for partial file match skips (thanks to @Amwam) - Added support for --quiet option to only show errors when running isort - Fixed issue #316: isort added new lines incorrectly when a top-of line comment is present ### 4.1.0 - Started keeping a log of all changes between releases - Added the isort logo to the command line interface - Added example usage gif to README - Implemented issue #292: skip setting now supports glob patterns - Implemented issue #271: Add option to sort imports purely alphabetically - Implemented issue #301: Readme is now natively in RST format, making it easier for Python tooling to pick up - Implemented pylama isort extension - Fixed issue #260: # encoding lines at the top of the file are now correctly supported - Fixed issue #284: Sticky comments above first import are now supported - Fixed issue #310: Ensure comments don't get duplicated when reformatting imports - Fixed issue #289: Sections order not being respected - Fixed issue #296: Made it more clear how to set arguments more then once ### 4.0.0 - Removed all external dependencies isort-9.0.1/Dockerfile0000644000000000000000000000123013615410400011567 0ustar00FROM python:3.13 WORKDIR /isort COPY pyproject.toml uv.lock /isort/ # Install uv COPY --from=ghcr.io/astral-sh/uv:0.6.0 /uv /uvx /bin/ # Setup as minimal a stub project as possible, simply to allow caching base dependencies # between builds. # # If error is encountered in these steps, can safely be removed locally. RUN mkdir -p /isort/isort RUN mkdir -p /isort/tests RUN touch /isort/isort/__init__.py RUN touch /isort/tests/__init__.py RUN touch /isort/README.md COPY . /isort RUN SETUPTOOLS_SCM_PRETEND_VERSION=0.0.0 uv sync --all-extras --frozen # Install latest code for actual project RUN rm -rf /isort # Run full test suite CMD /isort/scripts/test.sh isort-9.0.1/example.gif0000644000000000000000000023762013615410400011735 0ustar00GIF89a         # !$ (8 !!%$%!!"%%&!($*&,"(%(',(.(*)-,. (()0,2*1-1-5021546283<595<9=$'00"//# )((1.-20/988N:@=A>D8?A""h*G)AE8@B"hhB<;XXDC6FFFGORGPSPNLSPOXXX[eg`]\c`_nmm--88;;UUUUUUCCKKOOPPRRSSTTUU**9˨VVEֱNľ½9! NETSCAPE2.0!3, H*\Ȱႇ#J3j8Ǐ )\Id R\r bʔY-rl狟@JѣHX#?J Bj!Xf•`"Kٳg]˶*jTBݻx˷,r LanNY̸Ǎњ=B˘ KϠC{֊ӨS>-UװcAms߾cN8ȓ+_|sУKNuسkߎ7O3\/ )ʗ%_ ,L3XN;ԓOA5HPI%TSOUTUWCl(Yh"\,x؋(c[؈!fcf棏 ڐCZlHfےE9/]GB4(3ʄ((NVVb/0-"x*SRuFрxqUw =)ÚctGqeтu=DЌ&GMj|KI 4\t-tk&9ON*T+H0WLET`90ꊲLkY[c#/yNpߊycBZ{x'͎$ iH 9+\v&ȧ Mf:@3C(鉣)$HMRYAɲ--IJXjV}U 7 GeYϢaFzT)\!<|H"SM/5lB-Ȓ D]SjF*uw6b* ЧNCQ%Tqy1c]W D+21iq\wJ!+^:9Ok+@hdbљSmLc6[ xKj%iL^]g KF´#l)VY 0s% {;̐`=Cm^}KrNz2g"d&ߝbuͱ@V^LM~XMr&ozrLYyI$ aL2h>36/M2,\(`^ bԯ%匮9]hӍt'MJ[ҘδcܠF{NV|)RWj?j[g-H _Ђw^@bVB {cЎMj[ζn{ǽmzNQv𶇼Mz=~xAO ;\A[ϸ7{ GNK\wW =M8 $E.NHOҗ;PԧNXϺ{{^F0AvbhO1qcp;ܛAf8΀ޡhHҠ5Aj\6xm8ވ7Aʇ86qx<9F?r7WotOgugwώv;A/7~o(Mӧ=m7G><_|=qO_=_ҏ>8yyWy x xPx x' }w Al "8$X#x p &,؂.0284 :<8 @@xtZu \v]'vbWvdvkvo ugwxw}w(Ѐ wyy8Wz{{{~w|gmn4W}p}ط}Hq*r߷o4nWm~~wu8׆k(i(g ؀`XxY WwTvhGv% "X ʸX ,( ` 0 ` 5ڸ؍7ȃ⨃BxDXXwJL N8ve'SvqWww[؅XbxdXfX(zHu{(|}8Vvrp׈ }6Vv藉}ȉ8sh iY 9W^X[ uwnv0p @ p 0 p + p q ؖnpi88X爎騎JPvlvsx} I;Ix iwzȉ|vnҧ`} Iq"9އg'inצ-~/1x k=ɀ([Xw׋Kɔ a!QSk_QQNQ& ~8@ YٍHuyxiuzHؗ~yvJI9XxyIɇ~|hnop)'+)jp(0oG|)|{4iIט@ٜ7tmLٔèRI _@)QI0 0 pr:tjhHڎ HT zw  zsؐ{GgInHsp(ppWv :jm|lj93Yoxɩ𤅇ϹIiWvuE\ʝ^J Y $X p@ Xyxv|jt~ʗIjiVhʠ Jx꘏mXz9{ Ymv @}@ @p@poWZ(i<,.:2977YK*M Qx7IYgך ؝P_eJ r;(xZz yLHz;x_ȨװWjz;Wˡ !*& m!+P6op/7p .@:gFz;z~=~}XZLk˪ A霺hUW Y[vغ]c g; ˾оP ՐlI{r9 x}}Hwh ;: Jy|( 0@`ƒP`0o2L@ @ &2go.K 6pÂ` o+-Q0 mp0':D F{FHᠬLJD);ًP(v˵НYK"x ` 0ɓ@ɘ p ɞl˟(\ [xɸʱg`A<@F PK|`@ ð A<}M 0o Ë@<| p Ņ0 1˻>? BƷWH8ٴy,R[|̽\WP Ҫ0^kIf0 Ӑ =>m { 9]F}HJLNPI PV}V ^]] Pfmֺֺun p L( 0 vvŀ ɰr7 0؂W؅g ֠xט -ٚ' rXx{*̹ |:p:ZKynP@p 6 n`n v @ 젣m`*0м~ |ٶG׆場(: ې؅W؂7p w{ Ȁv= ẗ́p]Ȳp ~ 26}~Ζ"≐ *,.02>4^. 7 ܟDgGط~Y8\(ʘyHߓkޝ |K|Gڀ v M VP P}@8|nPpn|6m m m.|.K 7k,o +ǡGv|NDŽC̀ǠKvbu EN>Qe[.̮%`>^~/: <@AFXuHnvow劻TMٕ]ka.}p|̗e\);*Ҧsp=֝`| ̓@ `qiK zH҈7^ԩ ގE ~S9ӭXǮ⥛ b?PP ؞jl;C(Hބ^KNw< WykٽS t7א 0 p  |ڌ~   -, 'Ϝ ӭ|`P @ `M{]??A/{H?[H붎PuE nL ah` OxZURF5liZHݻvu hw?w&1Y@s;l0k/‹r+:, ,ڪe:*c**bZ * GdMFa&Q~H0ʤAHHcF2J,rK.A"\%|&nI'JŦX E*Dg>Ci6D+ ኋB /c0 CP pӌ~qG}@ǝ|@wUVXu'GxO~t\5yt4YWU{hTYOU|qWP4,K#I԰GkTEI. 1 @sB1rq{S(#֑ǁ!b386d&cdJ6dSVySH Z2L4\36{z΢eΪS+>4 e-Cz7wkלuKWLͽN ?ӎumvmN{ޏľ ݯnlzymiA8DMPA..ɺk-{˶|@uWꧏEOB0`QNa 抢]lF@F0$sJX;ٝv7c 5wB{dq%DJC^C!j6k >Og#}K8 ~5aVQ0POR|9B7F8BKD 28 r'C8'ىhޞVBKynZAopӣ^&2SؐIP3<߰wI/Wb/-.Z$EDbL30=YR$Sdf3LhFSӤf5yMlfSL@TMp '+\qNtxELhNx³NpqO|B} EJ0 "%CbC BC 4,J R_(6.t!ny =JV1,P&DJ.K Fp+^𕯐rn(6QjXtaD! 9t MBbac/|@~"FdrSkek[Vbӛ⤫*iNu敝g<9Oa|πTUBU>4MXTnjP6B6dJdӮԥ2Քj3u4:mh}8uج0TPOCbtauGXB'P]Unwֹ5wͫ^ ,aխ2TTVhQwّv/)RҕejI)OmStmF\7u,V܀~atzXnm|cs7&9Was|M/-ޱWwh|:m~vt#I*pi|f4R")\[0 ꆋzT~8TIlUo`e`wWxXl(hHGZғt-}iLgZӛt=iP[z*LMG 3]>gDA ~WAK"˒E2^~d$O:6zM%0hG~PW,![ϋ<z"a JsjZ2QP[wo~#FIxVIPz[(*4VFCCӞ&l}2m`i+g9SWTgKYz۵|@HV7n=3wK4ԭ>K,ߍ3-RcY&JXv~Sj=BAOx4d5H`6ȿ<`k%zy 7(׆1lšz_Pqˊ[n0<^~]FD/xщ((c_@C. =u { ~UI_H?g&~B]xƻ;nϋ^pDs}ۑ8s̛1B< =Y;jӻի42X \H7'%٫LL'FpJxHhJ(p'4M>#>~BtBG+ Q;05Tp>V23YڱY5;5^5_cC? 9c?s?'  >~>~KAO #EG#UQc%lB'BU™„b.0,"+A\8-;C;Gr,Ǚ0pCmùl?"C 4R{{DHl8C DX|@Qh/p1AMsB/KXJ NC2x/EZ4p.:3p=Q '(NHGC3A'0| Hx*'pKA$KAQESIPԴ4TIH B@F8Wh=@2*2y'% &kªB.c2ڊz¬Ҳ-ԬS;UG;S 9S;{l4 6M*%>#q27@ӭA "'+'` S'13TO2A'NPX(H<4U'@$䤽 -OX]?U݄,/U4]]N'x'h?VjMk/\U`ApNZ}iO4fF(U4am$JTN^TQp1`e-ni"՝T1r2?`'ms/@'v:*Tz - 79/* S)33MS5/6 TxSSS2=ٛYY(A%ԥ2;STTFuTAk. I'KLM=T&PLOZUBhYev'#1[O׶]N>/ s=D(`4('Nz۷F@.suVM<$ONPhr܇MP'`ֻ}O1 5UO\|}]]Lej2XU #IUASEU>VXWh'(52,R|R. Y(-YӅ,jD}٠ٟٞ}__ڡ-TA4BDEeTGިBڪWZtTrZq:l=[Mμ\}^Pe'OrK MOMHXKaka\z\B\5F>]10l\vam5,έ]-|@]MAS(oBUWqUh=b^ڍ]YzECٔUY2_AͲ]䟽F~dMZ-=33Ud&G;] ,Je-`` SH [ԕ[#]#AUaM]2\3 K\˕]ܶmE%=UmvPH.&ӠHLHb[KRP`KmF(Ih3f~fP(f$F}|FlwG(4eo*P@ m2Wv*X5I2.ґS?;b(_AdhP+d,di> -=__dNdd}ZݙUTڬhWUpfeR1Hx'f b{VZ\A8/N]D%ܮt3HMln(h&x2J8lMV!&gj='tQmᇜLh۵FHkPxk'┌FWL8mvl4thd38PKPCxIO1ve9&'=ؑ =c>c?Fn醺)䠞(nYi.o_.dndNjenWUv`j'~<+x@NHp_poppp p p p p pg#F@m _&##;U+;4QcqQqrÊ8?"'>r$"_rBq(qq_޼y{1q/aBL+:os7s8s9s:OR ?SXH_8.qq+L*>)?(r' grK&tJtN)oE_,r-oD.s2' S 44EcEc:u\u]u^sPS35?/8sA'B~l8D#P>*7!OtNvtmvojF7E<Zģ35r/0ȁlT03PKp^wx6u`;Tvb@?vgw\vLt,h˵poxxO*h7`aeMTNwwVwzXwy~_s'xyz7x`?w_̉GQ;hvyO~tcxrݜzG~\yoyxw1wuX{{S{{||/|?|O|_|o||ȏ|&SjC5^ jn~2 ^O eo}_ Տ2"~+RA`TZ 7kzgĿ{S~~~~~~~~/?O_og.,h „ 2l!Ĉ'Rh"ƌ7rQ",Xpݩ*3J%̘2gҬi&Μ:w8Rdɓ)XS"JǠ ' IehS$F+ذbǒ-k,Z?CE9P +#FyZ0 f"$*A'\zM1Ȓ'Sn^A*B<NM/"qزgӮme ލxppȱu:r#g͜)M"ʼn /M"З l<rrËܳA"4WkQfzEW]! NDSmz!!Ɩ[I $x!pٱ A_q#|}D]Dbfxv8IY_7sq_RHsWȁWdВAFkI.$l,DN}'D&r`*E&) }U!"DUc*T@G(!W{NF|ǩ*:\A}@EH&jݥF)(*,"Q0R*]N j@*Ttik~lh]FW (gV[+` I*+lK rpy"!A20K.(uF-ꨶv+Ƿ+™~nIX+2 r K H஻믉P9<1M;tL!Q@@]'2*se#;L@xq4ϋ!^n=P+ii[Hp6(' Йם9A}9kSOm5#@#ȑz;e  &Xy r]|g:;~: VXU M&bA`m9{SOT9%V*>NR8Q<37!H&$:iJ( R6|V rR&"DQ@w%RVdZ.(^"E-Bs] #ӨFʌf(9[|=%vACGC2"1GRz1EKr`Ĥر OT(% .6~.90C2Ab^)a$c-Gy\.$@ZHLfЅTqˈ)qD0iˎ8 kR7yifs@@r@PR<(2a.,|<POkJShΟ&=iE PdrIQ41ե@ryS[&8n ۜgGifLFJԆ VBTD.eD K1hYLcU Hej=CQi"@!P׽D|VWT̩.ϊص U!PfS;JOm!+f t-(3@-fZ9'±؄,\ Xfs{vB +T=mjuԥ !U*\=JMiB 4-vفԷ_ehBJZD@^\˭=dԅ6%g<ـz׫/ ,g* /[ph+L3J| *{S&&g= &%kF!76d9CY%D^̑F@7>2%sct:kjxSΥeC*,v2a| ܜf5le,ö́J%fv{PA3YxC,9vyo\:΄ύ֖sj)U>wӾA-;2bشU]7Qzʽ5xFTAvZ^fbH=Kdl6؂*r"F/Ӻf*6bo@IlUp.8q}zĮ 8=V-syk[6x Top n t/[4mA+ jͣC@<77+'8ͥ.R]@ωgԅg<*a: $.;N~؊/>ҿ_W??{{?o?H_w`  DK D ̟%fE`Ξ vDh " 쁠D I N G8{HAG}Gr,|MG$BIŠE i\P dɖMAI NAFDA\(!r@̬( BD@#u.RDRa ˽`Z@ #"N8 @N،! t !ј"/*ԍMތ́Ԉ񴇊 B!^ ("6Da*a NpN+6} (aJ6J6r`7!C8EHDDEvmddHqGl$I$KJH֣J dK$Dfd=~JҤNKF NN%YdO@N RQ#&RFMUF6"TJVUVU"%PnXDWWf@XZDAUd*eE%]~D[eD>\%_]Ze\e_&Ee d9%a6D&bjb"K:A?d^QaN0S@źhŐefffN`\h(H橔& A$a\(D v)!affbX̦AMxbjtRq6rjf9oGRq!tcxTudǒXw GvLW Ng zy aʂ$BbxXυ rgw]er|~aHI|KHG ēLbV b( @#\㚴,ΜɝɞTphar(x.⾀MMXT~ +ǫK Aa` 4W$MWTU<bҕF$x^pdbP" VʨLD%KR, t N°!A}n(zh9(jL1F뤌"s^jfͼl"i  , *ʪjЀ*0͉ڍ6`4W˰M+t0Rb|c.c!ANu"i@4 UHP@pk]iQ뇊+9240FL Hqi+9xc,U PPfy$R ^l;ECe!0)m[V%ުNrNOR0Y~kq%PDM؎-fVi* mw֭^j-mj8:*-2%߈aSN+ƫ^!h2rIab*i4C;.ƻ{f{_o3{޿nWP||:7'0DDc(C跆&C+7DտFG+Bx0i8΄5svD ?A 0sp=Īr>K~ 'i'@[u!^_ @O/I/K 4>|AQA#d @("B,"A5q E rRȏ4VhHFI<3H 9v:Y3fD'%*T(I#B_%:+D">BGPSjuiӧ $rc53{ j}hS@JEFS aL7u$[z !'P prRc jZR|T]_}tPdkuZw xүrϥI6ܜh/B6|gS- @CA_b.VCn{E(e]F9,ݎVc=VW, f,zڞBI_M<=rpZGv[.ڔVvBl%3W r͕0]CI,`v<)[_b\9@`C gw]dl ďЇ!G%d1# a?? ꁉf#nSm`!  r@@A'5c Q qLdU!`df#[$fhfYb Hs%b{l*Ӛ$7eD.{ j2PV'|$=\'UPSTg˳3 hK(!wAhEʬDU4 9*MQ%10@A A?8%*$ r <#NM?MqJFx!J)B10iS>~D,\@i"&*&K]MX{cV yj:מƮkVje-jv$+`AErE,Y4֭yf'XYh Kv-,X]YF+Al*6I-k#[ۺVGJj ۈHkUZ˸_RRh}ὤx[匎 ;oD;_׾o~֗} A0n] 1RkW%T 08(#.&qaq~7 }5qIVd'?Q^͍S #oRe/a * Vrf7q鼜3Yg?ЁլgAщVh?yv)]iK_Q&3iOԡ@6- CթVYRձi]k?k[vq][VF[׼]mk_>mo&-ns鶳mw[}vȞ6oηv tm?DT)^?3<8w.Z!y1qpѕn srsᴚd5zÓseַ*O"P^(I)|T)KsZWdiѿ8'?bR U$B Pj#xJqPV F08L†LkqEo0rpaP " -M_U.  o`i f/ wl wz0\ K C/ ">(~Bn&!++ Q" q` 3 o"DKNC.* 1BH|"'[ou"hHb>0 op[POV#GgjoejQC"JoEgJVOu/"$koQoƏK/,"+cKQ=o&o00Ы6nO"o%J I[*#?.2r#cA%o$M#SR&k#]gR'gnR#_r'#{%9$)(Y(2)Rr2'*?1(+0+Y),0,1,, r$i@-2 ./}/T-0;%0002/201's,S2)S3o2(s3C:J33S4SSH4#֌P5cS:Z4Iq w l'ʜɂ,4Te3 3/k4&0ɲC3'ɀp5h KCË:s딓\63а34HSs>K ?A,K&C"QDD֓x:PbIJ DL,q#H1Pqi|*O Ɍ`]{PqP7oZTY Rդ>ZU<R1G/Ք1?Ss,V%p5Da6/8ܕ$uV-cus_CW_U`q VYsnÐegzx< qR)ܱHJ䑣fTXqOIq~.0diuVkDea1?g6`Fj*@s6 ,6VU@j$2*!f$rDP@Kppp#JKVO6^+unʗvVfdpu7.Snng$Qvo7h@w_`ne|,E wwyy#v z7x_gxQS{ܸWnW||9_}Է<]}{~w_K|׀g_c 뗀؁-W@y؂8/UJ/؃?-7x;KBx*G؄W8Bx.5*g\`XiX'-/g@MIxz&cX鬇%8lX 6X5]{x4drC+Ȏ#TɬÈsˋ]@UH3H*q9ylR3yݘZо09Yu[90xf,J'$ YyY8&lI2&G'ؒ78&"&6tc(*t@bG)6<4G', FC-|e+r4Al"&/py/l9:!=C,UYyؕ11ίHCKtJ8ExԁFpDG G~Lz"5DoK֙tH|S珜3T](F.jD!EFPhBD9Z8Jfb/3aY UokRiq guhz^'"$IG[mu2"{zYYˏ`_V ,ZPjIbmvGxغܺn!fY+#UPfj56{-ϓoii/;j!tdi$¶!':Qk`_usMKor) jI>ש`Lq [0;%L_αxXAa,{#ɫz[({/ٽ,qū{'׻ήs?"|& jzpsjL5uâ ׃ݣ=Cڮ}hדc_X[A )]DZ }^6᭑;"6R;{h?5}֒KL+"iC;V{Kʶ=j,u4M3]Vogᑻ˹m都ºQ;T'[CO|-{ף>{Abw>M>;iqõLr"(=qIWZ"Ug70?5 7* ue!l?" Yaj0R p:Lg&{M3^J۟߿)O"80… :|1ĉ+Z1ƍ;z2ȑ$K<2JZnS A+kڼ3Ν<{ 4Г._XpҥL:} 5ԩTj ̪\z 6رd)jȚUfҲlۺ} 7ܸgfPKs޽| ]k>8@Eɔ+[͜;{6A>:꒢IU0ٴk>:9x֤Hċ\WHYԫ[.r 1˯Ծ<ۻBۿnmG_`ghGA >a-\IaE@nb"6աb*֔-22HcG/\5c ] cFʈ#vlG> %IGn\Zn9sA'fwfr&͉gM ܩg 9hV(h.d=:i&V鐎"i䐗:jU k]z*ފkP~jkz l$@*8yln,ˎPVܴ~KbhsE7ꮫиfkn뺻&/:/Jp¹ ?|,CL!, 8@A *XÇ2Hq3jtЯ )I Re0;xI͛6O٢O. HҸ)PJիAʵ!`KlG$hӢȶ*pʝ -*x˷o L~+^w0ǐ#K˘3k޼n#CMَDR^ͺj!mȞM۲}ͻ8jNkXμ0N}سν{ ࿃OyҫWox Fbō񃔀J-K5M;PB%QJ- QEEUUWuhUV^8i$։iE׊,2{&48Nxg@\ihE$ڒLvnPF9UieoW%= dih^| '}ty_~ RHS PPTZ8pa!r%V$"d棏5ZkM0W}=ؚ-com;[ sݙD ȷ͚.A%+ |b.i' z;zOԅCjjvZcюG*S<9xcQG9I,E]{0S͸G)r" Ab!-} u&ҕ^I:NB PVY`D)A^*Q[/ep Xi L(XN x3ykk!`zx# d wH>';mc 'Nb$ŤI*b̊R[G$ !WVde*A㠑tCR91}z 0>Sd!CY&Adf OGdnzs1'=pv`fl%;ɯe ɥ$ @G;N2u@φ:si<+SF7юΜS'- R-(-jD3EӚ&8ͩNw}>`?&!PZP>Y JOyRKTfMͣXͪV*ta"-g"lZU Ә_LaMJWJT1R&zj9UϪd'KYam$/;+Qu}hM6%`ֺlgK[ $f>vnB⭢8P:lfgXEw.GlJK6zoU4Mz}^sǝ$!~u'II LX!;]q򹖽.YLaM¡v7<|cmd(Nq@'MgL8αw<-c HN&;Pm]|([Xβ.{B}2hNfSMBL:xγ<*π܎Q |F;hb90%Sf󛉼L{Ӡn70#JT ݏDbltϼiEָ5aAj:ǫ&k]s7af;˼FC/D0JH `!B`j>l:HG#XChH5~Xh(D9QlXbxe8ϸƓ|^#h8jLà ?]P1}Da 9mPȅs\ w?Nf(Mru$O2Ňl{jqL U>r@8ցru#A|[ڜ`wzj/Dcݲ>zG@v##?7eD?10D߽@C< }r@®DG4AD=&/Q<wZϾc?-9%D_#O}?xݑ{!f/cfy/Cӗ7}Q}d66 @y0 ހmp ~@ 0 rvh0~~G7> Cw|v ` (u65vyx}FL;vov|hI}P wyт@|@Fjpjw W gzMDHy8X8u؈ 爒8uxvJȉfԗx]VxDcG8@Њ88#ŸȌ،8Hؘh(XH縎8X'8}vx9HI fȐ9{Yg  e֑)&yXF,ْK;29?& Y E:6ۑ<9D;'1YLٔr-qGJTYQ 8Vٕ^XR_YVc)fE@)X\ɖrhp9xIuq~i2P},.JY٘ 39 PpɘhI9y93/먚0 b8YٛԘٌY)r9؜/yHٝH 94IIY3|؞ yHɝٟFR}9:!ɠ:z :qL v&q#z,i;A٢2j+:8*h59ڣv-6Dh@*EvAJʤRgN*WSvV/`Jg\XffBqezleDmn(p*veo/_z|ey/>kڧdB:[Z=zf{`9H1Z3ʩ/e zVZ6!;WAYfڢʫWQ&ʢ9b':?ڬ*p j!7?`!8zZJ@P蚮IծfJC: 1H{ [*PC ۰+#P/!:[/9/\"?`[*+Ҳ13 {)5<8`?A+DAG9@[Ѵ-1T+V ;Y X`+bMd[ !^jkln]DFt w+Dzuc1 ?;eA+zkZ0D4JpD[1" d!cqH`{[}z˼wDKΫ){|JE4˽m{{rH0˾;&RM{gJH<\|fJ l  ZiAh\,[M!#Fo)L,L/ܢ+\Moq4\'zS<>|2CL, BGN,PS#H|3pa\,^a,&[\':`&Z,n|p#r=@l9DmF} 1(yͯNݔM="Y!e%^) L [MVi_lݕj=oq-s-u}wyN~]nE+gMtȇ4ik3m ٍU0ٔC-mמ}٢=Yڝ&ozVڲ Ʊ}F|G(@ڵǍܞ}MiS0M-89mҭ)}=Yߚqߘ>b> « !nގ#%.'~)>$.(4>69n;=^?AC^xH6_ތ[b>F^i.]^lΌegHGv^x{>x(D+.N踸..>鰨鏞NNꪨ风M뱮qΉhk帞뚸.H>~ʾ(QQ^~خNXKhh7>c+^Lވc+o J+_/ /}/?MX݊)o+$.3τ*D79<&EBD/GJ@LPyM/AUyWo?@Z`oa/^G;7h/}0fVprtovc{^V!`G>S_[uo?W{/qVog/Fk?VOO: ;iɟih4 i_OhѿJ>/h ,j?zo3hر -dC%NXE5nG!E$YI)UdҥD4 K9uOA%Zê=*4SQNZU4dP&VaŎ%[Yr]5[qΥ[ I_wĻ+/fqQ3 e̙5ovE&]tX3Cfk'#K8mܹu>1÷jۻ'^pߵg."_u_Nvŏկgo<ϧ_;߿e&? 4@t[A +2O ;Ý2-?&DS I 0QEc"sqFsQDEAO!T?]8rI('JK.tNsLL4dHTM83+7O>DPD:sPBSBUC\QH1tH+ԧF tSNstNC%QSUR34!PMWu֊Z=UDiu~lU^]WXATYf/bYhBu5ZlͶv\>3W\rӅ I0TcՕ7MvOpy%{_+}_ VR`| V`J7օ#\Xbc̀]wac78aKPd{d+َ nyf_Ncs>[-gy~T/ !,GP-XH~ "JHсŋ:cG CydIR~͛6hЅϟ_}AQ7nXT㏧P:BUƆDU#H"Jp)TҦ%{5۷pݲKݻxe.lLgT6ǏHLy2ެ3g ϠCt騨;;u3b˖s۷qͻINxC@KNkNݒ*ïd |̙kϠF%Ț~;vѣWUaUWA&X%vce`Uhf8B8ׄ (b&!WAbhj4Ccsi6jidkͦ$niHWzPA-tݕXRv[&'f?gf?֓{@$_|w~hgVЁ|>a"Vסq9袌Y]V bJʘW+v⋕(Brf׎~ϏYjD)+))j%S6g%utm)]zYA,5[yhmE eyCA'iA+hCP*fjd龃aZb~z">4Aޕjh꫰64dZ]PR t ʖlIK If0ߝN5.Uq a^,Di\!kb T!#B.hS>` d$nEśA>pg' \C'! & Ls݌>K*b朧}_PN:-WY-='nm?OG(ĆB5v7,A(|B~W ҾF4Z?z4H.?ʟ(#̈́41[݊ILmys3 ZPoñK{G^gDZOԿb)}0!\HCO-`nxj@Z!O"H*J8^= q[W$rjCcC5iuT"CPafF;;}KL$nFy!'iz$y8vL*,JVpYxJTޱPe2@s)bnKt[0 IJZЬ~c8cƬ!BR8*]Ir1eCxHRDyvJ: TX{GYA'(~V-,*SK[B|97QeAC!'LPF>f*nA 0hBφ*@ PJԢJ0)¯PꢜJժ*DlK-RCr)TChMZֶܛMvxͫ]׾~;)TaX9j٪YJ*@-1DB?я4ӠtM`GKuN#l־%|qlgK[V6d+RyeHJן#Mr:G0x*ͮvzEH3m"ZMz\`E.{K{᫄_%NM08 [xK;p\"u  b'nA;~lڅ{s:a oFC&A> AC?LB8d=& RHD^rh$ė"e*SBʃ0CF?8ik+πv5?49AF8[pCMM :/w&ZA~X!|@ѐAl@G!h$ngh8d H#Nu4WC|:qCcd?ZvH~x˴3HLl{z4AhvAҡK!w5?FsEWAE šOɲc; ȝv7vل;qj1Yģ gT/ɹAqTCʚ&61)>ukh2.57ATQ\08Z M1\0^7d A}23mvd/~썏'䰳ϼ;"o}:kћoHԻGl/wP;IPO~䅟>ʏS|Ͼg]}?}7&[OϿ[}]x])S X H (xF*0؁ X&H4"Xx,؂$28H8v7@u@|*(H(`jorG'PnzLJ~xxG h؈}舔|/v8w}x(؉6ц~؊X' H'gPȋ¨xh8(yǘH'1XTָ_ gӊ(gԘhc똉btXd-wx֏yV嘏ِ9Iyّ$i_)X*]'9+Ւ) 4\2i5u:ٓ,-ӘT>9΅<BJXF(KՔO)VQ"BNSPy^ GY?@\fJa d)gٖ1kٕn9ieIx9vɖyٗB==w闄9/)|Y(dy"񔊁'\ٙYxB0vOdᚢT)I-=SP)Y-Bʹɚ;9d9Yɚќߩi)y) y4"I99YG[jYy* :J ) ӠI~'BpJJ! Jx_rYny#jY,8C>H1ڗ3+z_I)j)5JbJI9Ĩ Z:r xjS꧞4s:mm/٧ZzJhF9QzfA ڬ :يڭ UzJzʮ,T (jj>׊Z:b}Ȱ +k[k۰#ˍ,9'++-/;1357ˋ9س>@0K:K8H+JشN[PIkT˳V{8Z\۵8:(*Kh"&˶. (2Kر6G˷y跀[F{۸y+c;+j;][[OxBغ 98Kq넹_8;[.x˂ʻ&ؼ 8ԋv˼۽ܫ+{+۾;W~췿{~ ~\w<} |_2S">U=ETw,03D]%3/+9A;=N?n%Ln^NYQ\S^K~刕啹\U^Na]@Jen׉jm]4PH95vDw]|.3i\.狮\N5G^.5 IOi~[c QO1.U~$nNjN<S^nx.NN3jJ#k^Ԯo޾QȞ^NqO>^489EJnoF F /o CO;(sP#%o!),!.o+E#698_0 @B5_r[JNFC?TMK ^?/I?RWR.l(nDl1poNguX/WA!7aoO !,,x@P1(TÇ#JH!Aȑc MiBɓ;R\ɲeK0cAMBɳN#@J蔣HN´ӧMPBիX^ǵׯ`ÊKٳhӪ-۷pBUݻxE:߿P L%iÈ+^ǐmʜLeʑ#sC- !װc˖ݱmI]ҳΗy8B?|<\yQ$H&njνlO>\J_/{_ϸο&`J!4l %m rۃvڀ!fqvh)QWzHx,04^ލNͧ8׎@'aY$]K"Fmt”TViXf9\JHEir1a&b4(t֩x]=獀zvI6裐** eTXv_Vhb9)&bG'9*cjqJŠ`}\*EŤ6kUi CJdvSOs+{zk2%d n=F[ 7+{B«ϊ lz\naD–;@,sþ'Et?Ϻ`-mbw6x[VRMrKao|p.hn'7㐏ݱWnyN޻V褗nzmꬷ.nuVwZh7;G/Wog/'(\T\9n$C!Ās嘉3R.D\hP}+ħCُKx̉Ki Nw #lvQD.@O1$6rz \AC"Pv\($zQ+%.z ݠD("B#rzr_8Ӎ46`6Xd+Xf"I?XTeqF(4OF?KD+&@Z+#EC8fQgcX h&?]3< `] 0(rhB! [N(g~,+px뎮 fHW}~QE(t>h&@ rWVpF gmb, tYPuah8DHi d"+ ĂQ[~ aLs' &<ˈQ\F.ثVK\u*W$˅\셟.u,k7P{Rf/J o &Ob2m #xγWQzT)(: _b.KʎF%s0hPG]5A(pAznV\aMZZ&w^2U}Mbf;[mɖ沟Mj[;VN累n{;Mr%6]mt~MzzηS}7N Ox5;ܔ3dNw27qH [xGN>~\ !vWr8wԧΝ/E8ַ^{.\u|hz3}Kw܃;T؎\fOq /yg*m€OKЫ6Nv۾ܲo=oo r~ovS{wo }OW4BDY)xHIJCNɔS9UYW9P[ف] _ɕY)c e)g9akmdV)hI^iz|ٗlI9 X9geS٘UQyyGr薜9~i9 I~}f7i" |ɚ} Ù|Y)})|)9}ԙyɝ÷izyYʩy{ٛYy7韯)GiǠ頄zyzʡ #*w"z*%ڠ-:w,~74w6z2a>v@h7D*vFz\Ju٤cP٣StRzNZZvYڥR`tb:GWfZth@Ǥlsn:rJstZ2x:sz-ק~r$7qzZqڨp: WZpoo ꨂ,jqwV*GZiکګxZjºڬz:Ԋo֭jo6 oz얮nڮ:n:VmֶJml[vKl ְk6Kk{j۱z g";6V&Kc(,[`.2[\4[u8+X:<ճ>S@ԔD[.yfJ kL۴PjR;yVVKgX5\ c^b_d[uh[jնnWp;5t+Sv{zKN|۷L;T[KkJI[tKIչKX+I[5+}ʺ $Jd+X<ջS4N{֔KMۼ;[K49I۽kJHJ建Kػ{뾞xʽ IX{QH\I \Hl lH G4I\xrx“$L„d„y,I.2HGS5H#Ç8?L,ĆFFHJiE }ILŃdNUL=y*A a'Lid7cvWw[ m|_LmDkloy)i;|i䟀L6FYRJVY%Jd\v[^s)V(lU(ohY7Ng&tw5R7)kV i(`_?JICZy VjoEc\)p*ꨤ木FM)oUTt9(I\*\νTuGꡩF+쵿붹eW)vTJ_YjHE?/2{>U+0VAҟGi#JZ.%](0o ,$lr] ^orRܯH D_U1emWbc D$yj^J֙ДǂK;ᩍA΁N'@aJZ aLXM2Vw 'ǢL4YqdMIrL>ٸ3<+G`IA%hn$6Yk6L%Yuγcx=03M ЈNtF;iG>їMJ,X7iSazҝ2yԨNqJfU$M Z,wkԽ-b [ 4lQ#)nUͶzunkV+h܈.7B?{npuF֍~Y1fCO8=/+Ef G|( 7c%.iL<#OyKr]V&]{6-RqwU|<:)}s}DїPӋ1(ֻ+}`bs}~NEKl;N1xiPp_x, ƓO{'/;{e|?Oz8/u~Qb۞ZIwמi7I;?TلOE.ǿ\OU6~?8?ͻ~?/+e6H~owy ؀ z'għǁ"($X x(-( h釃1ǃ5(GhCoEŇ!脰RH~TXwXxL[|Zb|dX׃hzj׆nyp7tHyvxzx|؇w8xW(wlxvauXVwuL؈8lX 䅩(x>HzȊx(uXu؋8y8WxxȘxHXw˸ިk׍8xhv؎8cZ7vZc0֡b )6$_&zɕ*:\,ڢ0V2:[U6zR8괣<:N>BIDZtHHJwԤNGPi4T*FVz^ZE\ڥV`JEb:KTfDhGlC؄oJ8Yvxѹ|J~Z ɨjԉZE:STDHjD>CZ:t*C0ԪB%4:Bz!:Aګ@:Sz;ghE]zEZUt:EںJԭފDF4Cz*p,۲2<7{;4;;B=?99}CkBEGۯ+kK˴0O MS;MkW8E[5I_/Gc;g;1zjӶQp2r[t[sno뷈(Cykbs۸+*;pR}^˹)㹅 tKZ3/n; Bj29뻦s')ckw뼿u+%¼k0ͫ˽3"D+B6kтݫ᫾ix&2km¿kZ) FR^ X5E6U b$6{P 6|6-lVUFV̱ VvƦ,sf۬ -Zڕ[W@#Btt`=ЀHkYEFfg1m%H!fV23^-P#{^#Nd6B["q#K!05 WT /877 VF"*]F  Cȅ~(1jbcHIPBT E-['t(Z lا?&/GpS6٣WG dy> }ܨEB|( $@!ŔL*wPJ"qJ, )Ft9Z |$xt6Z:H4{1aZؤUL)U4/M.L:)5)%lğ4B~= jy/PE!YJWp6[2$oaFe.u! "b %MLgJӋn hhDcZ5P~Z6UYArî~&Z O,gVՂD\ψSj<K,ԣe$]k BD%keυT1,#*. Ӯ: 9)*NU]—DYSP'MۍGDQ.o UWFF9|Y,A kAFgEkZ f?o\䪊ݼj,zŮɍZY4 BclV3-qmC3&1siKEZ@5H|VZX֗mEnbAȠ*ͰW.rܦ<)D)zY޵pBtt7BH:]xǗCɷBR$#+7}?) S (EdY7!1H' &z9:ۙql"ENV\WLrK.a4ldաEȝm|j21ո!>3(; sꙐr;LR Kk!nU˒T@6g}h eٳ әMbN6f;&NLiCV7wL`މqCl6 mەB罸2"<$qfM67o/߷ NSvrnkx-NC\w;N~#2gLg<.aE{0Ї ^68sq!#'ԧW +ǷQtO`{s7d׾thgN牿Ч~+_s7ugH ag׀Xx؁ "8$X&x(*,؂.0284X6x8:<؃>Cx@8Dxdg'H؄ؤNpoŧ~GP(\xZX z]XA>znW}f5}z6r tpLa{uu:}z8:` (}G腆؇mYXMׁ؉8Xx؊8Xx؋8TDŽ Ntv}AAmx%IluѸ<׆YX;Hjdswf{liH~vƆ|q]{LzpX؎7ݨ)Urz!7po)lqb}s YO)pWP~8u)G7u؜Ԣć%{I"z|&zC>Pt i4id7z؟}YQl=z'gcJ{hJOQi}XEWIozxz|ڧ~:Zzڨ﹦ځnZ瘗ٷ:s}Y(pنr$|iY pR9%ꩰlW)뙫DwsHaijrHZ{ٓo oJs͚]@j:o |:mߚxz蚮꺮ڮ:ZzگRJ[Dj)nasᚬ  x론ٛY; $K([K:)ذt9=(#;q;+!hyxJKشl)@˒LT+V[N+S @*+5ۘ3ۜ:jl۶X ;Iid8v`۷rtHKTk kkؠhL˪u,|۸;WGbL[뉷ș{Jj|U;ɍ )YAk;:vJj[?|IC3~'AڶqzEkZg`q;);軺 [ob{zQ۳KGEZ뗓 :w[9|+ L;,+;lHl¯**:z&|Kll)KJ lyMgʿMB/:zI۵BL5;S,LTĠX|\2\_\[7\9|;^~ fxU |}"a;W J 2"Œ, o\m ݺ~ ɨMˋ,#r< { M~%iꢌw̜\},Unj{Mx jTl0͒MT*w>y<4r|tnL˻6 z Ђk{[$w(EmIVΤt\S{Ŋk[ (a:aZhW=/>˛HmS$>>^~Ӹ8mwӮ#-M;5ٔ״]h1\ӗ1cC>9~ɛCnʯ+-GGY1ۡeص-Σ5,^+5>~Q}3ꃬ|ޔn}A.nJ<H ږk }uԅ22ቯMg{~Ÿ./}Vj Ҳ+K8 y  :. -4_!VO19~\_Ml :u|r\=χm4 +aɂ.mq6?< $؏@cpaC F!#nG!E$YI)UdK1eΤ S̓9+nyQ"Ɵ?1jsĝ ^\SJZThԚYnWaŎ%gӃJ vLڝ9Mk1#ݦSU jݹ [aĉ/fIWR=Ie̙5ogU6iiԩUfڵS.Mmܹuo'^qɕ/gsѥO^uٵowŏ'o|iկgZg{g}<~p@ 4@TpAtA#pB +B 3pC;CCqDK4DSTqE[tE[sq<wrH"4H$TrI&tI(rJ*J,rK.K0sL24L4#(M6M8|3N:NO@tPB 5P*DUtQFuQH#tRJ+t/T974N?5;G5u!,i  H*\Ȱ!A{(!y ̗oL# CyHdhO%H6$~E5`'qYА8̠{\ʷ =QrM74N\# z[/J" gtX:>{FYDWg'5ۏCIMG$Wj̡AoV7!OIw<@јS)UT >=HF>SB!,g>!,n  HC0_>#J,y1b=kw Bp&\i@v Vc'enDQGDs~_AbNr^eMo$'A$K?xB:}*PTт'&J~q!3YB_2 |Uuֶ%/$ 4QP~%3u:s31sS 3dsy4Y/d=: F_VƓnz(,!9k Ja}.9J.NǽS1ͤcU#9EAINf^3$mu@!$g?||QM7c?8(9E!d?JZuuO<)U:gQ?h\;$f3hъET5P5⼣N0PWyyjb]? 莼ˇI1Uk:ꠓN0Ts wqb]PRY!蔫_$14CduI O3ʺ5%b0i0Cjq.X"$v@t=ܣX:<M@l#<!,n  HC=#J,nw ʋ'0yQ(<9K`#b\بY? :HʖjnS#u/GӦџ;!6r5E&L&WFJY`Db!/ 4-Jon]4k4jA(4d4 2h 5 1&41EMݚ=ԧ ܩ:FWU'[#2պm@J\5nrFx7eJeb$ ,K "& yKY"2M/>t,YN\]码QSO=#P# *F _1JM|`%wZeѓH!#=9fre3`HDGpXu?pZXOw ٳPHv O>$Xm!B!,n  HשCPB#J$Hϝs ؏޼g!F;xr˗A2zg\+4x뇦Py0yP&M}.\L4x˱I1Mٯ(Q F`O+1xU-_{1=K~Ff.|~ᢕ3p%ҵQikT!2Eh(L$3_J8CM7D_ԍrv'絕hnM34Q?͠KE_UcMxA< %\P uP~@M$"Vp<|q8\4(JWSR=۵0Sd1Ju,bTfd"&[+O@9#E#OCO><#V?u% G5E5'hNd;=]J<STf!bO!^|_¢O>W\.sI$|ӏ8m^(4NWh9L%K# ^&9BI9m صcpScS:Ԉ;M#uIV#_%Tr n|Eh!DhB ;38a( P"bRN$377_ b," ulwlL?hï=?<䓏4>O͊:U(DJ ks?!4,o  Hˇ`JH =J[Lج(s$uVr?_Ӫ SrS._#/OШsɗy%^D:K)$'QسW(49(W(ߟxHcwFYBI^T|]r(חB$Rw /PܒD8 %'9tnT(4}D>Q6-%/AS:%z‰$(4hs&~6vMcn-@!X%3תݏ=Ss nמ=!,KHDxp 'EDD /2P"1 |CD)ɐWl(cB_Rfi2cʀ! ,KPDxP@(bB%"3!4hDpG4"R&Q(%HEQRҌFA$o:b@!,KNDxP@(bB r2!?h DIb~ bQDD%E"IZQ#ǝ*!,KEDxP@(bBJƢB"c1:Aʇ) R%KS*!,KLDxP@AD!:)(QB &0"Ë&x$Ƃ|SJx SH0!,K0DxP@(bB+RĸQaF 5PdI!,KQDxP@(bBh2!?h8I$¢DbqȘL$ŏd,IcDxztx(Ā!,KWDxP@(bȉEQ E@Cf"Ch)`@)?)@"6>CCDbqD#&щKl"8(RQXE"zQ_b8,vhcɸF3qo<F:pģ;1ܣB9&5I" 3)d}$BDN̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2O28R$|2BĚ\S: YDO!ܴ6׹2?_x;NvS0"*A?'H4mA)5NAIM ^SJFbPK' CE?ҊCۨJ"4$O'љ?+6WSJ8D4AT=MVS|#j)*ZU+NCTDU Cmֱ"^m\J׺"&v+Uv׾*%~ + :>¶f((b+ƒ$^5EYN6ͬGP%ijO]4KZԚ_jXljH-lbBX=Ap;>D0EU+~5oKFЫ`kt2݃TmY]ب /BԚwz W{Kͯ~Gw9MlDzjZ2]}BW)a?SlB0e|F[9fh(b&b 7TKBY2PQ,t!=1e% Ĩ`t]\ VNߓZHYɑi$k!D~r([+M6+2qSYS?;f93!_ ]DWe'EU=L(C飂rrZc4HcN3$Se,'asTX.G_Xsbmh8fH1iecN壆|Fs\*5}{ 񆃸egXpxC^u(؆Rs[*"\N ps9';xuƃʼnƊHfȋR:!Xmcɸk86dԈ芭Kt{t 1\#!~8uRuPg~5DCD{69~T H])!a~bo֏َyX yfv_u q])_>vn,vvXa yP2* . \bC9&8-vC'yMɓ+ٓVYXYi+iajx9$Ad^{j>Ėj1_7t)^w9zgV9An s(A~]Fpqax햃Hx9WJ|AfǕGzFk}8X||Ci|y|w}{aʷYI]كR1~ }!~wkh)(c}5 )FxYA'SIEeaMIA1ȟ-Hr!N(Jh, Xs axVXt1AcrpsXe *!,Jf%M/*0ZraըhCh8xڸOICʍ4aMڌVj\؏EW5舑Uogh qꏀ*בctAtzgIʦJHnWJɔWIZg#?1;Yuu4YiYꪤz3Hcw홫EyRiת)ja)JLhy$Һ_{9׊ۚTzZfH9IڕYH)_5>#1|u:9^˛bz {˚zAۛtA5؝yq ;H:)0˜2+Qu:;r! BK G;4O{!z̘V r!ZFr#zA5&fJ2fa"z4zy lU Kڋ "Gq_L.WA^ DK^zt)Mtx{*{8IHwJj:1ѨKz ٱۻs b [#u:>J(1ϛD ΋˼뼭ٳ:jൾΊn9zԭlqhAeq   IZ `K4A}NIч­I(lD[{.۷w6x[33~֙6;;F|?KL+QS<>[W,p!A0sJ6h W{m̴39;HvxL]{SY(ZZ؆)r|eB4+J/l ,ɍجm |[wSڹJ*D H˗ 5aP4̏ ⎏̥zë x</ьĻ͎j[˫ė;BɔL|L̩Wϲz=A *  L5KmO˿ ҽy})ҙ$ mҾ%4ӴҪӲ$ z> =| ۙ'E]$D ۬M C4lqA 1ZfYKm]}^Ѽ.N,^P^M-ڂDmx06 8) /nk1.35fZȉl;=$*GOc $XA .dC:)T E$JԈ#? (IѠQI&K FhA(4méfΚ?#QȉC)SN)5*ӄRZ%ZKaŎ%T@]nZIv)]"^~9دB" &׮ɢQFn| ˊ,qŚ}~ΥU7<=ڲ1Mz4իa%"$lj/EܷCR^wA{.:x_}{ݼ]O~|0J dhC@A dpB %PB0,2bCD@AĻFE[t3a,kFsqG{G rH"4H$TrI&tI(rJ*J,rK.K0sL24L4TsM6tM8sN:NO@tPB 5PDUtQFuQH#tRJ+RL3tSN;SPCuTRK5TTSUuUV[uUXcuVZkV\suW^{E(, X90 ,MX\HJh=fr֭h*@Y2}HʖCV67 EuLr"^D($,- AA" )$H$[_]tBG8).GLX8! Nl.?pRNa>9e ظe :d1f~ͷ_J'c~7R,l2dܡ'FhgI4PoQvjE]I3lBI3@ëfcGU2qqfE#(|g(N/O$ HKȫq/b; (] cIbP!:Ab23N$BBH )|:6X$;HY ! L ؠFN W^ѹ⇐-wT5cE":qM"N/௘ ]51&N&gF<&$.#5#"/EFS@W.^dVF Hqʕ];|ABvq cj}k[-EF/ְ`A*:ъz ]Җ=ĥ&J'3 ӌd-҄$GLxSV\АdQV_=Yތ!)=#^/?2Fox<8NYbX?:RwA\4cп%QA0zO^ /t^BŇN9e(u1*"(Şf)TdHۗGN<&_VM&ɍozPEsLۈB٬?-+a jI2|X[_"|O %yݤ?}·II|X&Я*ʊYԢ]#Mf{>|mx[w}o|[wo\'x ~p'\ gxpG\x-~q:Yl:q ]9^r{\(_O1Y^7yy]Cyent]Igυt}Q:ХtWS:׷SWL&D'Tp'iN5l/=t{w{ >x]{+S^|=yУ^'M;v%S:}l_~|G7>?}W?~o??/?~_~?G?o_G @c@(@D@4d6s@l@@@  @ @ AC< D8$D@BDCD=TAtFdDC|-p1@ DM4ODEORDP4TDER\Q EXTVXdZ|EYSE[E_E`EaEbtb_Fc\Fd cEffTi|dFgklFf$i@/,;3G%Bǯ)G]Kv\Gu$xds|GyGztzG}Gxy HH~ǂǃGGdHlȅHȇHLȋtȌH$HHHɎHɓ,IKx,c$əɛ|ɝɘIɞJI9LXD"OD i0Bk [ d J# y#'HB/!"ɱ!RȘc >"=RARUtAa3ڙJ7D8uR+RR34<`#{"6Ű O5]ӰS2+Q]T q U40UqR,m&YP*UT GIвISJmSO=,ժԛ9!jQI4+)VA]~8pxpSB%BSdՈ VTxLF"vwu1"[֑yМ u8wBf$вtUה R֨HѧX{6(X(ocp)Ű'5a[xX= *O%ڢW8F˂WcǏUu7~Jb+)t*)TiXԠ&@ e۶=-iB(INp$ۇ!,ODxP@(b#'!cQ@"'$aFQMdʓ%=:@4Tlز 1 ! ,HDxp 'EDD /2P"1 |CD)ɐWl(cB_Rfi2cʀ!,KDxP@(b/ AȉEE!#DD) ɔ+MTɒ˙1[i"E!3,0DxP@(bB+RĸQaF 5PdI!,QDxP@(bBh2!?h8I$¢DbqȘL$ŏd,IcDxztx(Ā!%,WDxP@(bȉEQ E@Cf"Ch)`@:?FMݤP_ %y[x i B@9$FFxeQ@!,  H*\ȰÇ#JHņ2jpǏ CIF%S\ɲˊ'3I͛crɳϟ#uJQB*]eҦPJtիX Vʵ+ӭ^Ê vٳ.ˢ]6ڶpJ|+݇mt_{<8p^ˆ書1Ŏ#,Xʖ3gŬTΞC/-4QҦSDuM֮c-vɀ!-, <*\ȰÇ#JHŋ3jȱǏ CIɓ(QeI \ʜI͛8sɳBv( B( )JR)BNJP(џGz ٳhӪ]QR1aذ}X V &]z+Qۺz'&^GX#G!ӨSF-P %5|QD". p?߈u Q+_W +FKW" Q:kW\ .C!(%2Hz1 |DshT)seJ|~Mn3H%-r늍G/(BZ @ @lL2&"x#rP-li2 gwI+#5ǧt.d2;Izړ&dȧ> |%'ϊTj%CAϲUD'JъZͨF7ю&ӝМGEǣ(M)wPn6R+'3i2tsҗv%JJTtj$;L ECҥBUIZԮz&[RǎĹSg=Tdwl*.SyR1+VV5<+_u6tQ}JxMѫM嬰V5We#-{ٲ kKW =N_)tֺ!kUL+ڲUvf)+Wr+pqͻDTzyԋV_KݮNV},tgZ.9SnH=2'xZo\ӭ~Uz%Ye{ɦ7u7cS"So};Z6թ` zذ}/hMep`WUŋد+ms%ku^ȷŮY;_Wf2p,X~^_cS檐< #G7Lɥf삗po~Y6sF͝[8πɞm<@ЈN E;ѐ "MJ[# .N{"Gm i=B>G=ϝT)GQ"jD^:ަ:u*PpTw*UUDLH]66IebW0fBv[8е~{+7C:8{ :}P/Y:NgG CJA*LK,d+jLZ餭JGdqЇHOѓnNԧNVϺU{eq i]Mvgcowhw={>O|/xeEGXZ@M8JLSTF(XY؅\UWx`G5CVRJR$$Gc2&z|؇~@rf=4; @yp7qbD8𗉘lj׉hxhȊ`,q4dCfp;*CDQr!!L$+W̨(ϸӈ՘؍Xq0'2qׂrk5s0s@3kJ][^ȏ8Vb(  I OHa,gfP (`בZ&t蔑'%Y3Fu"Yt&ْ.0294Y6y8:<ٓ>@B9DYFyHJLٔNPR9TYVyXZ@ifUXǕb9.uI!T\ 1Lf] .Ts6uw:chAoԡcBq#Fw,)|7 !!t0q͓y !?o}U&hUt3ј GRQ a9! N < #aqLYTw"3kI %VB%|0 NsJ !{fIysW';w"`BKA]QvF "`$"H"!y# !H?zbɟ (ZB c"q9]l8'Ȇ'(b-ŃH?3(){*tB2*Ͷ* m!)B`TtyB(P[JV45-Z\)^a3yɢ ezgJ)}Ħ*b՚s 6C !4?KV,uQpp3n6 5x:3:x#h a·ZZ \Aj9-0󪺪ICe Wo_y4t:dE*7A*l31iIuk9uCJq z!n )J7ZŚ3ЪQҪ1֪K#D}4,J@hpW8G4ZϓȚzk/"{ڪtxCc:ʫc$*5) Asr6ZG_4uvEy%qqC!pTʥi+d۰*Bbht IaOzEy{;}+SB yZwEz˷V%3PRR6g"ۨZ IR^{?(vzHCi |#G9'Ghyk=W0꩚aKj-L.ô<t L {aLн!k-˼Ǥke^i)97 SD(ǿKt$9Uy&0O[BPe[N8ᙠ^f\&|(<̓ey9w9L:-?Aǜm;@n9[AHELO|G,JlBZ1_|P#U|dL^,hñ"awyaٿA  a)q: !ԙ^ĉO l\NPȑ,W꽆܄ڛqy]>^0BKF^A GtBJ a89a۪Wf;8&SK:ߦN>׾L~ګ~Gˣ] |:x |Wf۫$n ka~cy6:2kAWqʱ]kC{;Hk_ˍۥ}ʘ@9U:n9ToL2^MҳQow~upW[p ѷt3a˒f$2s'OAި:ug3f2:o;D V_0BБk d˸BKY|/<; ƉLoo=mZ:Y~<,^ω{_D٬~Ko_߸s:%*-OQ?٠?Ĝo "#HP^#瓙zjbzk_k{l6lFWRp;oƳgIKN;cajӘU=)jq$RBq.ibN$<'=)ӊ=WR鐂 {,V >LUXӪBf{&LP@V4TLE9ҟgXzs~s,f XQ5N\Cp$L [Gⵆ@RY[$vYv6f-h5vheQ/namd \~^f-9Ā"`٫0<l{+Z%Wdc _!Zd+%Ayլ>5=f0ZDKEMꑏv9w׼n ۷t#̨;kHCZJl23Ŏ1v؈9sU.udG@%؏1ܘțnAC+ #[5Pcm3C232dCk?4L>GYô6$ ;ë9YB*$ |<0DMC璾@z@LD;:)4D ˺zjC,Zt.Z=CDLDSD[=E*³:#DcILgT? Dq?{?3;i?D#?t43aGnD5k,? XFb;hln˼7[st D9ť8,H5̻a$L+|I͋I$4BɕʞBWұDžl,F@ɧDgEǟ1$H|~ҋDJ,ߊ/D"^$JJ²C'6.J\D#{DH9.MSHjLDFÌʪ*5=T47UZ1Cǻ?u>єLDm FudӜKBn\NQ̢|z@XG=GCFS:M,m^ >;E43e D>P4|LTb$S4%7FNK!%Tk{8 IHO}t}ҥwVEWRuSOϧ{/WJz XjB>ϴ@}&P 25r 4K&TUދUKϫRR~]Oz=}ČXQҤDˑK3\.Et#d}sˬ08ٍM|\ݻ[ A-JT1 Cyݞȟ--y[õ]h ^^-]]U^bOސuT݁FJ]QEQyd85ʲ]EϽP`T(+1{_4jj-܈=˵ܺߘT`jB`fY3Y3PįK %=DM,3jӛiw J1I~1Z1&4@[s1~ ,bVN`E;۔]gU"_rMD ^=D]Y:.2nbٍSIĝcv=c|O?6S%߹m>> P~B7`m^HdIdJdKdLdMdNdOd`Peo`R>f)aT^eeaV~eXeYeZe[e\e]e^e_e`fafb.fc>ed^f)1ef~Qehfqejflfmfnfofpgqgr.g_]d>c!Ot~凳m@u?}gx̸5?|dy^Z=Vgc66tfUU(&Ul_M|; ֻ%f YDJR.wߦ؋#;NaU4~ 7^4[X鞎F%iXg.\.m#課(e=#mj*GԎjkFKվ6bU?tOnퟶ`l2UuSl?5,WNԸk=vEg=f6:5o.C!hMC^K[f}FR~oyh&mOkcsL-_UN2{_ qq/q?qOq_ ;g~bl/fӮqqqqq r!r"/r#?r=7D~rGJ;0\Crjߦ2j.6)v z%17po?q5%W\k۶K&cY@)fjiB2kFW$C\3vֺMoNh+9RuXuYuZu[u\u]u^lNs_d;tboG?ve_vfovgvhvivjvkv24VL\Jowpvr?wEwo/qOww_wuwwxw u]Uvx7w{ozxzx'xGx~7?xGr2LӍ7x'Wyyy_yyՊW */z?zOz_zozzzzzzn\݄-ݮzUz/{_?{)ᬟ{{{{{{w Fږy/|G|ŧ?|Ɵ|ǧ|ȷ|ɯ|Ϳ||ɏb|?|ԏ7W|ڏ}؟}ۯ}}yx{hO~~/~'~W_g PvMv~~/LvaO4v/d~,h „ 2l  FhbĊ7r#Ȑ"G,i$ʔ*WBRaƌ5RtPf1]ӧN4JfΘlh)ԨRRj*֬2)ҠE&V"٭6vES+Xpҭk.޼yw _B߶ L(۞#>LAb ~lg-m4ԪO*pcE {Њ6-4A-kgmyk}8qq]m:گgMj`#wNfy哅ٱW̹zzӹ,Zawbmw *DW\!xq|\ն_p5slaۇ1}h(]s!2x#9ꈚI0Š!`ѦqYf5 Bya2YXaL g%ya9&+QV}Cmߛ2ӓz֥ŭ^mc@נ5W&*hBZҖg.ʆg}5(a0z6:ZUޜ(*|hHeZ+&$]:*,i:;-~j-;;.b[.骻.[{/T/k|0v]x%0w R(1kܫ!IF_C<ԃ=NX"z=Q@DѕKh2`ph<@N42L\4"OT<1ObId#DO8sN0`C\Og>QJcZ> !,i  HܵCB#J$ϛ@v Γq@z P:(AS,@J 6bϥ~iP<%R)(Q#u|[y˘gּs|QNqԠ~01?آAh3hť4%z2z;4dLFs q5\h%:<ТDMMkY׫HP!rQ"3 \!o~ٛil"MhuYӥFdyM5]eJg!7~Ă'wZA)6M:bN4 TO?R#$IF%B51(Տc5Y#HL"3]W1vB3LG 1;hWd@ VQ2H!F5g^ŒHtu;cQ?ވ#:lty3N4F5V?cA׳=1t#=n=L=G!,i  HC=#J,y {^~1d FIN\ɲ`#4*HR\y`Q)ѭD~bH˘3 SMzwVD5].xMg.jMD/d&E 4Fc/xdPUȌ_& /DY"QK_(1?sU(_eVH޼t9gQ.iD~c3W~\.K'ޙDq(J#JhИ!ӍBpwkRPN}g9!p|!Pb%8^4JISS=pOV:5It ~cBN!(k%%Nv zxb"p#(E9j8%:' ^8>sG7<;,"?!,i  HCP>#J,xz hO < Arɓ\cG C`Id%%W>(¤ǒ 2$ R{ 8o\,׭/tү^%h1HnG2+AQ(N"sֈR)=Ϟ94)!h3_~0˞( {M!Žs/] Bc?%Z(W3L"$3j{hh2K)c?g6{=j^N(kS 3M3=(9I4REeUEo ClŢ\NkD(rJѐG&A& Z2o^D.C滇ϧ|9w/Ӂ$WldIDyyf]H\|kQѠyDBh-fM8!SΪ›4g' hmjHDfy\wI\_ H%zHU$$Rhfv54O("a>Q61%/L&=ڶirhܰkqMMF>0'@0?(S!k KZqFdee }:;;;\>n޼tF ! jxoV^xuE.O7oƊ+BuEtƍorss[oGYaa!oߎ~wΝtF !Wdl߾Νۯhw۶m.!aUUU;+~;{qVjϘ?'V@pՈ]vY%&i3 !duT){uxb)5xb]&1 o4S$zHtJaB!Sd0[W#A=.*m Ba7q8X (L!d")y „BԎ 0m8t废Sx~0`B34uj1V T`&vB!3N$Ѓ% *1LAABkO6,K-w  B+5x* &R!B !>g!S10a B  2Vz$6>[yxd|*TjB| h. Bo8-H0!/N ѭNKaBP8U}?AvBV΋ƺxH<כW(kTM+17.ԩǯoWZV)1fˆEX$'/f.kTDQEVkG'dtj=X5/?#Aᘿ' 2oxޥNTAk;fADXy\ ulغSǛPڠuHá0!V8T~[/يo*/[6"-Z:Eq18uNj;Wݲ!~I2)[m9G_AFeaH]5Q-sYʬ;:cJCHܝ>-@18Yӎv \K(z|]HOgqw'g.HFSj`obVÁ1Tf[wƓ;3$e_ 9Iki*5{'Emsk_oƋ̓UM3mnƻ ,Vliydt~:C-iwm8TG^=?d]#~MsѾy V~Ør pK6=~ zd#3ޑ)0rҫ^/AKW?3a~r7| $x|,ϐ#,P ?.qg$a~xd:ԃ.a}O)z-=hEH.S5iKPLdL縜Q)9IH \*AT?B>f& Aĕ`4Y,#LyS7kEAn"}|4H<5Ba2EZX(Kɂ\~̉”p$G#5*bpo QR߉q ch oZd-, /\`ϔ_ k]ڽ%J ͎x>Ą%0[l(Į5㚁j ]S$>$0 Ve=(uT  jpkq95siM <ܒbT Fܿ:7OL?rÑذ-Y{uƪ_;Fp0H.XÄW͏qˋj32dxob;"NDsV 7%b??Y2jiƻ,6;,Q࡛yO /%9}F~j}CkEEJݳUDX6.V;%HFJ!2&Z~6`ϖ۰eC. 12&fgvz%UZQRl)f:}|.g }` YwcUW;'1u,= ,RX:O7߈9%0! zi1=$P,el^ {uw2"fHK@]Z&d,b3aR؅7ofA\?OGX'錾jGi;!3!* 2 iTq* +E{պZp$\.7g[ %Ph!U2w>ݓaռhd'0?!e>PuJ-EVbd-r"DC,d<էZ~&V 0D$49N]pqfh"p,Kcyzb\@Z3b:760q֖L9;* Ba2U%xD\d^z^PtpZ0}g-{ y^F7ߦ Y]?W~ZQS,6Ä1g;SFh=|'& Χnœ`|e]}*{f-6v+[' ݌+ąbS7ᡛB"ka-YOp|nfݩ>U)|e>>w{{YgKz2қ}@< <3EH%غ1g6@<<^,&1nX&ax-;B(LTXߚϷϏ%Iyq8JgL0Z> ~=&Sq2ܔ MrlРA -=nVorݚ9 m֣+Y>S\O_&BaB%Ejt]N-;4hGFwܹYf3ؘ / Z6}}y<„/.<qၸyw[6tW+:({uhաoM&sr„ҕ>Vcm/R   ;BE}/& mS[M ZVV62h`vKDZP8WNNo w5 `hݦmv .+3NJc*݁VFc[Hv"ŭFsF͚p #?39W'@9?ELӗdLddd`Μ9#?۵k]J4?!ǫڠ3vth6F_rV> ˓h)LQQQ1qqqFҥKo"qy*#b_ cڱTҲ>p{9n& .:h0q0ٳg\ HHHo{od2r\,*ΥirƗU:kZ'z1{C#s4MN4\Gk׭Dȑ#Z%cjOt,|,Mds,q%e7d6'%L3lR[ϗ=3DWWU?V -7ۊUG/1P}g嫘jй>DB~lfUbr4zF;iWϏqFJ>.ARȱ:G`r60W~f 3B[+Wi0q7OZjűcX}/MoES[~|bhphF O]JES[sO,d|yذ,vgӫ bdelYYʦ_|sUՆ+s³K%7Ywcu]m\L5>=ujQX.ǂBDp8^_L&COOs["}KWhehAg|"ܽr>,c,#58xdi-bQX݆]G/1VckE*B@%jԃ&(k|F`L$*Vvcej١4AA#&سgL&l04$7n/`K_1'e'ʜrMk2p}:lj7>/+_Ubi ÑP xpq#_/K}o/C7ͥڞai1.3ي5hh-7q̮k[|ף0Yz5:;;QTT4@d(((իWbCO~;q~*EqCU<.3["۲O\z f (z-a yy_b3xPm0Hh)jvF7VA5i+SR|u]3gf$w.\.} W t~7*_XYd ,H? A(ABhdAoS=A4qNхN-YKD:uu Memb<| j,LGAn"#ǖK%#ן<#R#Y R<hN?hii 0D% X17کTx#o{;KeCȿwaH-c;E%^iעX=vbeRG_?O۶m6^\("//Z݂Do@Y ,.*uzR ~>XGz*Z]rF&x+U/s,ǜ^#VYHK%xt|ܿ:ݣÂ8r2Y>jrv@!)2ѵü>L%=='(HKddM;^εB3h;"%=5B,u[ :/}qiA6-J`5R[Pig(r~ܼ >m.8SYT6]`KJa=L@,#,, b@,HZ-^i;PצƫS hj[dƇۙݔln\(<5u3y$*dh eUk2E\r¡3,P䆹^q}'˃f1[Z)n& ! tmh8ԠSU!">U.7lAR8VZ{{1zW."ܡsky *XNA;Iw,pp3"4ЏM|6ap !P[[ 6 a8u! oo)2d.{ͼHqAc݀j,KAddrn<.ω@ukߔ$]XYWNJ/nFR~H 2uի?4, 0"P,s%be|\<WdDy b7uROdt0d21}:Lâ:yId9ugib (wγpH~^GR\p32ɂ/M70:b!yؼ.@q wl>ݳ`(ܿ: dĆ"/5KD" 0Pm}hhנSGπjidy[p>whYz> !$BDK"AT?bv{th*.]Zz כGdj /5ODuT=*C'˃48$!„BCTB(L!PB0!B(L!PB0!BaB!PB0!BaB!„B0!BaB!„B !BaB!„B ! B!„B ! B!&B ! B!&B(L! B!&B(L!PB!~ĘFIENDB`isort-9.0.1/tox.ini0000644000000000000000000000364113615410400011120 0ustar00[tox] env_list = coverage_erase # When updating the list of Python versions to test, # use search-and-replace to ensure that intra-environment # dependencies are maintained for parallel execution. py{3.15, 3.14, 3.13, 3.12, 3.11, 3.10} coverage_report mypy [testenv:coverage_erase] description = Erase coverage files dependency_groups = dev commands = coverage erase [testenv] runner = uv-venv-lock-runner dependency_groups = dev depends = py{3.15, 3.14, 3.13, 3.12, 3.11, 3.10}: coverage_erase commands = coverage run -m pytest tests/unit/ -s [testenv:coverage_report{,-ci}] description = Create a coverage report depends = py{3.15, 3.14, 3.13, 3.12, 3.11, 3.10} dependency_groups = dev commands_pre = # `coverage combine` will fail if run twice in a row. # The '-' ignores failures. - coverage combine # Only write an HTML reports when NOT running in CI. # Also, ignore the exit code if coverage falls below the fail_under value. !ci: - coverage html commands = coverage report --fail-under={env:COVERAGE_FAIL_UNDER:99} commands_post = # Only write an XML report when running in CI. ci: coverage xml --fail-under={env:COVERAGE_FAIL_UNDER:99} [testenv:lint] description = Lint the project skip_install = true dependency_groups = dev commands = mypy isort --profile hug --check --diff isort/ tests/ scripts/ isort --profile hug --check --diff example_isort_formatting_plugin/ isort --profile hug --check --diff example_isort_sorting_plugin/ isort --profile hug --check --diff example_shared_isort_profile/ flake8 isort/ tests/ ruff check ruff format --check bandit -r isort/ -x isort/_vendored [testenv:docs] description = Build the documentation skip_install = true dependency_groups = docs commands = sphinx-build --nitpicky --fail-on-warning --write-all --fresh-env --builder html docs docs/_build/html isort-9.0.1/uv.lock0000644000000000000000000144637413615410400011130 0ustar00version = 1 revision = 3 requires-python = ">=3.10.0" resolution-markers = [ "python_full_version >= '3.15'", "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", "python_full_version < '3.11'", ] [[package]] name = "alabaster" version = "1.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, ] [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] [[package]] name = "anyio" version = "4.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, { name = "sniffio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126, upload-time = "2025-01-05T13:13:11.095Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041, upload-time = "2025-01-05T13:13:07.985Z" }, ] [[package]] name = "appdirs" version = "1.4.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470, upload-time = "2020-05-11T07:59:51.037Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" }, ] [[package]] name = "ast-serialize" version = "0.8.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e1/a9/11851c3e02a3fea2ddc9932d1fdc7d2edaeecc0d2e11bc5f2a7fde2b0934/ast_serialize-0.8.0.tar.gz", hash = "sha256:6c37c43e4004dfb42d321ddedc569dc17ff4259296f3af577c9ea46a809bc010", size = 845638, upload-time = "2026-08-07T11:29:02.152Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/34/16/6e520b57cd8c75914b38c670ad4593d13c22911e4306cc7165dab8b0789b/ast_serialize-0.8.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:3d822605fa7bb326ef868d25fafced7fc660fa46d9b90c02ea86d5e2f5d325f7", size = 863924, upload-time = "2026-08-07T11:27:34.579Z" }, { url = "https://files.pythonhosted.org/packages/03/e1/48802de9b22a2bcad42ec80601a17e3f69172fe4f590e6311bcc2b323aeb/ast_serialize-0.8.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:2efa40b068197d5efb62655b43baadb842ed71c4958cccd3e8b86a35726f0119", size = 1177662, upload-time = "2026-08-07T11:27:36.196Z" }, { url = "https://files.pythonhosted.org/packages/38/d4/323438db76bded3a1f3523a3167b8325916b2ddceb2107a330c6ec9fcf4d/ast_serialize-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:db1b957291bca08c7e72f43a12357b2948e20775d970e3fc3dac0aa3160ab725", size = 1167072, upload-time = "2026-08-07T11:27:37.646Z" }, { url = "https://files.pythonhosted.org/packages/77/82/53c5400b54144b56de8ed7f957fd1ccd97e42482009292ab46121d15f8dd/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdc0d5b18ff8fb364e87923e47c0a91d0d69dbcaeaa274591f7fd26892cc3a3a", size = 1225497, upload-time = "2026-08-07T11:27:39.225Z" }, { url = "https://files.pythonhosted.org/packages/44/5f/36c07327a8b91303fbf1382c7c3e8a2902072dbe1b9546138a5288e75ff0/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9da7330f3e235bf7da89b8d39205c6350fc0c08a85379743f2df9fff87d6d980", size = 1227101, upload-time = "2026-08-07T11:27:40.799Z" }, { url = "https://files.pythonhosted.org/packages/9d/48/5adf5c67addc7ddb328122208c6d375a84cf154984f412b4087330a157bd/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3186969ee66a9863b00acc6523ace44c56974eecb348a7ea4b228d9f0b80e19", size = 1424001, upload-time = "2026-08-07T11:27:42.708Z" }, { url = "https://files.pythonhosted.org/packages/38/a1/70074dd3869d2b0e934f91891d8d6b734361cd3b80f85ca7ece2e668ecdd/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40a57b73731be45da4fa41430c4d5dc94a24b3a4faba7b9e069978c0402064ea", size = 1245545, upload-time = "2026-08-07T11:27:44.4Z" }, { url = "https://files.pythonhosted.org/packages/e3/be/53b9c0a8a6399950c2e3546bdfab96d2b299d5b114b47eb94fd3c49c4054/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5075b9da3ef807eda752502446dfecea3b381c4900b7e27a5d5f4f899eb39951", size = 1248961, upload-time = "2026-08-07T11:27:45.781Z" }, { url = "https://files.pythonhosted.org/packages/eb/13/3651d3812548a2bda15e26e5dd51aadb48cf682d0865370255fcf0e367dd/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:293cc1c5bfa741f8e3fbe8175b9c07beee487c9a6fdbb25a5acad9f1df2d30a9", size = 1243877, upload-time = "2026-08-07T11:27:47.325Z" }, { url = "https://files.pythonhosted.org/packages/21/a0/521f0bf000f675e9312a4aae2c8ba7a992405d072a85c485e08fd59433b9/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0910c3442a75216dde0f102d854ba2aaa71d2482e0ee213630b9bf29584fba3", size = 1293903, upload-time = "2026-08-07T11:27:49.264Z" }, { url = "https://files.pythonhosted.org/packages/b1/7e/402fc902568aa2ee65865a3e151f000db0153da8ce6b1be4c9c349025f8d/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43dd6d596879bb1cb8a12cc9dae7bb10090a39a35883026c24f82488a195619a", size = 1401070, upload-time = "2026-08-07T11:27:50.947Z" }, { url = "https://files.pythonhosted.org/packages/ff/7c/97d4b66c057f1706fc8be6dd532cc77c988794357c8f4ffdb6adabb39562/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8c9d537f59e936392cfd3597789d1390304dd659efc3c486ce7f40fb6b8a9f53", size = 1502602, upload-time = "2026-08-07T11:27:52.364Z" }, { url = "https://files.pythonhosted.org/packages/89/6f/72cc3b71562001bba46e898ccfbf1844f7939b3e28912736206102f2e5a8/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f0190a33d7f97c65e9069f7a7f40499eea6b5cbe260c558378109caf20ce934b", size = 1495848, upload-time = "2026-08-07T11:27:53.803Z" }, { url = "https://files.pythonhosted.org/packages/a0/53/d6f629d1e49308b2f363dae028baa213ec222c9106fa1f7f0d1f7b41499a/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:77308ae6c5cf5264cc0f01a7c556ec77a9e68eb1f61b093534d698139fdc3b14", size = 1556556, upload-time = "2026-08-07T11:27:55.342Z" }, { url = "https://files.pythonhosted.org/packages/ee/22/340f35dd8dfc6d412d53dc20699ca014b8d228db923e8ed4759c512b162c/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8d53a23f27e1ed3a36b2d26fd2a1a6228c8e85a1ed62ff7cdb44bd610769f20a", size = 1417822, upload-time = "2026-08-07T11:27:56.712Z" }, { url = "https://files.pythonhosted.org/packages/11/29/6dde5c13fbebc051d3a6df4ec0a6fd1d5359333cc1193f7f609f3410b4d8/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ffa5e7cb08f96fed9121f77b224151e41caf88feab9d652bb46c78202b6fbeda", size = 1445153, upload-time = "2026-08-07T11:27:58.275Z" }, { url = "https://files.pythonhosted.org/packages/62/c5/f473a8ed030f7a0ca24b9849cca184677a50c053867a7b808c2e1289bbd3/ast_serialize-0.8.0-cp314-cp314t-win32.whl", hash = "sha256:fa70ed4dea0bb18b30a1789c77baa701d0ef30c474f2ccabdea61e25623a8827", size = 1063711, upload-time = "2026-08-07T11:27:59.793Z" }, { url = "https://files.pythonhosted.org/packages/23/63/39e171fcd38ca057c2e1979d5ee81ac7a3502784abe3d83df7454f7a0978/ast_serialize-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d8b3c8eee4c1baef9d4e84d2a59a805501617127be42615cb48970b15b0892b6", size = 1103740, upload-time = "2026-08-07T11:28:01.405Z" }, { url = "https://files.pythonhosted.org/packages/21/1c/d00762b399e7726d68d0a088cc946e3a4c60f1c6176f557608f672f627f3/ast_serialize-0.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:ac4f0a83c55a9b782f79ad55a5247b7db123c1db405959791c2ef886e9710c9f", size = 1076021, upload-time = "2026-08-07T11:28:02.947Z" }, { url = "https://files.pythonhosted.org/packages/4c/11/911210c3c78923273a9211a2b6cfc4c8aa723b30dab3e1c8d19afb983b40/ast_serialize-0.8.0-cp315-abi3.abi3t-macosx_10_12_x86_64.whl", hash = "sha256:86b8a1e6d90467345356098b040150e82fbc26d24a7a202224b13dc1f6264ca0", size = 1177715, upload-time = "2026-08-07T11:28:04.654Z" }, { url = "https://files.pythonhosted.org/packages/77/89/6282881c8587606638db153cbe21e1e0c4d1f3970dee1aa0610a1c62a026/ast_serialize-0.8.0-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:39e92ff8e8cb45947fe9007174b2950e1fb098e6abd00266a13cd3bcf6675068", size = 1169347, upload-time = "2026-08-07T11:28:06.1Z" }, { url = "https://files.pythonhosted.org/packages/97/78/a9f846a03a340ff3728c915f23338ca742742f3292700559cdb3ad999b1e/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c85d8d18db5b2dfcb3b7e38a4d600ca35504c0ed8a6f75cd1c811e4ffe248a15", size = 1225916, upload-time = "2026-08-07T11:28:07.654Z" }, { url = "https://files.pythonhosted.org/packages/c0/15/aba6ef8a988a6eceb6f0359589aac509e29ae2dba67fd9bfd5af0c3f13e7/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9830ff7e764f74d9eefb01170c61a9f0fd2c027dac5fcb72e064decd57d56371", size = 1227135, upload-time = "2026-08-07T11:28:09.504Z" }, { url = "https://files.pythonhosted.org/packages/94/29/3f63d696ea7c5b8abadcecc3505be51bd900daaccc522ed8322fa5b05a93/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6479d9722a4cd21b578f5478074c41e6169f04811996ec881655560f703a5bba", size = 1425040, upload-time = "2026-08-07T11:28:11.044Z" }, { url = "https://files.pythonhosted.org/packages/e2/5d/0aac338604ff59df5774d4304307898982252f325ff7cafe31d52fedcb65/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a63bed264e818cd83eec11feed0f50aa162542b91132ef58afebc857182763a5", size = 1246278, upload-time = "2026-08-07T11:28:12.519Z" }, { url = "https://files.pythonhosted.org/packages/23/ca/9f1ef795bb724719532bd86dbec11e5b66857d3fbe9b6772baec0191a6ed/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d187197d234aa45d6cfa2b096be5f666e8cc2e7eb3722d0ab8926293cf5720c", size = 1250029, upload-time = "2026-08-07T11:28:13.896Z" }, { url = "https://files.pythonhosted.org/packages/dc/25/5e061372d2ed953b9ba3b9c4f73de3b8e9234cda3f6c088db4686801d0e1/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_31_riscv64.whl", hash = "sha256:2d39a56282cfcc0d8eeea37267c754be59c98d48505c23b1dae5c6011f3813dd", size = 1243575, upload-time = "2026-08-07T11:28:15.37Z" }, { url = "https://files.pythonhosted.org/packages/a8/c1/ae7da218053120635a4ca802366c69f707203641af95372eeb83f70dfd52/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f7cc5f10386994c0f4844f1e6d6a97127e9b478660eb6dec2b257644f0acab64", size = 1294396, upload-time = "2026-08-07T11:28:16.813Z" }, { url = "https://files.pythonhosted.org/packages/2e/89/271d1f49c5269fcddcc789ea3f25be401f6723fc1138aeda539f4d05516d/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6102f2f985c2e542be85cd857678ec9356fefa792b93cadfadd31139f5696f27", size = 1401987, upload-time = "2026-08-07T11:28:18.333Z" }, { url = "https://files.pythonhosted.org/packages/55/be/4e7d77fcf571ac7cb5cf7115a20c36642bd7d29473b45dfaaefeb9618f90/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_armv7l.whl", hash = "sha256:3a8660fe66667b76a6e9dccd1d33e66b229fde3b308db991c041609226c005b6", size = 1502904, upload-time = "2026-08-07T11:28:20.039Z" }, { url = "https://files.pythonhosted.org/packages/8b/ae/ed1de2db7e019d4236fbc164ffa5ef9a6022a300a342bbf142d21b7c141e/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_i686.whl", hash = "sha256:e7266307e5fba39836edb79def8608887af48820508bff3c5f2941e1e04d1534", size = 1496967, upload-time = "2026-08-07T11:28:21.734Z" }, { url = "https://files.pythonhosted.org/packages/92/89/5fea507fae5c5f18b7dc7f95e5c00956574b8c717b8fd2049c504fab0b18/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca7e6fd1ad845d1cc649dc2ecd499db2f8f46af5bf8da7b70dd858774cc038b", size = 1559041, upload-time = "2026-08-07T11:28:23.194Z" }, { url = "https://files.pythonhosted.org/packages/42/71/478d69df21b64e064554a68134c94be304270316ca676a94e63c389a636a/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_riscv64.whl", hash = "sha256:2880350b13d3eae69a0d70bc1fb6c9bfaca4dbd0e20ba8cd1aa483080b56ff06", size = 1417367, upload-time = "2026-08-07T11:28:24.601Z" }, { url = "https://files.pythonhosted.org/packages/5e/2d/8962dc8d5b3a9dc27b36f9db199afa25264c741505469d9ec10ffbfd2ba7/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:ab0f9a59f7d63d0d441b56b9a818b273705264352d5115cfee12e940e816d958", size = 1446178, upload-time = "2026-08-07T11:28:26.152Z" }, { url = "https://files.pythonhosted.org/packages/4f/22/14d2ad4fd1d1bcd0dc687ca268e0630069f45162496260c0efb70ee0ea72/ast_serialize-0.8.0-cp315-abi3.abi3t-win32.whl", hash = "sha256:0485a25ef519c62e749ee3c1ad8070e591b380d67226349eb5a70b228dc1ac4a", size = 1063811, upload-time = "2026-08-07T11:28:27.864Z" }, { url = "https://files.pythonhosted.org/packages/18/1d/84a327c0202a41aa5fdba3ade33904d6d8f3b9e6806fa83568d835395850/ast_serialize-0.8.0-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:bd84d60bca7079e741be4ac5dbe237751a59d7f6f9f0126b11880d63822cbe16", size = 1105518, upload-time = "2026-08-07T11:28:29.691Z" }, { url = "https://files.pythonhosted.org/packages/8c/92/74556dec52fde85a2ad84ed159991b916241043788609c15d8b77e14570b/ast_serialize-0.8.0-cp315-abi3.abi3t-win_arm64.whl", hash = "sha256:057769b5921336eb2d9124f2a731b42ed05ffdac559b840dbdf6f3937cf153dc", size = 1076319, upload-time = "2026-08-07T11:28:31.282Z" }, { url = "https://files.pythonhosted.org/packages/d1/5d/c650b1f2cc1e75193358da95a080261422e8cd10b66d7370b1688c9915c5/ast_serialize-0.8.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:a02cbed7d8bfdcdee88edaac12bd50d53d9953aaa2e1852ef078625be5f1c0b5", size = 852914, upload-time = "2026-08-07T11:28:32.929Z" }, { url = "https://files.pythonhosted.org/packages/d9/e3/6142e920fec6ef7bccabd8c24ed8ed99f8bdc6cb8b065e1df7c6a3b2d667/ast_serialize-0.8.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e1bd223df0f6c96b396975fa604cb33bce53d9b4a0185490be4c4a289f7c9c87", size = 1184007, upload-time = "2026-08-07T11:28:34.654Z" }, { url = "https://files.pythonhosted.org/packages/a6/e9/6e8be8df02b35d85e2b8809f7f1cfa290bdf5882b55127a539d049482db0/ast_serialize-0.8.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ddd3b61f45c132da66c5476b281891e08c1fd87fbdabe8a6973e1622efc85f06", size = 1177588, upload-time = "2026-08-07T11:28:36.318Z" }, { url = "https://files.pythonhosted.org/packages/8c/80/7e0fd2e2e2aba257820db4a8657c4c356844d36b914b20a4af294bcfb902/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f9caa63fad8241257ae401b5ff0a64026c6adb36b8e86cbe8782d9ea505daf6", size = 1234575, upload-time = "2026-08-07T11:28:37.772Z" }, { url = "https://files.pythonhosted.org/packages/b0/6a/3bae0af06f9b1bae3001c44d64215f5b567877e7aae9ffd45db11c3a7647/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3926fa117b5e65019853a2969966d11c7175af377a3425991f3fe73784412405", size = 1236015, upload-time = "2026-08-07T11:28:39.14Z" }, { url = "https://files.pythonhosted.org/packages/6f/c4/ce2d41a1bc22508e82618901f7e10f2a5e2f9556553fea90624daf9875e2/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:485f1113af805e9e170b95ef993ca3fbd4f89c04bab25c58b4fc632d854801ab", size = 1432808, upload-time = "2026-08-07T11:28:40.664Z" }, { url = "https://files.pythonhosted.org/packages/1a/90/f5058f209756dd70e958b7538aaa82d25d24944baf9ec8ae6f27b06fcacc/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccebbed24f1281062d5852353c72c47502955926cfcb8345ffb3a44d87ff3d3", size = 1256251, upload-time = "2026-08-07T11:28:42.223Z" }, { url = "https://files.pythonhosted.org/packages/bf/32/7f77ea87fa0836daab706ed5cb7f903bb25fa26a77439011aee626af11d8/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:252f883290d1cdb728eb7fe1d9a7221b88af5a329aae0bc91ddee4dafb820331", size = 1258574, upload-time = "2026-08-07T11:28:43.751Z" }, { url = "https://files.pythonhosted.org/packages/eb/5a/75b82ad2725b5e8e8c742732f9e76c6738a292d0709e1f60d10a973730b4/ast_serialize-0.8.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:96abc072ad29db8d02194afd47d68987322622787daceae82398d7b69f3ba2e6", size = 1254075, upload-time = "2026-08-07T11:28:45.28Z" }, { url = "https://files.pythonhosted.org/packages/4e/54/8c20ed4eea805516a3fd23dd4a721ce28c64f50f0e4b359969f60a8c97a6/ast_serialize-0.8.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9118ad3e369727060b2696fc4078f250ecffca4248ba87f537f55cea9f9dce06", size = 1301018, upload-time = "2026-08-07T11:28:46.851Z" }, { url = "https://files.pythonhosted.org/packages/cb/5b/9f14430f12fe830b656fb38f8e2e05ee13b02a88967660bef46af0ab22a8/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f359df4bd921918af8bebd142a376c77511d7151cc8ba852760b587b5a4a54f3", size = 1409951, upload-time = "2026-08-07T11:28:48.312Z" }, { url = "https://files.pythonhosted.org/packages/2d/3d/084882eca93c842bd4262591a071ec7f825340644035e51501208cc5a8d4/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:e94f9121d13fa36cbf21314783c77d05ae3a0868decd18cf5233fdcc6de49ac8", size = 1509544, upload-time = "2026-08-07T11:28:49.847Z" }, { url = "https://files.pythonhosted.org/packages/ce/73/ea84852096c2036c61cc0b2f97b90242207419f534dc671060ee1c8e05cb/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:54f95b486018d262bcb387a9afd96f0da74508b442762b80c769454a6fbb3ee3", size = 1505671, upload-time = "2026-08-07T11:28:51.239Z" }, { url = "https://files.pythonhosted.org/packages/cb/88/287b9a5300c1f2f651d259f670931b63110adc265b7613c885b44c5bc53d/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4c38b915511e32bc718c49dbce98ff9af36bac0ad6a604f58000cd5e3aecdba7", size = 1563685, upload-time = "2026-08-07T11:28:53.112Z" }, { url = "https://files.pythonhosted.org/packages/ee/f3/1bc3a79afcf0c2a8d2c37182d0d659d1545a9d7f7f6dc9cf3e63d6c17135/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:9a2ef9cf12f2de4f1028c42c1dd7d775255e0fb3e5bb48896c97e35ef52366fe", size = 1427977, upload-time = "2026-08-07T11:28:54.418Z" }, { url = "https://files.pythonhosted.org/packages/5c/cd/440c798957e14e31776bfeb024d8fafe0bb1d5b89c51c2f067e69938f7b0/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f18048fe9f6dd266bd577cdec48bdcecb74faaa01fe941324435483b013ed2a", size = 1454335, upload-time = "2026-08-07T11:28:55.968Z" }, { url = "https://files.pythonhosted.org/packages/4f/4a/587eb36dcc240a54c8660f599464516b469ecad96f0dbdb6bccbedb50745/ast_serialize-0.8.0-cp39-abi3-win32.whl", hash = "sha256:31883542dd6c94d178f5db3d32fbd69c5eb88b3a7c018e7ac8cc0c45195ddbed", size = 1068858, upload-time = "2026-08-07T11:28:57.541Z" }, { url = "https://files.pythonhosted.org/packages/5f/a4/3e887bbd92164e183cb6e412c6a3e9198ddd446d7fe405958293ef5ef49c/ast_serialize-0.8.0-cp39-abi3-win_amd64.whl", hash = "sha256:861794565b06337005c1447ef23103a3d5a627d08bdc827870d00d0b28ef5f51", size = 1111839, upload-time = "2026-08-07T11:28:59Z" }, { url = "https://files.pythonhosted.org/packages/25/6c/b400476d3ceba681ab929787edc9554f6d88fcc69435eb681b00fc0457a5/ast_serialize-0.8.0-cp39-abi3-win_arm64.whl", hash = "sha256:b2a5978662fd4db463dfb4b974d2b10ac6430b98f5333aabc7051909df3561d0", size = 1083655, upload-time = "2026-08-07T11:29:00.349Z" }, ] [[package]] name = "attrs" version = "24.3.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/48/c8/6260f8ccc11f0917360fc0da435c5c9c7504e3db174d5a12a1494887b045/attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", size = 805984, upload-time = "2024-12-16T06:59:29.899Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/89/aa/ab0f7891a01eeb2d2e338ae8fecbe57fcebea1a24dbb64d45801bfab481d/attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308", size = 63397, upload-time = "2024-12-16T06:59:26.977Z" }, ] [[package]] name = "babel" version = "2.18.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, ] [[package]] name = "backports-tarfile" version = "1.2.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", size = 86406, upload-time = "2024-05-28T17:01:54.731Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181, upload-time = "2024-05-28T17:01:53.112Z" }, ] [[package]] name = "bandit" version = "1.8.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "pyyaml", version = "6.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, { name = "pyyaml", version = "6.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "rich" }, { name = "stevedore" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fb/b5/7eb834e213d6f73aace21938e5e90425c92e5f42abafaf8a6d5d21beed51/bandit-1.8.6.tar.gz", hash = "sha256:dbfe9c25fc6961c2078593de55fd19f2559f9e45b99f1272341f5b95dea4e56b", size = 4240271, upload-time = "2025-07-06T03:10:50.9Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/48/ca/ba5f909b40ea12ec542d5d7bdd13ee31c4d65f3beed20211ef81c18fa1f3/bandit-1.8.6-py3-none-any.whl", hash = "sha256:3348e934d736fcdb68b6aa4030487097e23a501adf3e7827b63658df464dddd0", size = 133808, upload-time = "2025-07-06T03:10:49.134Z" }, ] [[package]] name = "black" version = "26.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "mypy-extensions" }, { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, { name = "pytokens" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e1/c5/61175d618685d42b005847464b8fb4743a67b1b8fdb75e50e5a96c31a27a/black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07", size = 666155, upload-time = "2026-03-12T03:36:03.593Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/32/a8/11170031095655d36ebc6664fe0897866f6023892396900eec0e8fdc4299/black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2", size = 1866562, upload-time = "2026-03-12T03:39:58.639Z" }, { url = "https://files.pythonhosted.org/packages/69/ce/9e7548d719c3248c6c2abfd555d11169457cbd584d98d179111338423790/black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b", size = 1703623, upload-time = "2026-03-12T03:40:00.347Z" }, { url = "https://files.pythonhosted.org/packages/7f/0a/8d17d1a9c06f88d3d030d0b1d4373c1551146e252afe4547ed601c0e697f/black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac", size = 1768388, upload-time = "2026-03-12T03:40:01.765Z" }, { url = "https://files.pythonhosted.org/packages/52/79/c1ee726e221c863cde5164f925bacf183dfdf0397d4e3f94889439b947b4/black-26.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a", size = 1412969, upload-time = "2026-03-12T03:40:03.252Z" }, { url = "https://files.pythonhosted.org/packages/73/a5/15c01d613f5756f68ed8f6d4ec0a1e24b82b18889fa71affd3d1f7fad058/black-26.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a", size = 1220345, upload-time = "2026-03-12T03:40:04.892Z" }, { url = "https://files.pythonhosted.org/packages/17/57/5f11c92861f9c92eb9dddf515530bc2d06db843e44bdcf1c83c1427824bc/black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff", size = 1851987, upload-time = "2026-03-12T03:40:06.248Z" }, { url = "https://files.pythonhosted.org/packages/54/aa/340a1463660bf6831f9e39646bf774086dbd8ca7fc3cded9d59bbdf4ad0a/black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c", size = 1689499, upload-time = "2026-03-12T03:40:07.642Z" }, { url = "https://files.pythonhosted.org/packages/f3/01/b726c93d717d72733da031d2de10b92c9fa4c8d0c67e8a8a372076579279/black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5", size = 1754369, upload-time = "2026-03-12T03:40:09.279Z" }, { url = "https://files.pythonhosted.org/packages/e3/09/61e91881ca291f150cfc9eb7ba19473c2e59df28859a11a88248b5cbbc4d/black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e", size = 1413613, upload-time = "2026-03-12T03:40:10.943Z" }, { url = "https://files.pythonhosted.org/packages/16/73/544f23891b22e7efe4d8f812371ab85b57f6a01b2fc45e3ba2e52ba985b8/black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5", size = 1219719, upload-time = "2026-03-12T03:40:12.597Z" }, { url = "https://files.pythonhosted.org/packages/dc/f8/da5eae4fc75e78e6dceb60624e1b9662ab00d6b452996046dfa9b8a6025b/black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1", size = 1895920, upload-time = "2026-03-12T03:40:13.921Z" }, { url = "https://files.pythonhosted.org/packages/2c/9f/04e6f26534da2e1629b2b48255c264cabf5eedc5141d04516d9d68a24111/black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f", size = 1718499, upload-time = "2026-03-12T03:40:15.239Z" }, { url = "https://files.pythonhosted.org/packages/04/91/a5935b2a63e31b331060c4a9fdb5a6c725840858c599032a6f3aac94055f/black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7", size = 1794994, upload-time = "2026-03-12T03:40:17.124Z" }, { url = "https://files.pythonhosted.org/packages/e7/0a/86e462cdd311a3c2a8ece708d22aba17d0b2a0d5348ca34b40cdcbea512e/black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983", size = 1420867, upload-time = "2026-03-12T03:40:18.83Z" }, { url = "https://files.pythonhosted.org/packages/5b/e5/22515a19cb7eaee3440325a6b0d95d2c0e88dd180cb011b12ae488e031d1/black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb", size = 1230124, upload-time = "2026-03-12T03:40:20.425Z" }, { url = "https://files.pythonhosted.org/packages/f5/77/5728052a3c0450c53d9bb3945c4c46b91baa62b2cafab6801411b6271e45/black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54", size = 1895034, upload-time = "2026-03-12T03:40:21.813Z" }, { url = "https://files.pythonhosted.org/packages/52/73/7cae55fdfdfbe9d19e9a8d25d145018965fe2079fa908101c3733b0c55a0/black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f", size = 1718503, upload-time = "2026-03-12T03:40:23.666Z" }, { url = "https://files.pythonhosted.org/packages/e1/87/af89ad449e8254fdbc74654e6467e3c9381b61472cc532ee350d28cfdafb/black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56", size = 1793557, upload-time = "2026-03-12T03:40:25.497Z" }, { url = "https://files.pythonhosted.org/packages/43/10/d6c06a791d8124b843bf325ab4ac7d2f5b98731dff84d6064eafd687ded1/black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839", size = 1422766, upload-time = "2026-03-12T03:40:27.14Z" }, { url = "https://files.pythonhosted.org/packages/59/4f/40a582c015f2d841ac24fed6390bd68f0fc896069ff3a886317959c9daf8/black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2", size = 1232140, upload-time = "2026-03-12T03:40:28.882Z" }, { url = "https://files.pythonhosted.org/packages/d5/da/e36e27c9cebc1311b7579210df6f1c86e50f2d7143ae4fcf8a5017dc8809/black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78", size = 1889234, upload-time = "2026-03-12T03:40:30.964Z" }, { url = "https://files.pythonhosted.org/packages/0e/7b/9871acf393f64a5fa33668c19350ca87177b181f44bb3d0c33b2d534f22c/black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568", size = 1720522, upload-time = "2026-03-12T03:40:32.346Z" }, { url = "https://files.pythonhosted.org/packages/03/87/e766c7f2e90c07fb7586cc787c9ae6462b1eedab390191f2b7fc7f6170a9/black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f", size = 1787824, upload-time = "2026-03-12T03:40:33.636Z" }, { url = "https://files.pythonhosted.org/packages/ac/94/2424338fb2d1875e9e83eed4c8e9c67f6905ec25afd826a911aea2b02535/black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c", size = 1445855, upload-time = "2026-03-12T03:40:35.442Z" }, { url = "https://files.pythonhosted.org/packages/86/43/0c3338bd928afb8ee7471f1a4eec3bdbe2245ccb4a646092a222e8669840/black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1", size = 1258109, upload-time = "2026-03-12T03:40:36.832Z" }, { url = "https://files.pythonhosted.org/packages/8e/0d/52d98722666d6fc6c3dd4c76df339501d6efd40e0ff95e6186a7b7f0befd/black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b", size = 207542, upload-time = "2026-03-12T03:36:01.668Z" }, ] [[package]] name = "cachetools" version = "6.2.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/9d/61/e4fad8155db4a04bfb4734c7c8ff0882f078f24294d42798b3568eb63bff/cachetools-6.2.0.tar.gz", hash = "sha256:38b328c0889450f05f5e120f56ab68c8abaf424e1275522b138ffc93253f7e32", size = 30988, upload-time = "2025-08-25T18:57:30.924Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/6c/56/3124f61d37a7a4e7cc96afc5492c78ba0cb551151e530b54669ddd1436ef/cachetools-6.2.0-py3-none-any.whl", hash = "sha256:1c76a8960c0041fcc21097e357f882197c79da0dbff766e7317890a65d7d8ba6", size = 11276, upload-time = "2025-08-25T18:57:29.684Z" }, ] [[package]] name = "certifi" version = "2024.12.14" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010, upload-time = "2024-12-14T13:52:38.02Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927, upload-time = "2024-12-14T13:52:36.114Z" }, ] [[package]] name = "cffi" version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, ] [[package]] name = "chardet" version = "5.2.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618, upload-time = "2023-08-01T19:23:02.662Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385, upload-time = "2023-08-01T19:23:00.661Z" }, ] [[package]] name = "charset-normalizer" version = "3.4.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload-time = "2024-12-24T18:12:35.43Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013, upload-time = "2024-12-24T18:09:43.671Z" }, { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285, upload-time = "2024-12-24T18:09:48.113Z" }, { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449, upload-time = "2024-12-24T18:09:50.845Z" }, { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892, upload-time = "2024-12-24T18:09:52.078Z" }, { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123, upload-time = "2024-12-24T18:09:54.575Z" }, { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943, upload-time = "2024-12-24T18:09:57.324Z" }, { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063, upload-time = "2024-12-24T18:09:59.794Z" }, { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578, upload-time = "2024-12-24T18:10:02.357Z" }, { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629, upload-time = "2024-12-24T18:10:03.678Z" }, { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778, upload-time = "2024-12-24T18:10:06.197Z" }, { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453, upload-time = "2024-12-24T18:10:08.848Z" }, { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479, upload-time = "2024-12-24T18:10:10.044Z" }, { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790, upload-time = "2024-12-24T18:10:11.323Z" }, { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995, upload-time = "2024-12-24T18:10:12.838Z" }, { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471, upload-time = "2024-12-24T18:10:14.101Z" }, { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831, upload-time = "2024-12-24T18:10:15.512Z" }, { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335, upload-time = "2024-12-24T18:10:18.369Z" }, { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862, upload-time = "2024-12-24T18:10:19.743Z" }, { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673, upload-time = "2024-12-24T18:10:21.139Z" }, { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211, upload-time = "2024-12-24T18:10:22.382Z" }, { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039, upload-time = "2024-12-24T18:10:24.802Z" }, { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939, upload-time = "2024-12-24T18:10:26.124Z" }, { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075, upload-time = "2024-12-24T18:10:30.027Z" }, { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340, upload-time = "2024-12-24T18:10:32.679Z" }, { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205, upload-time = "2024-12-24T18:10:34.724Z" }, { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441, upload-time = "2024-12-24T18:10:37.574Z" }, { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105, upload-time = "2024-12-24T18:10:38.83Z" }, { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404, upload-time = "2024-12-24T18:10:44.272Z" }, { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423, upload-time = "2024-12-24T18:10:45.492Z" }, { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184, upload-time = "2024-12-24T18:10:47.898Z" }, { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268, upload-time = "2024-12-24T18:10:50.589Z" }, { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601, upload-time = "2024-12-24T18:10:52.541Z" }, { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098, upload-time = "2024-12-24T18:10:53.789Z" }, { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520, upload-time = "2024-12-24T18:10:55.048Z" }, { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852, upload-time = "2024-12-24T18:10:57.647Z" }, { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488, upload-time = "2024-12-24T18:10:59.43Z" }, { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192, upload-time = "2024-12-24T18:11:00.676Z" }, { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550, upload-time = "2024-12-24T18:11:01.952Z" }, { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785, upload-time = "2024-12-24T18:11:03.142Z" }, { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698, upload-time = "2024-12-24T18:11:05.834Z" }, { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162, upload-time = "2024-12-24T18:11:07.064Z" }, { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263, upload-time = "2024-12-24T18:11:08.374Z" }, { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966, upload-time = "2024-12-24T18:11:09.831Z" }, { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992, upload-time = "2024-12-24T18:11:12.03Z" }, { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162, upload-time = "2024-12-24T18:11:13.372Z" }, { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972, upload-time = "2024-12-24T18:11:14.628Z" }, { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095, upload-time = "2024-12-24T18:11:17.672Z" }, { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668, upload-time = "2024-12-24T18:11:18.989Z" }, { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073, upload-time = "2024-12-24T18:11:21.507Z" }, { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732, upload-time = "2024-12-24T18:11:22.774Z" }, { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391, upload-time = "2024-12-24T18:11:24.139Z" }, { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702, upload-time = "2024-12-24T18:11:26.535Z" }, { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload-time = "2024-12-24T18:12:32.852Z" }, ] [[package]] name = "click" version = "8.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] name = "coverage" version = "7.10.7" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/51/26/d22c300112504f5f9a9fd2297ce33c35f3d353e4aeb987c8419453b2a7c2/coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239", size = 827704, upload-time = "2025-09-21T20:03:56.815Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e5/6c/3a3f7a46888e69d18abe3ccc6fe4cb16cccb1e6a2f99698931dafca489e6/coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a", size = 217987, upload-time = "2025-09-21T20:00:57.218Z" }, { url = "https://files.pythonhosted.org/packages/03/94/952d30f180b1a916c11a56f5c22d3535e943aa22430e9e3322447e520e1c/coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5", size = 218388, upload-time = "2025-09-21T20:01:00.081Z" }, { url = "https://files.pythonhosted.org/packages/50/2b/9e0cf8ded1e114bcd8b2fd42792b57f1c4e9e4ea1824cde2af93a67305be/coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17", size = 245148, upload-time = "2025-09-21T20:01:01.768Z" }, { url = "https://files.pythonhosted.org/packages/19/20/d0384ac06a6f908783d9b6aa6135e41b093971499ec488e47279f5b846e6/coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b", size = 246958, upload-time = "2025-09-21T20:01:03.355Z" }, { url = "https://files.pythonhosted.org/packages/60/83/5c283cff3d41285f8eab897651585db908a909c572bdc014bcfaf8a8b6ae/coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87", size = 248819, upload-time = "2025-09-21T20:01:04.968Z" }, { url = "https://files.pythonhosted.org/packages/60/22/02eb98fdc5ff79f423e990d877693e5310ae1eab6cb20ae0b0b9ac45b23b/coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e", size = 245754, upload-time = "2025-09-21T20:01:06.321Z" }, { url = "https://files.pythonhosted.org/packages/b4/bc/25c83bcf3ad141b32cd7dc45485ef3c01a776ca3aa8ef0a93e77e8b5bc43/coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e", size = 246860, upload-time = "2025-09-21T20:01:07.605Z" }, { url = "https://files.pythonhosted.org/packages/3c/b7/95574702888b58c0928a6e982038c596f9c34d52c5e5107f1eef729399b5/coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df", size = 244877, upload-time = "2025-09-21T20:01:08.829Z" }, { url = "https://files.pythonhosted.org/packages/47/b6/40095c185f235e085df0e0b158f6bd68cc6e1d80ba6c7721dc81d97ec318/coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0", size = 245108, upload-time = "2025-09-21T20:01:10.527Z" }, { url = "https://files.pythonhosted.org/packages/c8/50/4aea0556da7a4b93ec9168420d170b55e2eb50ae21b25062513d020c6861/coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13", size = 245752, upload-time = "2025-09-21T20:01:11.857Z" }, { url = "https://files.pythonhosted.org/packages/6a/28/ea1a84a60828177ae3b100cb6723838523369a44ec5742313ed7db3da160/coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b", size = 220497, upload-time = "2025-09-21T20:01:13.459Z" }, { url = "https://files.pythonhosted.org/packages/fc/1a/a81d46bbeb3c3fd97b9602ebaa411e076219a150489bcc2c025f151bd52d/coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807", size = 221392, upload-time = "2025-09-21T20:01:14.722Z" }, { url = "https://files.pythonhosted.org/packages/d2/5d/c1a17867b0456f2e9ce2d8d4708a4c3a089947d0bec9c66cdf60c9e7739f/coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59", size = 218102, upload-time = "2025-09-21T20:01:16.089Z" }, { url = "https://files.pythonhosted.org/packages/54/f0/514dcf4b4e3698b9a9077f084429681bf3aad2b4a72578f89d7f643eb506/coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a", size = 218505, upload-time = "2025-09-21T20:01:17.788Z" }, { url = "https://files.pythonhosted.org/packages/20/f6/9626b81d17e2a4b25c63ac1b425ff307ecdeef03d67c9a147673ae40dc36/coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699", size = 248898, upload-time = "2025-09-21T20:01:19.488Z" }, { url = "https://files.pythonhosted.org/packages/b0/ef/bd8e719c2f7417ba03239052e099b76ea1130ac0cbb183ee1fcaa58aaff3/coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d", size = 250831, upload-time = "2025-09-21T20:01:20.817Z" }, { url = "https://files.pythonhosted.org/packages/a5/b6/bf054de41ec948b151ae2b79a55c107f5760979538f5fb80c195f2517718/coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e", size = 252937, upload-time = "2025-09-21T20:01:22.171Z" }, { url = "https://files.pythonhosted.org/packages/0f/e5/3860756aa6f9318227443c6ce4ed7bf9e70bb7f1447a0353f45ac5c7974b/coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23", size = 249021, upload-time = "2025-09-21T20:01:23.907Z" }, { url = "https://files.pythonhosted.org/packages/26/0f/bd08bd042854f7fd07b45808927ebcce99a7ed0f2f412d11629883517ac2/coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab", size = 250626, upload-time = "2025-09-21T20:01:25.721Z" }, { url = "https://files.pythonhosted.org/packages/8e/a7/4777b14de4abcc2e80c6b1d430f5d51eb18ed1d75fca56cbce5f2db9b36e/coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82", size = 248682, upload-time = "2025-09-21T20:01:27.105Z" }, { url = "https://files.pythonhosted.org/packages/34/72/17d082b00b53cd45679bad682fac058b87f011fd8b9fe31d77f5f8d3a4e4/coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2", size = 248402, upload-time = "2025-09-21T20:01:28.629Z" }, { url = "https://files.pythonhosted.org/packages/81/7a/92367572eb5bdd6a84bfa278cc7e97db192f9f45b28c94a9ca1a921c3577/coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61", size = 249320, upload-time = "2025-09-21T20:01:30.004Z" }, { url = "https://files.pythonhosted.org/packages/2f/88/a23cc185f6a805dfc4fdf14a94016835eeb85e22ac3a0e66d5e89acd6462/coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14", size = 220536, upload-time = "2025-09-21T20:01:32.184Z" }, { url = "https://files.pythonhosted.org/packages/fe/ef/0b510a399dfca17cec7bc2f05ad8bd78cf55f15c8bc9a73ab20c5c913c2e/coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2", size = 221425, upload-time = "2025-09-21T20:01:33.557Z" }, { url = "https://files.pythonhosted.org/packages/51/7f/023657f301a276e4ba1850f82749bc136f5a7e8768060c2e5d9744a22951/coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a", size = 220103, upload-time = "2025-09-21T20:01:34.929Z" }, { url = "https://files.pythonhosted.org/packages/13/e4/eb12450f71b542a53972d19117ea5a5cea1cab3ac9e31b0b5d498df1bd5a/coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417", size = 218290, upload-time = "2025-09-21T20:01:36.455Z" }, { url = "https://files.pythonhosted.org/packages/37/66/593f9be12fc19fb36711f19a5371af79a718537204d16ea1d36f16bd78d2/coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973", size = 218515, upload-time = "2025-09-21T20:01:37.982Z" }, { url = "https://files.pythonhosted.org/packages/66/80/4c49f7ae09cafdacc73fbc30949ffe77359635c168f4e9ff33c9ebb07838/coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c", size = 250020, upload-time = "2025-09-21T20:01:39.617Z" }, { url = "https://files.pythonhosted.org/packages/a6/90/a64aaacab3b37a17aaedd83e8000142561a29eb262cede42d94a67f7556b/coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7", size = 252769, upload-time = "2025-09-21T20:01:41.341Z" }, { url = "https://files.pythonhosted.org/packages/98/2e/2dda59afd6103b342e096f246ebc5f87a3363b5412609946c120f4e7750d/coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6", size = 253901, upload-time = "2025-09-21T20:01:43.042Z" }, { url = "https://files.pythonhosted.org/packages/53/dc/8d8119c9051d50f3119bb4a75f29f1e4a6ab9415cd1fa8bf22fcc3fb3b5f/coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59", size = 250413, upload-time = "2025-09-21T20:01:44.469Z" }, { url = "https://files.pythonhosted.org/packages/98/b3/edaff9c5d79ee4d4b6d3fe046f2b1d799850425695b789d491a64225d493/coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b", size = 251820, upload-time = "2025-09-21T20:01:45.915Z" }, { url = "https://files.pythonhosted.org/packages/11/25/9a0728564bb05863f7e513e5a594fe5ffef091b325437f5430e8cfb0d530/coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a", size = 249941, upload-time = "2025-09-21T20:01:47.296Z" }, { url = "https://files.pythonhosted.org/packages/e0/fd/ca2650443bfbef5b0e74373aac4df67b08180d2f184b482c41499668e258/coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb", size = 249519, upload-time = "2025-09-21T20:01:48.73Z" }, { url = "https://files.pythonhosted.org/packages/24/79/f692f125fb4299b6f963b0745124998ebb8e73ecdfce4ceceb06a8c6bec5/coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1", size = 251375, upload-time = "2025-09-21T20:01:50.529Z" }, { url = "https://files.pythonhosted.org/packages/5e/75/61b9bbd6c7d24d896bfeec57acba78e0f8deac68e6baf2d4804f7aae1f88/coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256", size = 220699, upload-time = "2025-09-21T20:01:51.941Z" }, { url = "https://files.pythonhosted.org/packages/ca/f3/3bf7905288b45b075918d372498f1cf845b5b579b723c8fd17168018d5f5/coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba", size = 221512, upload-time = "2025-09-21T20:01:53.481Z" }, { url = "https://files.pythonhosted.org/packages/5c/44/3e32dbe933979d05cf2dac5e697c8599cfe038aaf51223ab901e208d5a62/coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf", size = 220147, upload-time = "2025-09-21T20:01:55.2Z" }, { url = "https://files.pythonhosted.org/packages/9a/94/b765c1abcb613d103b64fcf10395f54d69b0ef8be6a0dd9c524384892cc7/coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d", size = 218320, upload-time = "2025-09-21T20:01:56.629Z" }, { url = "https://files.pythonhosted.org/packages/72/4f/732fff31c119bb73b35236dd333030f32c4bfe909f445b423e6c7594f9a2/coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b", size = 218575, upload-time = "2025-09-21T20:01:58.203Z" }, { url = "https://files.pythonhosted.org/packages/87/02/ae7e0af4b674be47566707777db1aa375474f02a1d64b9323e5813a6cdd5/coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e", size = 249568, upload-time = "2025-09-21T20:01:59.748Z" }, { url = "https://files.pythonhosted.org/packages/a2/77/8c6d22bf61921a59bce5471c2f1f7ac30cd4ac50aadde72b8c48d5727902/coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b", size = 252174, upload-time = "2025-09-21T20:02:01.192Z" }, { url = "https://files.pythonhosted.org/packages/b1/20/b6ea4f69bbb52dac0aebd62157ba6a9dddbfe664f5af8122dac296c3ee15/coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49", size = 253447, upload-time = "2025-09-21T20:02:02.701Z" }, { url = "https://files.pythonhosted.org/packages/f9/28/4831523ba483a7f90f7b259d2018fef02cb4d5b90bc7c1505d6e5a84883c/coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911", size = 249779, upload-time = "2025-09-21T20:02:04.185Z" }, { url = "https://files.pythonhosted.org/packages/a7/9f/4331142bc98c10ca6436d2d620c3e165f31e6c58d43479985afce6f3191c/coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0", size = 251604, upload-time = "2025-09-21T20:02:06.034Z" }, { url = "https://files.pythonhosted.org/packages/ce/60/bda83b96602036b77ecf34e6393a3836365481b69f7ed7079ab85048202b/coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f", size = 249497, upload-time = "2025-09-21T20:02:07.619Z" }, { url = "https://files.pythonhosted.org/packages/5f/af/152633ff35b2af63977edd835d8e6430f0caef27d171edf2fc76c270ef31/coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c", size = 249350, upload-time = "2025-09-21T20:02:10.34Z" }, { url = "https://files.pythonhosted.org/packages/9d/71/d92105d122bd21cebba877228990e1646d862e34a98bb3374d3fece5a794/coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f", size = 251111, upload-time = "2025-09-21T20:02:12.122Z" }, { url = "https://files.pythonhosted.org/packages/a2/9e/9fdb08f4bf476c912f0c3ca292e019aab6712c93c9344a1653986c3fd305/coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698", size = 220746, upload-time = "2025-09-21T20:02:13.919Z" }, { url = "https://files.pythonhosted.org/packages/b1/b1/a75fd25df44eab52d1931e89980d1ada46824c7a3210be0d3c88a44aaa99/coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843", size = 221541, upload-time = "2025-09-21T20:02:15.57Z" }, { url = "https://files.pythonhosted.org/packages/14/3a/d720d7c989562a6e9a14b2c9f5f2876bdb38e9367126d118495b89c99c37/coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546", size = 220170, upload-time = "2025-09-21T20:02:17.395Z" }, { url = "https://files.pythonhosted.org/packages/bb/22/e04514bf2a735d8b0add31d2b4ab636fc02370730787c576bb995390d2d5/coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c", size = 219029, upload-time = "2025-09-21T20:02:18.936Z" }, { url = "https://files.pythonhosted.org/packages/11/0b/91128e099035ece15da3445d9015e4b4153a6059403452d324cbb0a575fa/coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15", size = 219259, upload-time = "2025-09-21T20:02:20.44Z" }, { url = "https://files.pythonhosted.org/packages/8b/51/66420081e72801536a091a0c8f8c1f88a5c4bf7b9b1bdc6222c7afe6dc9b/coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4", size = 260592, upload-time = "2025-09-21T20:02:22.313Z" }, { url = "https://files.pythonhosted.org/packages/5d/22/9b8d458c2881b22df3db5bb3e7369e63d527d986decb6c11a591ba2364f7/coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0", size = 262768, upload-time = "2025-09-21T20:02:24.287Z" }, { url = "https://files.pythonhosted.org/packages/f7/08/16bee2c433e60913c610ea200b276e8eeef084b0d200bdcff69920bd5828/coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0", size = 264995, upload-time = "2025-09-21T20:02:26.133Z" }, { url = "https://files.pythonhosted.org/packages/20/9d/e53eb9771d154859b084b90201e5221bca7674ba449a17c101a5031d4054/coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65", size = 259546, upload-time = "2025-09-21T20:02:27.716Z" }, { url = "https://files.pythonhosted.org/packages/ad/b0/69bc7050f8d4e56a89fb550a1577d5d0d1db2278106f6f626464067b3817/coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541", size = 262544, upload-time = "2025-09-21T20:02:29.216Z" }, { url = "https://files.pythonhosted.org/packages/ef/4b/2514b060dbd1bc0aaf23b852c14bb5818f244c664cb16517feff6bb3a5ab/coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6", size = 260308, upload-time = "2025-09-21T20:02:31.226Z" }, { url = "https://files.pythonhosted.org/packages/54/78/7ba2175007c246d75e496f64c06e94122bdb914790a1285d627a918bd271/coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999", size = 258920, upload-time = "2025-09-21T20:02:32.823Z" }, { url = "https://files.pythonhosted.org/packages/c0/b3/fac9f7abbc841409b9a410309d73bfa6cfb2e51c3fada738cb607ce174f8/coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2", size = 261434, upload-time = "2025-09-21T20:02:34.86Z" }, { url = "https://files.pythonhosted.org/packages/ee/51/a03bec00d37faaa891b3ff7387192cef20f01604e5283a5fabc95346befa/coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a", size = 221403, upload-time = "2025-09-21T20:02:37.034Z" }, { url = "https://files.pythonhosted.org/packages/53/22/3cf25d614e64bf6d8e59c7c669b20d6d940bb337bdee5900b9ca41c820bb/coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb", size = 222469, upload-time = "2025-09-21T20:02:39.011Z" }, { url = "https://files.pythonhosted.org/packages/49/a1/00164f6d30d8a01c3c9c48418a7a5be394de5349b421b9ee019f380df2a0/coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb", size = 220731, upload-time = "2025-09-21T20:02:40.939Z" }, { url = "https://files.pythonhosted.org/packages/23/9c/5844ab4ca6a4dd97a1850e030a15ec7d292b5c5cb93082979225126e35dd/coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520", size = 218302, upload-time = "2025-09-21T20:02:42.527Z" }, { url = "https://files.pythonhosted.org/packages/f0/89/673f6514b0961d1f0e20ddc242e9342f6da21eaba3489901b565c0689f34/coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32", size = 218578, upload-time = "2025-09-21T20:02:44.468Z" }, { url = "https://files.pythonhosted.org/packages/05/e8/261cae479e85232828fb17ad536765c88dd818c8470aca690b0ac6feeaa3/coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f", size = 249629, upload-time = "2025-09-21T20:02:46.503Z" }, { url = "https://files.pythonhosted.org/packages/82/62/14ed6546d0207e6eda876434e3e8475a3e9adbe32110ce896c9e0c06bb9a/coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a", size = 252162, upload-time = "2025-09-21T20:02:48.689Z" }, { url = "https://files.pythonhosted.org/packages/ff/49/07f00db9ac6478e4358165a08fb41b469a1b053212e8a00cb02f0d27a05f/coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360", size = 253517, upload-time = "2025-09-21T20:02:50.31Z" }, { url = "https://files.pythonhosted.org/packages/a2/59/c5201c62dbf165dfbc91460f6dbbaa85a8b82cfa6131ac45d6c1bfb52deb/coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69", size = 249632, upload-time = "2025-09-21T20:02:51.971Z" }, { url = "https://files.pythonhosted.org/packages/07/ae/5920097195291a51fb00b3a70b9bbd2edbfe3c84876a1762bd1ef1565ebc/coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14", size = 251520, upload-time = "2025-09-21T20:02:53.858Z" }, { url = "https://files.pythonhosted.org/packages/b9/3c/a815dde77a2981f5743a60b63df31cb322c944843e57dbd579326625a413/coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe", size = 249455, upload-time = "2025-09-21T20:02:55.807Z" }, { url = "https://files.pythonhosted.org/packages/aa/99/f5cdd8421ea656abefb6c0ce92556709db2265c41e8f9fc6c8ae0f7824c9/coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e", size = 249287, upload-time = "2025-09-21T20:02:57.784Z" }, { url = "https://files.pythonhosted.org/packages/c3/7a/e9a2da6a1fc5d007dd51fca083a663ab930a8c4d149c087732a5dbaa0029/coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd", size = 250946, upload-time = "2025-09-21T20:02:59.431Z" }, { url = "https://files.pythonhosted.org/packages/ef/5b/0b5799aa30380a949005a353715095d6d1da81927d6dbed5def2200a4e25/coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2", size = 221009, upload-time = "2025-09-21T20:03:01.324Z" }, { url = "https://files.pythonhosted.org/packages/da/b0/e802fbb6eb746de006490abc9bb554b708918b6774b722bb3a0e6aa1b7de/coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681", size = 221804, upload-time = "2025-09-21T20:03:03.4Z" }, { url = "https://files.pythonhosted.org/packages/9e/e8/71d0c8e374e31f39e3389bb0bd19e527d46f00ea8571ec7ec8fd261d8b44/coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880", size = 220384, upload-time = "2025-09-21T20:03:05.111Z" }, { url = "https://files.pythonhosted.org/packages/62/09/9a5608d319fa3eba7a2019addeacb8c746fb50872b57a724c9f79f146969/coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63", size = 219047, upload-time = "2025-09-21T20:03:06.795Z" }, { url = "https://files.pythonhosted.org/packages/f5/6f/f58d46f33db9f2e3647b2d0764704548c184e6f5e014bef528b7f979ef84/coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2", size = 219266, upload-time = "2025-09-21T20:03:08.495Z" }, { url = "https://files.pythonhosted.org/packages/74/5c/183ffc817ba68e0b443b8c934c8795553eb0c14573813415bd59941ee165/coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d", size = 260767, upload-time = "2025-09-21T20:03:10.172Z" }, { url = "https://files.pythonhosted.org/packages/0f/48/71a8abe9c1ad7e97548835e3cc1adbf361e743e9d60310c5f75c9e7bf847/coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0", size = 262931, upload-time = "2025-09-21T20:03:11.861Z" }, { url = "https://files.pythonhosted.org/packages/84/fd/193a8fb132acfc0a901f72020e54be5e48021e1575bb327d8ee1097a28fd/coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699", size = 265186, upload-time = "2025-09-21T20:03:13.539Z" }, { url = "https://files.pythonhosted.org/packages/b1/8f/74ecc30607dd95ad50e3034221113ccb1c6d4e8085cc761134782995daae/coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9", size = 259470, upload-time = "2025-09-21T20:03:15.584Z" }, { url = "https://files.pythonhosted.org/packages/0f/55/79ff53a769f20d71b07023ea115c9167c0bb56f281320520cf64c5298a96/coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f", size = 262626, upload-time = "2025-09-21T20:03:17.673Z" }, { url = "https://files.pythonhosted.org/packages/88/e2/dac66c140009b61ac3fc13af673a574b00c16efdf04f9b5c740703e953c0/coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1", size = 260386, upload-time = "2025-09-21T20:03:19.36Z" }, { url = "https://files.pythonhosted.org/packages/a2/f1/f48f645e3f33bb9ca8a496bc4a9671b52f2f353146233ebd7c1df6160440/coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0", size = 258852, upload-time = "2025-09-21T20:03:21.007Z" }, { url = "https://files.pythonhosted.org/packages/bb/3b/8442618972c51a7affeead957995cfa8323c0c9bcf8fa5a027421f720ff4/coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399", size = 261534, upload-time = "2025-09-21T20:03:23.12Z" }, { url = "https://files.pythonhosted.org/packages/b2/dc/101f3fa3a45146db0cb03f5b4376e24c0aac818309da23e2de0c75295a91/coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235", size = 221784, upload-time = "2025-09-21T20:03:24.769Z" }, { url = "https://files.pythonhosted.org/packages/4c/a1/74c51803fc70a8a40d7346660379e144be772bab4ac7bb6e6b905152345c/coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d", size = 222905, upload-time = "2025-09-21T20:03:26.93Z" }, { url = "https://files.pythonhosted.org/packages/12/65/f116a6d2127df30bcafbceef0302d8a64ba87488bf6f73a6d8eebf060873/coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a", size = 220922, upload-time = "2025-09-21T20:03:28.672Z" }, { url = "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260", size = 209952, upload-time = "2025-09-21T20:03:53.918Z" }, ] [package.optional-dependencies] toml = [ { name = "tomli", marker = "python_full_version <= '3.11'" }, ] [[package]] name = "cryptography" version = "50.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/57/ef/8f2df13c7216bcad3e1c74e07f6e193d93e998e114f524a53877c9af27ad/cryptography-50.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645", size = 4719554, upload-time = "2026-07-31T14:23:35.611Z" }, { url = "https://files.pythonhosted.org/packages/d9/41/029086c34d91052fc3b88bcc8056f709a7c915c7a23b235a54eb800b1c97/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7", size = 4702130, upload-time = "2026-07-31T14:23:37.635Z" }, { url = "https://files.pythonhosted.org/packages/7d/ff/b6ce0954962e7f7b969f850a883744197bb3910bdfd7b6da162eab7d9f68/cryptography-50.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3", size = 4725244, upload-time = "2026-07-31T14:23:39.471Z" }, { url = "https://files.pythonhosted.org/packages/06/1e/63a1027cb7fec360a182208e1b7767d5aa1fe57be3d6aa856e69a321edc0/cryptography-50.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f", size = 5342265, upload-time = "2026-07-31T14:23:41.286Z" }, { url = "https://files.pythonhosted.org/packages/6b/72/a1116d683a6d7ece94590013882515de087edf9ef0e6292aae615a44df73/cryptography-50.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae", size = 4734609, upload-time = "2026-07-31T14:23:43.139Z" }, { url = "https://files.pythonhosted.org/packages/15/37/36a9c479bbe49acea2636c7fd3360d20f7b7e079c300352011c44850b181/cryptography-50.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a", size = 4356517, upload-time = "2026-07-31T14:23:44.939Z" }, { url = "https://files.pythonhosted.org/packages/32/98/8a151d64367204cbc63ec65d37502f1d9c53cf4bfc6ec3c532614dbec60d/cryptography-50.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987", size = 4724529, upload-time = "2026-07-31T14:23:46.93Z" }, { url = "https://files.pythonhosted.org/packages/22/f6/ec13b470172126464a86bf54d2294a46d29837fc51ba3e45d4047946fb5e/cryptography-50.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169", size = 5299852, upload-time = "2026-07-31T14:23:48.851Z" }, { url = "https://files.pythonhosted.org/packages/da/3a/f05e32c99d440c9bb891ea0e36c9091891e36be5a9a87ab2ee6ea20729f6/cryptography-50.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f", size = 4734462, upload-time = "2026-07-31T14:23:50.861Z" }, { url = "https://files.pythonhosted.org/packages/ca/dc/bd72b26be8953f80625f63151efd38eee71c76ca6cf591c08ff34615a79e/cryptography-50.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105", size = 4852708, upload-time = "2026-07-31T14:23:52.715Z" }, { url = "https://files.pythonhosted.org/packages/27/20/c930314a2ab476d15dec966ec87e2e9637bb02b06106b12c0396c57bb603/cryptography-50.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef", size = 5004179, upload-time = "2026-07-31T14:23:54.887Z" }, { url = "https://files.pythonhosted.org/packages/d4/67/91eb047e69c5e845f2f14b8a2e4a1aab0f283cb885531e9e22c8adb176bc/cryptography-50.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc", size = 4700648, upload-time = "2026-07-31T14:24:00.702Z" }, { url = "https://files.pythonhosted.org/packages/30/82/85f0f7425c856b9f96459411eb12e74ef72df9caf6f8f15bf23a33ff131f/cryptography-50.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f", size = 4682442, upload-time = "2026-07-31T14:24:02.538Z" }, { url = "https://files.pythonhosted.org/packages/1a/28/b555a365adff1cca2fbe7b9e487d68a40de6bc67ff2cb587473eb43de0e7/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3", size = 4707596, upload-time = "2026-07-31T14:24:04.394Z" }, { url = "https://files.pythonhosted.org/packages/72/d8/f52538140cc719df62a01cf87d1c7142318d235817109d6f4054d7c352d6/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533", size = 5314552, upload-time = "2026-07-31T14:24:06.31Z" }, { url = "https://files.pythonhosted.org/packages/38/14/6120e5bd7c5aa022ad15424ba4d5c5269d0d9448ed4d55e492ea91e3c1c4/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037", size = 4717113, upload-time = "2026-07-31T14:24:08.349Z" }, { url = "https://files.pythonhosted.org/packages/fa/71/190bf38c3ee2e0f8efc9860ae100c9df4169742eef274b91e7aa1cb133b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f", size = 4338580, upload-time = "2026-07-31T14:24:10.227Z" }, { url = "https://files.pythonhosted.org/packages/3a/63/504ccfbbe61fd8aa983f7f146399cdf034c72c2fc55f5b2dfdcdcdb20c99/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11", size = 4707038, upload-time = "2026-07-31T14:24:12.169Z" }, { url = "https://files.pythonhosted.org/packages/01/77/2cf79bbfc4d12ca106437a6e170d6aaa01a373e93093118aaaef0e801bd4/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d", size = 5273110, upload-time = "2026-07-31T14:24:14.38Z" }, { url = "https://files.pythonhosted.org/packages/e5/45/8aae2972c520145377ea3559a605a899bebe227bf070b33cdb445929a9b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c", size = 4716439, upload-time = "2026-07-31T14:24:16.415Z" }, { url = "https://files.pythonhosted.org/packages/7b/20/4fe50b619a48c2525cc46e2dbc1ac490708d704be5d467bdaac6dc955682/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c", size = 4837383, upload-time = "2026-07-31T14:24:18.553Z" }, { url = "https://files.pythonhosted.org/packages/92/91/3a31366e183343d3703f8995c095f5734676bd6938118047e50fcf279eb4/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95", size = 4985772, upload-time = "2026-07-31T14:24:20.385Z" }, { url = "https://files.pythonhosted.org/packages/ff/c6/7a6202a534e32103a285b7834a120869557fe198d51d7cfe59754c8bda9c/cryptography-50.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3", size = 4745252, upload-time = "2026-07-31T14:24:26.118Z" }, { url = "https://files.pythonhosted.org/packages/85/4f/0fa8c2f4428198f15d9ff8d63400e27afbf94ce833f6108da1eb3753f945/cryptography-50.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f", size = 4728939, upload-time = "2026-07-31T14:24:27.994Z" }, { url = "https://files.pythonhosted.org/packages/d1/63/54dd723490ba2dc09b299682c10b38db38f159728bcaae8c591b8af2f22d/cryptography-50.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5", size = 4748483, upload-time = "2026-07-31T14:24:30.254Z" }, { url = "https://files.pythonhosted.org/packages/1d/dd/7c77d26285cc7f6991efce64a0f5b4f9383bfa5dd8c5033003eaf7db4cdb/cryptography-50.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f", size = 5367599, upload-time = "2026-07-31T14:24:32.457Z" }, { url = "https://files.pythonhosted.org/packages/46/c9/f60aed34c013f317f92817b6c171c2d22a78270fa41109bd4b08af26b194/cryptography-50.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025", size = 4762647, upload-time = "2026-07-31T14:24:34.599Z" }, { url = "https://files.pythonhosted.org/packages/be/f3/f9a0173b139372c3a48ed98154b45cc6b9de17c789d5ab552e621c293609/cryptography-50.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a", size = 4385197, upload-time = "2026-07-31T14:24:36.647Z" }, { url = "https://files.pythonhosted.org/packages/d8/36/83bb81f6e569bc38e1e4a7bc80f29b46bb9601920bc455fc8e888f5d5742/cryptography-50.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b", size = 4748095, upload-time = "2026-07-31T14:24:39.493Z" }, { url = "https://files.pythonhosted.org/packages/6b/16/d3008eff98c764979865834c3d386d4fd041b5f52e7f34fc29ac1a5eb515/cryptography-50.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708", size = 5325948, upload-time = "2026-07-31T14:24:41.556Z" }, { url = "https://files.pythonhosted.org/packages/9c/f8/d97f9603efda3888187bfdb893f26c41be4735c10631d05d284ee6b047c4/cryptography-50.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47", size = 4762400, upload-time = "2026-07-31T14:24:43.636Z" }, { url = "https://files.pythonhosted.org/packages/64/a2/4615c8f7d81a00b1d6e6afe19f694e1543582349fb5f4076f6cb5dc36485/cryptography-50.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9", size = 4878208, upload-time = "2026-07-31T14:24:45.522Z" }, { url = "https://files.pythonhosted.org/packages/d2/1a/efcfb02f91407149a0dacffffab791f7e19bf6385f63b3666dc8b5e5c9c8/cryptography-50.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7", size = 5037050, upload-time = "2026-07-31T14:24:47.697Z" }, { url = "https://files.pythonhosted.org/packages/01/b6/0b9e125e90f3d2dcf599a218a899cda7326a3158cfa258723f0b398b08f6/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8eb5e1172eb569ea8a872796576e6a67c276351728b6455d5beb01242b027c6a", size = 4692441, upload-time = "2026-07-31T14:24:53.743Z" }, { url = "https://files.pythonhosted.org/packages/53/c9/a5151588710785a96d7bc4de27d4cd62f263bbbcb203cfe29df537eb6505/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:910d11e1a385c654bf738bf3e6b8e6ed5de0f5610fcae2be9e5b398d8081d20e", size = 4699810, upload-time = "2026-07-31T14:24:55.746Z" }, { url = "https://files.pythonhosted.org/packages/c7/1a/15b92b25eb6ce3089cd49377ae990a0f3ad485a510f968aed1f19dbdcdf2/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:62598a8a57f815db4c6259a4e97d857dab56697e7de8e8ab02352ab74da1995d", size = 4691924, upload-time = "2026-07-31T14:24:58.082Z" }, { url = "https://files.pythonhosted.org/packages/62/15/219075012ab13e8905f3cd572204f4acb4b111df787104346b9bc0cea789/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:07479a1cb08219ab719147e742e76090c9c773321959bb94946fffdd397a6437", size = 4699593, upload-time = "2026-07-31T14:24:59.951Z" }, ] [[package]] name = "distlib" version = "0.3.9" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923, upload-time = "2024-10-09T18:35:47.551Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973, upload-time = "2024-10-09T18:35:44.272Z" }, ] [[package]] name = "docutils" version = "0.21.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.11'", ] sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, ] [[package]] name = "docutils" version = "0.22.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, ] [[package]] name = "example-isort-sorting-plugin" version = "0.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "natsort" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d8/4a/eef19c44d8a952b8993665fb9d67338deccab506607072d7a45c7c46d369/example_isort_sorting_plugin-0.1.0.tar.gz", hash = "sha256:cb9112a5aaa0483086b362f201bdb01a65cf711a281c0d0bcf00b2a9bb14a8e7", size = 1110, upload-time = "2022-12-11T21:04:57.488Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9c/2d/1fda94a5ddd0c0b713ca8141a269a89592554dda86b4066ac147b4c4d9bc/example_isort_sorting_plugin-0.1.0-py3-none-any.whl", hash = "sha256:4a41342268a880c2a00aada25c8fc269efec1baa3b79207a429a0b8a68a2f071", size = 1719, upload-time = "2022-12-11T21:04:55.089Z" }, ] [[package]] name = "example-shared-isort-profile" version = "0.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a7/31/ff8151ccbc393c2e19b855c09a8a4dd8bbf0178b79e3ed9d93c278569ff0/example_shared_isort_profile-0.1.0.tar.gz", hash = "sha256:c40d63639a64a6c641c84648d891f29370f446f3c6afd57e61430cfa45cd3b84", size = 1022, upload-time = "2022-12-11T21:05:42.675Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ab/cb/915806bb31b949283adc89f8af29bd736b47c7ca3e816cd11bfef40d0c9f/example_shared_isort_profile-0.1.0-py3-none-any.whl", hash = "sha256:579fb3cd8db5fd63b9b1d426090167640f695a17056e9e5f75bcd9a44341f70b", size = 1643, upload-time = "2022-12-11T21:05:40.507Z" }, ] [[package]] name = "exceptiongroup" version = "1.2.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883, upload-time = "2024-07-12T22:26:00.161Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" }, ] [[package]] name = "filelock" version = "3.20.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, ] [[package]] name = "flake8" version = "7.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mccabe" }, { name = "pycodestyle" }, { name = "pyflakes" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9b/af/fbfe3c4b5a657d79e5c47a2827a362f9e1b763336a52f926126aa6dc7123/flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872", size = 48326, upload-time = "2025-06-20T19:31:35.838Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9f/56/13ab06b4f93ca7cac71078fbe37fcea175d3216f31f85c3168a6bbd0bb9a/flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e", size = 57922, upload-time = "2025-06-20T19:31:34.425Z" }, ] [[package]] name = "flake8-bugbear" version = "24.12.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "flake8" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c7/25/48ba712ff589b0149f21135234f9bb45c14d6689acc6151b5e2ff8ac2ae9/flake8_bugbear-24.12.12.tar.gz", hash = "sha256:46273cef0a6b6ff48ca2d69e472f41420a42a46e24b2a8972e4f0d6733d12a64", size = 82907, upload-time = "2024-12-12T16:49:26.307Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b9/21/0a875f75fbe4008bd171e2fefa413536258fe6b4cfaaa087986de74588f4/flake8_bugbear-24.12.12-py3-none-any.whl", hash = "sha256:1b6967436f65ca22a42e5373aaa6f2d87966ade9aa38d4baf2a1be550767545e", size = 36664, upload-time = "2024-12-12T16:49:23.584Z" }, ] [[package]] name = "flake8-pyproject" version = "1.2.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flake8" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/5f/1d/635e86f9f3a96b7ea9e9f19b5efe17a987e765c39ca496e4a893bb999112/flake8_pyproject-1.2.3-py3-none-any.whl", hash = "sha256:6249fe53545205af5e76837644dc80b4c10037e73a0e5db87ff562d75fb5bd4a", size = 4756, upload-time = "2023-03-21T20:51:38.911Z" }, ] [[package]] name = "h11" version = "0.16.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] [[package]] name = "hatch" version = "1.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "hatchling" }, { name = "httpx" }, { name = "hyperlink" }, { name = "keyring" }, { name = "packaging" }, { name = "pexpect" }, { name = "platformdirs" }, { name = "rich" }, { name = "shellingham" }, { name = "tomli-w" }, { name = "tomlkit" }, { name = "userpath" }, { name = "uv" }, { name = "virtualenv" }, { name = "zstandard" }, ] sdist = { url = "https://files.pythonhosted.org/packages/bc/15/b4e3d50d8177e6e8a243b24d9819e3807f7bfd3b2bebe7b5aef32a9c79cb/hatch-1.14.0.tar.gz", hash = "sha256:351e41bc6c72bc93cb98651212226e495b43549eee27c487832e459e5d0f0eda", size = 5188143, upload-time = "2024-12-16T01:01:38.042Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/85/c6/ad910cdb79600af0100b7c4f7093eb4b95a2b44e589e66b6b938b09cc6f9/hatch-1.14.0-py3-none-any.whl", hash = "sha256:b12c7a2f4aaf6db7180e35c476e1a2ad4ec7197c20c4332964599424d4918ded", size = 125763, upload-time = "2024-12-16T01:01:33.413Z" }, ] [[package]] name = "hatchling" version = "1.27.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "pluggy" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "trove-classifiers" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8f/8a/cc1debe3514da292094f1c3a700e4ca25442489731ef7c0814358816bb03/hatchling-1.27.0.tar.gz", hash = "sha256:971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6", size = 54983, upload-time = "2024-12-15T17:08:11.894Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", hash = "sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b", size = 75794, upload-time = "2024-12-15T17:08:10.364Z" }, ] [[package]] name = "httpcore" version = "1.0.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "h11" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, ] [[package]] name = "httpx" version = "0.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "certifi" }, { name = "httpcore" }, { name = "idna" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] [[package]] name = "hyperlink" version = "21.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3a/51/1947bd81d75af87e3bb9e34593a4cf118115a8feb451ce7a69044ef1412e/hyperlink-21.0.0.tar.gz", hash = "sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b", size = 140743, upload-time = "2021-01-08T05:51:20.972Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/6e/aa/8caf6a0a3e62863cbb9dab27135660acba46903b703e224f14f447e57934/hyperlink-21.0.0-py2.py3-none-any.whl", hash = "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4", size = 74638, upload-time = "2021-01-08T05:51:22.906Z" }, ] [[package]] name = "hypothesis" version = "6.140.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "sortedcontainers" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/7f/946343e32881b56adc0eba64e428ad2f85251f9ef16e3e4ec1b6ab80199b/hypothesis-6.140.3.tar.gz", hash = "sha256:4f4a09bf77af21e0cc3dffed1ea639812dc75d38f81308ec9fb0e33f8557b0cb", size = 466925, upload-time = "2025-10-04T22:29:44.499Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/65/2a/0553ac2a8af432df92f2ffc05ca97e7ed64e00c97a371b019ae2690de325/hypothesis-6.140.3-py3-none-any.whl", hash = "sha256:a2cfff51641a58a56081f5c90ae1da6ccf3d043404f411805f7f0e0d75742d0e", size = 534534, upload-time = "2025-10-04T22:29:40.635Z" }, ] [package.optional-dependencies] lark = [ { name = "lark" }, ] [[package]] name = "hypothesmith" version = "0.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "hypothesis", extra = ["lark"] }, { name = "libcst" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e3/f6/1a64114dee6c46985482c35bdbc12025db59973a0225eec47ac4d306030f/hypothesmith-0.3.3.tar.gz", hash = "sha256:96c14802d6c8e85d8975264176878db54b28d2ed921fdbfedc2e6b8ce3c81716", size = 25529, upload-time = "2024-02-16T20:21:24.511Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/69/bc/78dcf42c6eaaf7d628f061f1e533a596f5bca2a53be2b714adc5d370d48e/hypothesmith-0.3.3-py3-none-any.whl", hash = "sha256:fdb0172f9de97d09450da40da7da083fdd118bcd2f88b1a2289413d2d496b1b1", size = 19247, upload-time = "2024-02-16T20:20:47.059Z" }, ] [[package]] name = "idna" version = "3.15" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, ] [[package]] name = "imagesize" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/6c/e6/7bf14eeb8f8b7251141944835abd42eb20a658d89084b7e1f3e5fe394090/imagesize-2.0.0.tar.gz", hash = "sha256:8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3", size = 1773045, upload-time = "2026-03-03T14:18:29.941Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5f/53/fb7122b71361a0d121b669dcf3d31244ef75badbbb724af388948de543e2/imagesize-2.0.0-py2.py3-none-any.whl", hash = "sha256:5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96", size = 9441, upload-time = "2026-03-03T14:18:27.892Z" }, ] [[package]] name = "importlib-metadata" version = "8.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767, upload-time = "2025-01-20T22:21:30.429Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971, upload-time = "2025-01-20T22:21:29.177Z" }, ] [[package]] name = "iniconfig" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" }, ] [[package]] name = "isort" source = { editable = "." } dependencies = [ { name = "mypy-extensions" }, ] [package.optional-dependencies] colors = [ { name = "colorama" }, ] [package.dev-dependencies] dev = [ { name = "bandit" }, { name = "black" }, { name = "colorama" }, { name = "coverage", extra = ["toml"] }, { name = "example-isort-sorting-plugin" }, { name = "example-shared-isort-profile" }, { name = "flake8" }, { name = "flake8-bugbear" }, { name = "flake8-pyproject" }, { name = "hatch" }, { name = "hypothesis" }, { name = "hypothesmith" }, { name = "libcst" }, { name = "mypy" }, { name = "pep8-naming" }, { name = "pytest" }, { name = "pytest-benchmark" }, { name = "ruff" }, { name = "stdlibs" }, { name = "tox" }, { name = "tox-uv" }, { name = "types-colorama" }, { name = "uv" }, ] docs = [ { name = "myst-parser", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "myst-parser", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-immaterial" }, ] [package.metadata] requires-dist = [ { name = "colorama", marker = "extra == 'colors'" }, { name = "mypy-extensions", specifier = ">=1.1.0" }, ] provides-extras = ["colors"] [package.metadata.requires-dev] dev = [ { name = "bandit", specifier = ">=1.8.6" }, { name = "black", specifier = ">=25.9.0" }, { name = "colorama", specifier = ">=0.4.6" }, { name = "coverage", extras = ["toml"], specifier = ">=7.10.7" }, { name = "example-isort-sorting-plugin", specifier = ">=0.1.0" }, { name = "example-shared-isort-profile", specifier = ">=0.1.0" }, { name = "flake8", specifier = ">=7.3.0" }, { name = "flake8-bugbear", specifier = ">=24.12.12" }, { name = "flake8-pyproject", specifier = ">=1.2.3" }, { name = "hatch", specifier = ">=1.14.0" }, { name = "hypothesis", specifier = ">=6.140.3" }, { name = "hypothesmith", specifier = ">=0.3.3" }, { name = "libcst", specifier = ">=1.9.0" }, { name = "mypy", specifier = ">=2.3.1" }, { name = "pep8-naming", specifier = ">=0.15.1" }, { name = "pytest", specifier = ">=8.4.2" }, { name = "pytest-benchmark", specifier = ">=5.1.0" }, { name = "ruff", specifier = ">=0.13.3" }, { name = "stdlibs", specifier = ">=2026.2.26" }, { name = "tox", specifier = ">=4.30.3" }, { name = "tox-uv", specifier = ">=1.28.0" }, { name = "types-colorama", specifier = ">=0.4.2" }, { name = "uv", specifier = ">=0.8.23" }, ] docs = [ { name = "myst-parser", specifier = ">=4.0.1" }, { name = "sphinx", specifier = ">=7.4.7" }, { name = "sphinx-immaterial", specifier = ">=0.13.9" }, ] [[package]] name = "jaraco-classes" version = "3.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "more-itertools" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, ] [[package]] name = "jaraco-context" version = "6.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "backports-tarfile", marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", size = 13912, upload-time = "2024-08-20T03:39:27.358Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", size = 6825, upload-time = "2024-08-20T03:39:25.966Z" }, ] [[package]] name = "jaraco-functools" version = "4.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "more-itertools" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ab/23/9894b3df5d0a6eb44611c36aec777823fc2e07740dabbd0b810e19594013/jaraco_functools-4.1.0.tar.gz", hash = "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d", size = 19159, upload-time = "2024-09-27T19:47:09.122Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9f/4f/24b319316142c44283d7540e76c7b5a6dbd5db623abd86bb7b3491c21018/jaraco.functools-4.1.0-py3-none-any.whl", hash = "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649", size = 10187, upload-time = "2024-09-27T19:47:07.14Z" }, ] [[package]] name = "jeepney" version = "0.8.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d6/f4/154cf374c2daf2020e05c3c6a03c91348d59b23c5366e968feb198306fdf/jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806", size = 106005, upload-time = "2022-04-03T17:58:19.651Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ae/72/2a1e2290f1ab1e06f71f3d0f1646c9e4634e70e1d37491535e19266e8dc9/jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755", size = 48435, upload-time = "2022-04-03T17:58:16.575Z" }, ] [[package]] name = "jinja2" version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] [[package]] name = "keyring" version = "25.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, { name = "jaraco-classes" }, { name = "jaraco-context" }, { name = "jaraco-functools" }, { name = "jeepney", marker = "sys_platform == 'linux'" }, { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, { name = "secretstorage", marker = "sys_platform == 'linux'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/70/09/d904a6e96f76ff214be59e7aa6ef7190008f52a0ab6689760a98de0bf37d/keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66", size = 62750, upload-time = "2024-12-25T15:26:45.782Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d3/32/da7f44bcb1105d3e88a0b74ebdca50c59121d2ddf71c9e34ba47df7f3a56/keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd", size = 39085, upload-time = "2024-12-25T15:26:44.377Z" }, ] [[package]] name = "lark" version = "1.2.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/af/60/bc7622aefb2aee1c0b4ba23c1446d3e30225c8770b38d7aedbfb65ca9d5a/lark-1.2.2.tar.gz", hash = "sha256:ca807d0162cd16cef15a8feecb862d7319e7a09bdb13aef927968e45040fed80", size = 252132, upload-time = "2024-08-13T19:49:00.652Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2d/00/d90b10b962b4277f5e64a78b6609968859ff86889f5b898c1a778c06ec00/lark-1.2.2-py3-none-any.whl", hash = "sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c", size = 111036, upload-time = "2024-08-13T19:48:58.603Z" }, ] [[package]] name = "libcst" version = "1.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml", version = "6.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, { name = "pyyaml", version = "6.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "pyyaml-ft", marker = "python_full_version == '3.13.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/02/c0/098e5c91ff1537f00c85a6438b6cb1863d17144680cc91f47c87f104a200/libcst-1.9.0.tar.gz", hash = "sha256:087b58a9afe076bb08e2d726478e1f16cb928d67ffa9092817e033c335de522a", size = 914739, upload-time = "2026-07-29T21:28:43.153Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f9/b3/23144dcd9f9cd6f6150058653ecd0357c6b09361656ebd286165a5d76d34/libcst-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5705f664266742b8d9323db714f58cf06a6cde739eb09fd9ca31cd233f9806e5", size = 2055037, upload-time = "2026-07-29T19:24:27.708Z" }, { url = "https://files.pythonhosted.org/packages/5d/87/164a9bac39a817b505d27cf4490e6f66b2a1b8adb6b4e022b1ef4613bcfe/libcst-1.9.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7dcddfb4905c5ef90a6fdc58dccbc049faf0056b68161042fc1e0526b20cba05", size = 2210166, upload-time = "2026-07-29T19:24:29.386Z" }, { url = "https://files.pythonhosted.org/packages/1c/cb/b5aeaefbe471c4225c145c15c3bac96d67b0641a5e3ec9b871c62858c88c/libcst-1.9.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cc84aee3c293a012db722aa2d6ea87be524646852684a5a4e54c1fb98ea9072f", size = 2260568, upload-time = "2026-07-29T19:24:31.021Z" }, { url = "https://files.pythonhosted.org/packages/ab/0f/351dcd23629732e35e716e13ad536a193124cda075ac5e59dc866503447b/libcst-1.9.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:329ffda6b0ef708ff22876f982ec886a509c8f73ac1ccb0190693d35cb09aa0f", size = 2276024, upload-time = "2026-07-29T19:24:32.393Z" }, { url = "https://files.pythonhosted.org/packages/74/f5/0da2b60137a6290ba7dda902b32da64d46c9a5107aa002da578f8b1ff8c8/libcst-1.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5302fc698ef863e1a2fb540e95793b3d5a96abc89aa6423ee9c389d7d7ec5eab", size = 2384413, upload-time = "2026-07-29T19:24:33.983Z" }, { url = "https://files.pythonhosted.org/packages/8f/80/7e48d2fc1afda13a6de50ab6f8340dbd104fd6c9bcd2ff11dec66e320f2c/libcst-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6615b036694f6906e30dc018b5b34898cff7ff760ab549f9dea1ada4f53495b", size = 2110204, upload-time = "2026-07-29T19:24:35.491Z" }, { url = "https://files.pythonhosted.org/packages/42/87/52c88c7ba873011526aa35b8fa0cf55d50effe967c999e7cb688155c5691/libcst-1.9.0-cp310-cp310-win_arm64.whl", hash = "sha256:dacc583bcd8db56e457ae559b910673c6b5df66f694d1f1154ce0b3982158d17", size = 1988757, upload-time = "2026-07-29T19:24:37.053Z" }, { url = "https://files.pythonhosted.org/packages/53/36/b45e6950878d9143f95db609432031ac688b311aa14450a8df239c648c45/libcst-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cdbafbc992b38ea005ef37e20243c4177879020c82d414789fbb7fa22d6d3a9", size = 2054911, upload-time = "2026-07-29T19:24:38.542Z" }, { url = "https://files.pythonhosted.org/packages/6f/d8/1c1ae22e05fbc763dc5a4e4c3cab356166913ef19d2c2da42e6f1be5c98c/libcst-1.9.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3f735e3acb3afc75d50803fde1598b467bf05d12d6854025df4398aebd75a14c", size = 2209147, upload-time = "2026-07-29T19:24:40.039Z" }, { url = "https://files.pythonhosted.org/packages/84/e1/0d5eaeac4dadd34fcd9ea154d0a6e5ce10c9fec0e02c73f807a16475b8b0/libcst-1.9.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:951f13c193e590fc88c9582a5341a130fe53ecece51043cff4bd54e25a5e022d", size = 2260355, upload-time = "2026-07-29T19:24:41.423Z" }, { url = "https://files.pythonhosted.org/packages/71/b2/45ce33c6bca5c1961a90f2999138741daa0dc8cf6fe8b122c0ca2502632d/libcst-1.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bc6aa3e7f21315dbe9868c0d13ffaac309514e8c816320c5f91230fa5b21df92", size = 2275659, upload-time = "2026-07-29T19:24:42.828Z" }, { url = "https://files.pythonhosted.org/packages/bb/b1/936d2f17d93fb160579ff56ca671d26154209bbc07ea8645eb11db1f9633/libcst-1.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f101e0bf8638eba80694ecc3c3abb5309eb488f5271427536cb379c5c1e0ba9e", size = 2383467, upload-time = "2026-07-29T19:24:44.397Z" }, { url = "https://files.pythonhosted.org/packages/e7/59/db667f15d62cb22c75b24f6b13c4151292aa1889a64ff9abce92f60d5a97/libcst-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:77d9303572cf0fbd0a2eb71aff167a4246541f43bf58b781b35b9d0bbab80ddc", size = 2109985, upload-time = "2026-07-29T19:24:45.966Z" }, { url = "https://files.pythonhosted.org/packages/6d/a8/99926865cd3f97c0cffc71cd38152c883b7ae666eb8327d355f01bddb33a/libcst-1.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:4090105fa4ad114766d0f8b34418d9a3b359bc8645da860a447e19770b5705d1", size = 1988559, upload-time = "2026-07-29T19:24:47.528Z" }, { url = "https://files.pythonhosted.org/packages/b0/bb/d22c37c33dfe18084634f5ef89f8f0749ffe7b6e0ad312722aafd86bbbdb/libcst-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cd1a3500c41784075c4946a995d5ad89f68fa0d226b63ff3c4d78f6ea6dd23e5", size = 2043159, upload-time = "2026-07-29T19:24:49.013Z" }, { url = "https://files.pythonhosted.org/packages/10/b8/2dedef84d72e7271119217503b69ed6dc5d0b2077685e163caae669d9c70/libcst-1.9.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:611cebd3bbc2014576f4dcc7b845b3c594c96ddc287a3db9b78f22eff156a7d3", size = 2203245, upload-time = "2026-07-29T19:24:50.399Z" }, { url = "https://files.pythonhosted.org/packages/e8/90/e02ac2dad647423f947bb11f8322bfdba8ccfdd380e6c7b695add2d1acd4/libcst-1.9.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:8d731abe1307720ea1a52d447555e8443a6d130e0e520243c0634a58f6edbc9d", size = 2255388, upload-time = "2026-07-29T19:24:52.21Z" }, { url = "https://files.pythonhosted.org/packages/13/5f/6089a51518cfd2ff40950eb26bcc36951d7b6d4f4213568aa0290265aff7/libcst-1.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8bc5351d92ca6ac1cc32e097700e1161ad1ceaa4d9b2cca5abadb1e94576b325", size = 2268982, upload-time = "2026-07-29T19:24:53.562Z" }, { url = "https://files.pythonhosted.org/packages/ed/78/26881ec466fb70cbc129dca26ccb5a52a0061face2c5822e4b61f00f9699/libcst-1.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03165a264653bb77f6a11b412ae09c08bdb0c25864f3b8d42b816ac64b9d4b9e", size = 2378174, upload-time = "2026-07-29T19:24:55.175Z" }, { url = "https://files.pythonhosted.org/packages/e1/7a/a4dba5f11faf12a12ffba06d18019987851aab33b590242a602ffd4fb1bd/libcst-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:b755ed4a4bc2faee849b54820023137d60d4c199e0a0822f0ff0c1bc49e49b48", size = 2103462, upload-time = "2026-07-29T19:24:56.966Z" }, { url = "https://files.pythonhosted.org/packages/f5/13/57cb129093e0d6744b3c914ba6cbbdf51ed1b2c823882936c553a3a0cf1b/libcst-1.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e50576bad7d56459d9792cd0b0dfe5469dad13646e9a9c0a8b1ba20b269f332", size = 1979825, upload-time = "2026-07-29T19:24:58.403Z" }, { url = "https://files.pythonhosted.org/packages/f2/b1/befc0544283bb3923a928accf79ed685e5a725524bdb3491826670affc07/libcst-1.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b8b9df30f524317b097dc53065b25dda33d6a4cc3c7c8bf4fc83ca7559c58cc0", size = 2043754, upload-time = "2026-07-29T19:24:59.885Z" }, { url = "https://files.pythonhosted.org/packages/45/50/fef7c172a8457c95894edf5fb04805024899cdfea41fa01b0636587b79e1/libcst-1.9.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:e465a7bc9c2b9533eb9e06d2391f8819f811b5112919d4064c9fb8565aaafa08", size = 2203548, upload-time = "2026-07-29T19:25:01.323Z" }, { url = "https://files.pythonhosted.org/packages/18/ff/764cd2be1fd99d774fc44039c319dc0ed1d9d9afeaa02759a05121cccd4b/libcst-1.9.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8504b422c95676a8c27b517e1ac01413ece91bf356865c587ca9bdcd5708a2f7", size = 2255274, upload-time = "2026-07-29T19:25:02.764Z" }, { url = "https://files.pythonhosted.org/packages/34/a7/474748a27a02fa83e3556b260d5f5236ca48164fa7beffecf3b2cad24dca/libcst-1.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bcb9f9d4fcfe2ec7a40d2c26e03a538d5d9dc189c38551eb3f94ab661afee7c0", size = 2269126, upload-time = "2026-07-29T19:25:04.158Z" }, { url = "https://files.pythonhosted.org/packages/8a/b7/655e45363b8cf87b91e41e060b21c89e5316c7ef36eb14a1a498b27bb71e/libcst-1.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8d671c39a431c309476099b8ec811e412503ec0f4465f6fc907cb51c70e8e6b", size = 2378602, upload-time = "2026-07-29T19:25:06.424Z" }, { url = "https://files.pythonhosted.org/packages/db/13/6da63f0902ece43bf9d737017251ad7ad06edd9d3c4e1856450403cb4473/libcst-1.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:4d382fba04077eb556a1ea4295a4482e192aa93639c5a07ba0885841965ea0c0", size = 2103486, upload-time = "2026-07-29T19:25:07.979Z" }, { url = "https://files.pythonhosted.org/packages/fd/ff/dccb1a55e38b4e47256a97242d61d2bef5366c744faf88fb087d4fa0f995/libcst-1.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:a621e261990148c1cfbe26c1798bd2375c131cf358a44a7a4659fc41c1a336e1", size = 1979907, upload-time = "2026-07-29T19:25:09.532Z" }, { url = "https://files.pythonhosted.org/packages/65/2a/4943c71d90975bc59034a057dea346b365c276f308c1d31f5cf2bd85492d/libcst-1.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:eccf4c57d273cdd3fe1c67b72cf9bb1bbd4547aa011824e96ccf5b7136057aa4", size = 2044529, upload-time = "2026-07-29T19:25:11.04Z" }, { url = "https://files.pythonhosted.org/packages/be/ed/1168b98c2a0f338be3a44753647baab051feaf6167cc887bf4447f8fd920/libcst-1.9.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:32395244edfe6538e0ea2bf82051d60103d3f54861274805c4fb3745efb70a85", size = 2203519, upload-time = "2026-07-29T19:25:12.543Z" }, { url = "https://files.pythonhosted.org/packages/25/7d/2eaa697a80f899bcf2245680bbfcc1e478eb3b3328c21d4b2880bdf00dac/libcst-1.9.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:444e84c76cd035cd2fe136838c1a524d34b08216521f6f5093df8a6f6cfa5799", size = 2255879, upload-time = "2026-07-29T19:25:14.137Z" }, { url = "https://files.pythonhosted.org/packages/90/03/793b9fd96dd52d202f5f2b88d7e54f05ebed767d1bb3b32395a1817bf6ab/libcst-1.9.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bb5d0946f2b4c6711b5d69fe4f833b364f9e7a1a2b08f88b98619dc18975099a", size = 2269807, upload-time = "2026-07-29T19:25:15.647Z" }, { url = "https://files.pythonhosted.org/packages/a3/f4/1bc7aaea03971c45e8a885fed9fc1c73176dbbd00b0296a3cde9529bafd6/libcst-1.9.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:45808c03528b3ad40b14095348a918e08d98c47b4a125d631cb78e817c0a5b16", size = 2378171, upload-time = "2026-07-29T19:25:17.289Z" }, { url = "https://files.pythonhosted.org/packages/8e/f7/bc49e367d2bc8817213594dd52cf6b2da2dbbda90b5382d60653999274ac/libcst-1.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:568288cdbfe3b4ca3ae4852cb0a439ff053dd54c841bc4995bf2b71238b5de40", size = 2177847, upload-time = "2026-07-29T19:25:19.35Z" }, { url = "https://files.pythonhosted.org/packages/87/80/4d81577a22e6d535d1a3409f3a1c6903e09036f0e17fc47f92169dbc3501/libcst-1.9.0-cp314-cp314-win_arm64.whl", hash = "sha256:107593af46945593e7825821793393262bc4fa1d3ea24c3ed487b61269bbbdf8", size = 2058134, upload-time = "2026-07-29T19:25:20.769Z" }, { url = "https://files.pythonhosted.org/packages/d1/7f/c3f3a0e7a1a2adaa76e815e82a7814d6bfe7d49432276bba652248c68d0b/libcst-1.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f6248cb07444ab9a6733a855737a9febed8b9adca51347019348b09a3ac7dfe9", size = 2036102, upload-time = "2026-07-29T19:25:22.212Z" }, { url = "https://files.pythonhosted.org/packages/4e/af/2f5543255b2c7d749b966adeccc6bfb1652cf8b425afb913d0f3f025a865/libcst-1.9.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:496c24e0d3240bc7da45dae543aff3f5b6509978c39262d6b84ed2fb999dded2", size = 2194806, upload-time = "2026-07-29T19:25:24.017Z" }, { url = "https://files.pythonhosted.org/packages/cb/5b/03f4cddce426d005e208b39ea7b2d456e667cdee0f1891360f0fc8430f20/libcst-1.9.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ea490fa8540503db5f321f0268becab46eb50f710e8cec8041e241fd66f6874f", size = 2246762, upload-time = "2026-07-29T19:25:25.365Z" }, { url = "https://files.pythonhosted.org/packages/d8/31/9d5fe1e43dc3dbcc74f70f3e0e73fcdd8d84effc1059d8b45974f0d0d2eb/libcst-1.9.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:50ab94bb2524b419056d4003032b8c67102ac800f8d85b3c4260a01746d94dbb", size = 2259633, upload-time = "2026-07-29T19:25:26.928Z" }, { url = "https://files.pythonhosted.org/packages/a8/2b/6752b28d88c3a19b3bc0b9e1838443b6760d5c862b4f4b37955402659e24/libcst-1.9.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a2faaf92500d0226358125630f5aab4758e8aad3f2d70a10892ec3c700781a54", size = 2368043, upload-time = "2026-07-29T19:25:28.344Z" }, { url = "https://files.pythonhosted.org/packages/e2/94/775825b2637f8ab05694b6a4b3802ae6783b4e799f9b58d2400c7e2d4369/libcst-1.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0c7b548512db25af9c2997a95fa731bd6b6928ecbad6c0915d7482d8bb42d34f", size = 2176432, upload-time = "2026-07-29T19:25:29.921Z" }, { url = "https://files.pythonhosted.org/packages/fb/3d/88ad67427c6fd9db929e087912b0a540e5140e5cb77e7ca4170edaac8531/libcst-1.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:497d5329345f1f5df84e41b0bbd00204b64a2fd30dfe3cfaeaebca633a31e877", size = 2052931, upload-time = "2026-07-29T19:25:31.397Z" }, { url = "https://files.pythonhosted.org/packages/39/e4/ad790b043a38b10cea13166c8dd716654ad8b227c20f12eebf6326191cd9/libcst-1.9.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:a5068bf6114f6f4d79af7a6c80a28d1deb50441150ea1db13b5458ab34bec159", size = 2043987, upload-time = "2026-08-11T05:54:02.122Z" }, { url = "https://files.pythonhosted.org/packages/15/6b/d3cd8275cc54ffc9817ade91442b3e6e9edd1a4518bd4e6dda13e3152dbe/libcst-1.9.0-cp315-cp315-manylinux_2_28_aarch64.whl", hash = "sha256:7acfd18adcdd32dcf41ce676cf121002afcbe9ad2c72dc0c18d78465beedc228", size = 2204003, upload-time = "2026-08-11T05:54:04.031Z" }, { url = "https://files.pythonhosted.org/packages/da/62/87eddccb11d5d221d50ac4fff4b6e9b8d48c547028d01f7d0fbc5c8a1d75/libcst-1.9.0-cp315-cp315-manylinux_2_28_x86_64.whl", hash = "sha256:86361e2b426bd1b403e52375703c8b764e226e571adcb30442510710681edbf0", size = 2256053, upload-time = "2026-08-11T05:54:06.228Z" }, { url = "https://files.pythonhosted.org/packages/18/53/a44126aeb9fca8b4e342e0cc803ea00ca8f6cd5712619a7897b3eba13797/libcst-1.9.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:8c14abe844bec8b021111b3985474e49f5af799c020e8e40dedcac87c97a40c5", size = 2270576, upload-time = "2026-08-11T05:54:08.01Z" }, { url = "https://files.pythonhosted.org/packages/bb/51/3af3dd44b117d66486e0ed61e43a16b7fc8cc5c372e1dd4ca0e70b888d46/libcst-1.9.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:8930971d2299b7006bb5d10c10038f44efb6da89d5bca2823595e31a9cdb96af", size = 2378573, upload-time = "2026-08-11T05:54:10.093Z" }, { url = "https://files.pythonhosted.org/packages/fc/e8/e545087d298dbf6494e84a8092f0f5dbd62eedacabd4ecd6b364367b4804/libcst-1.9.0-cp315-cp315-win_amd64.whl", hash = "sha256:5891ce9cff815077614f509e3a23888c5ddb8081d6188e8ba1172c0ccc369022", size = 2177937, upload-time = "2026-08-11T05:54:12.168Z" }, { url = "https://files.pythonhosted.org/packages/84/17/93a00a8e03494a84db102dd0e3d35b8876b1084ca2c194b331a168d86eb1/libcst-1.9.0-cp315-cp315-win_arm64.whl", hash = "sha256:1260b5d070a3324447a53a00387225a55472a62077ce01922d30a2a78cc4b138", size = 2058633, upload-time = "2026-08-11T05:54:13.817Z" }, { url = "https://files.pythonhosted.org/packages/d7/00/c3751b12eb81822ea0b6131d374a98bc6c024c4b9ec5f6ea8f53dc23e967/libcst-1.9.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:02ee2dbdb5c218116f16a021350fc267a14be205b50d81c33f5d73857ab6fbf7", size = 2036178, upload-time = "2026-08-11T05:54:15.545Z" }, { url = "https://files.pythonhosted.org/packages/2f/12/48ff486eb5adc593f4818569e8896b74b672414ab4722a74b17e4c4cf31e/libcst-1.9.0-cp315-cp315t-manylinux_2_28_aarch64.whl", hash = "sha256:3e5b684191a0462b2d40a73261ea7f4da6a49e7a030b7e0fceca46bebb51dfe5", size = 2195496, upload-time = "2026-08-11T05:54:17.625Z" }, { url = "https://files.pythonhosted.org/packages/51/18/b13a4669864d41a4801fed7b86ede1049dc9a1e1dde250a7ee1f9c3b1285/libcst-1.9.0-cp315-cp315t-manylinux_2_28_x86_64.whl", hash = "sha256:2b150c4f298fe54eb0fe73abf71f57db74d070796140a8c206c9615b6861f01e", size = 2245769, upload-time = "2026-08-11T05:54:19.343Z" }, { url = "https://files.pythonhosted.org/packages/c8/c7/cf2d46744825e84afbd7228fdeeea8d23cf6c4b2074253c27efb7705c653/libcst-1.9.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:50b913e37187f00a6fb88962009364e1cf1b1284aa1762304f34b52b972f2218", size = 2260576, upload-time = "2026-08-11T05:54:21.444Z" }, { url = "https://files.pythonhosted.org/packages/d1/4d/5433d3d62250b2e4325938db101766bcab2a006819684713bcccb6259a88/libcst-1.9.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:9f92c75283030fd58fb7d6e560168a00381e0e3368ec8a026a3ec8a828a05f93", size = 2368709, upload-time = "2026-08-11T05:54:23.276Z" }, { url = "https://files.pythonhosted.org/packages/87/39/9f0e1690727623f99489895db0e42048a3e1e8cea0627517c593e437fd83/libcst-1.9.0-cp315-cp315t-win_amd64.whl", hash = "sha256:4adf97bb1aff8039b0bd4991e2f838be6e4fad552821b26b71a5d1a653c2582f", size = 2177866, upload-time = "2026-08-11T05:54:25.144Z" }, { url = "https://files.pythonhosted.org/packages/cd/79/9dc7811883e67ea771057e8937bc536eb3e77f3635fc5a93cd85b6fe1ea8/libcst-1.9.0-cp315-cp315t-win_arm64.whl", hash = "sha256:8f0dd08a5773d7051e105250b2a2c73d8065ea9b2d68e7e1bdc5244660e75f93", size = 2052908, upload-time = "2026-08-11T05:54:26.835Z" }, ] [[package]] name = "librt" version = "0.15.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/36/9b/356320fbae2ac8467e21c5e73e1389c80468e4998c62cc7d3536cc51b614/librt-0.15.0.tar.gz", hash = "sha256:4e66cbe84437497d951b799d3e1551291b6fb3d643820a7014b3655d57a59162", size = 214338, upload-time = "2026-08-07T10:49:42.663Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/48/12/e2e9ca532cf5a0e08c9489826c4a35c6958c92ba0313fda70e8c6c3912be/librt-0.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1a49adf16a7c9d9646816c2946135527197b6fcf4347c7b8b761cf1bfbf4489", size = 148673, upload-time = "2026-08-07T10:46:22.569Z" }, { url = "https://files.pythonhosted.org/packages/6d/7c/02005e23478bd5950618d9712e0fd2b4c511657857f3efd8ba6a5feabcdd/librt-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81a398f45b45a59200e13cd5ad1ae1d3f44334de98b148331afe2cdfee701c52", size = 153547, upload-time = "2026-08-07T10:46:23.931Z" }, { url = "https://files.pythonhosted.org/packages/a0/90/d8848a735f5642077fc4b3b4bebcdb08edf10178e3add45597f5201a368f/librt-0.15.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4eafbaff06b9563f8b1c850621ce51605de05208e09d4d71ce490bc972b7b9e8", size = 494355, upload-time = "2026-08-07T10:46:25.122Z" }, { url = "https://files.pythonhosted.org/packages/e1/0b/8604f41ea02feace490e9e405a338a15f9905369f55b239a9ce31c946f24/librt-0.15.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b0411b4066db926b80258c60dcb0e6db4c9cee312eab45b7e8866b17ddf9ada1", size = 485459, upload-time = "2026-08-07T10:46:26.447Z" }, { url = "https://files.pythonhosted.org/packages/a0/ac/84153bda1ce0da609182527ab92b40d961809e544eefdc5a1c2422971416/librt-0.15.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:febb1ce6cac545a54e6b769982824e955a700fdd9fbf3a08a3d82c990968b57d", size = 498398, upload-time = "2026-08-07T10:46:27.701Z" }, { url = "https://files.pythonhosted.org/packages/2c/3a/5ca6cd282b2c244bec8ec84102e09773264e9c02891d56ab3a8f0e4d7083/librt-0.15.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b230acc1c3bfe2d6f2627ba2b95dc92e58aa494600e9722d0e6ccbc931e59702", size = 515474, upload-time = "2026-08-07T10:46:28.9Z" }, { url = "https://files.pythonhosted.org/packages/73/d3/bd34110234779eb843c6ed66aba7c9b2091d3dd85989f1fb9922f564cb7a/librt-0.15.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6da110e5f314c19ab8478464d02ae18808ae73d522c15260fa4918acdcd64da9", size = 509484, upload-time = "2026-08-07T10:46:30.124Z" }, { url = "https://files.pythonhosted.org/packages/1b/6c/43c3f7f071d71631a7daa3b835ef2168ea39f20692d81464d4e47fbaa6d6/librt-0.15.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:eab9208b00ca55bf75983ec99f7bf13acc746a36102e98953addaad7f7ea1e1b", size = 532534, upload-time = "2026-08-07T10:46:31.511Z" }, { url = "https://files.pythonhosted.org/packages/c5/1c/b854adf036ea817c40408873a5b794d65a91d9f0f39826f2ad2a2d5d7f48/librt-0.15.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6c013cd3a1721e69e14380ada97eaa4b7b0cdf1c6b96fa765d4ea47c875088db", size = 537087, upload-time = "2026-08-07T10:46:32.734Z" }, { url = "https://files.pythonhosted.org/packages/25/5c/c9a890e244e7dd725d3bd8b560e41f0aec787eaf343b46956a290ab7b841/librt-0.15.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:567b1c430f8bd560e689421468278ac5941bab4a05303b5d95b6ae10db03f451", size = 536575, upload-time = "2026-08-07T10:46:33.965Z" }, { url = "https://files.pythonhosted.org/packages/5f/c5/c8e70b60b704299555f55db468eb46b1c81bfc60201ffbfe20407d89870c/librt-0.15.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:29c4cab9df457b19672c39be7f384ebb2bc925c4e2684b8780c222b43eb36389", size = 517142, upload-time = "2026-08-07T10:46:35.577Z" }, { url = "https://files.pythonhosted.org/packages/56/d1/767a90c41f5d381b3195bc88ac0ec4afda35777c9c781e1f9848fedd965e/librt-0.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bccbd8e5b0bffb7106cf18eb1baa3d7194b1cebb3b4b1cdbd4bdb19382a6ee6c", size = 558714, upload-time = "2026-08-07T10:46:36.829Z" }, { url = "https://files.pythonhosted.org/packages/f9/b4/3c0624b8dc8301ab808f2b3a910995bcabe28df070fb9a0e5505ae997dae/librt-0.15.0-cp310-cp310-win32.whl", hash = "sha256:8ae493ed5f659a7761c43d42f183db514536073ded9bcf671d2d1df47e29a07e", size = 104426, upload-time = "2026-08-07T10:46:38.594Z" }, { url = "https://files.pythonhosted.org/packages/31/98/e91c0382304bedb2db9c6801897319a9dcb68daac5e975819b562362f20d/librt-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc25fb356d0c7810bb49ff3df908ad1fda6995d660ab099ded69244ed7ab6053", size = 125057, upload-time = "2026-08-07T10:46:40.052Z" }, { url = "https://files.pythonhosted.org/packages/59/52/06790ced2ac7117f890c21bda43c39c958ec82aa665c0718e821d33ff939/librt-0.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:823b92cf3c18ecd08afc70c42473888b41b6e8ef5046f3b82c05c154a2fa3d22", size = 148039, upload-time = "2026-08-07T10:46:41.165Z" }, { url = "https://files.pythonhosted.org/packages/e7/1d/8e150b7fc449a1f33c8a760965cc1f43b14fc1577d9d0b50ab2701420e74/librt-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70bc1b602cf59917e8f0c7a2cbc8bcc6fbc14d5486136b00707a79619121d63", size = 153067, upload-time = "2026-08-07T10:46:42.418Z" }, { url = "https://files.pythonhosted.org/packages/51/87/a162bc5a66a35599dc619ecb215145f4de7d68e886b479b6d12593139f7c/librt-0.15.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:814ff83a25b5fce8b9c80c4dd803153fb5c5599fc74db9e022466938368957ef", size = 493087, upload-time = "2026-08-07T10:46:43.657Z" }, { url = "https://files.pythonhosted.org/packages/e5/3a/aeea1fc620cf48060d3065b37614edbf97043c099d0f50782bc8ca61d897/librt-0.15.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:57f5eeb6ad4c180de583b1038e61fe5fbd9796bb69a8a1c1a0c7ddbec4c8c60f", size = 485608, upload-time = "2026-08-07T10:46:45.038Z" }, { url = "https://files.pythonhosted.org/packages/52/ff/fe571ad416f0856fd0d5578ffc2e6dc531891e586e36b647bcf50569cab8/librt-0.15.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82909c8f7eb9952656b65d3147afde4cf8e6d5a991eebc86418b5e65843b0ab8", size = 498723, upload-time = "2026-08-07T10:46:46.35Z" }, { url = "https://files.pythonhosted.org/packages/0f/e1/7a65eb5dedb1f00aebd948cdd8e17add48bf066cab3514e9daf84ab45a6c/librt-0.15.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f779070399f991400fc451719e0ea388eb7de313388bada2c127a35de05f798a", size = 516002, upload-time = "2026-08-07T10:46:47.599Z" }, { url = "https://files.pythonhosted.org/packages/5f/45/59832b0ebfbd08c2742e6ece372ceb53f18bf1faef5d33c8daf3abebf749/librt-0.15.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bac89069bc496ebdf4f79ebb57bbd10d0b214c8454225deb672d91002bd17e18", size = 508607, upload-time = "2026-08-07T10:46:48.873Z" }, { url = "https://files.pythonhosted.org/packages/ea/0d/37fa73f3b43ebd8259f91ae9102a15e5a54e65d581e48dea72df3e81d7a4/librt-0.15.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e0d00c708fb2f5822b152429b1ac80a58dbbbc3f6c232c4d13a3f7fcf2ea5b4c", size = 530422, upload-time = "2026-08-07T10:46:50.45Z" }, { url = "https://files.pythonhosted.org/packages/26/02/e046c6fe7a5881ac34623242192f484426ba8a75595fd18f22c53a3f530f/librt-0.15.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6c6624fe268625869485553dd7cc1daf30d22558215bb2a4ff16f67a9801a31a", size = 534303, upload-time = "2026-08-07T10:46:51.693Z" }, { url = "https://files.pythonhosted.org/packages/95/32/d5e6d861ab0366f3edf74f887ab0c9eb9f535aaf01d32b80b4f734daa179/librt-0.15.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f56b397858a23dacf35ede366ed2212fdc03a6a57a1ad36468ad6e9dc5fac091", size = 536084, upload-time = "2026-08-07T10:46:52.951Z" }, { url = "https://files.pythonhosted.org/packages/2a/de/d69d725513fe53fc90c6d7a1f86e4428939bad2fb905b17fe4c18d413dde/librt-0.15.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4388184646efe2054911c5b00a1077d6d1ee86a95b7e8ba96dc7850a809f3f40", size = 514307, upload-time = "2026-08-07T10:46:54.194Z" }, { url = "https://files.pythonhosted.org/packages/36/93/f8aded0d6682b4f25820fa86e0690f87f01df9fd7bd09ddb04d9167ad021/librt-0.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97335f59082f9fe2ce6c2a9cc6433a0114bbb6cd4d5c09dd76c95c68b9f9a8b0", size = 557686, upload-time = "2026-08-07T10:46:55.443Z" }, { url = "https://files.pythonhosted.org/packages/74/09/ffeb6bdeb6cd862b4272fddc8ad05f938dd25d020ed517e631813917d80a/librt-0.15.0-cp311-cp311-win32.whl", hash = "sha256:83380ffde38062a2e9bb55d83e74474f6614665528b98a6928720fc006dfffbb", size = 104917, upload-time = "2026-08-07T10:46:56.605Z" }, { url = "https://files.pythonhosted.org/packages/96/28/7e2313a3ffbf0b4de7ba3da58a09e488507b4bd1ea2b5e69378354a23415/librt-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:f75720477ee05d509a310e856cacc8d909adc182f7b91193c207bcc26d7ee6db", size = 125886, upload-time = "2026-08-07T10:46:57.729Z" }, { url = "https://files.pythonhosted.org/packages/39/9e/04b8c3cde014ef255ee785730425268354543acc38902093a40afa0dc164/librt-0.15.0-cp311-cp311-win_arm64.whl", hash = "sha256:256237037a3ab001ae8d9803b2d43562a4c3aa38739843694349e4d5ebb0fd56", size = 111885, upload-time = "2026-08-07T10:46:58.787Z" }, { url = "https://files.pythonhosted.org/packages/ba/39/99c25030e782bdfb7a21be8c05254806a2e4bbb05c8d50c2a2130acbfa05/librt-0.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e87bc679f86a99aa3b26e3c78eeb821a247c9a28eae48eaafcc32c3bf4c3bb9e", size = 151021, upload-time = "2026-08-07T10:47:00.057Z" }, { url = "https://files.pythonhosted.org/packages/14/43/f4b1bd1b2888798a1409808889a25ea1ba49eaabce7d681ed27734c2df9d/librt-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71599e011ac880e8e45d46047d714871894c7d4ab6f25626f8d4f89da21f368d", size = 155267, upload-time = "2026-08-07T10:47:01.311Z" }, { url = "https://files.pythonhosted.org/packages/0c/db/3ad9c965c72f1e1d6beeec44ec10a54e17be8ae042fbb4baade16cbadced/librt-0.15.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c802434092b769b1d613ed2e13fac15fbfce1934a74bd10283b03c0fae231cd1", size = 503136, upload-time = "2026-08-07T10:47:02.45Z" }, { url = "https://files.pythonhosted.org/packages/4b/07/5888a6d76acd62ebce66c61b74d94e9370b9c32929f111e487bb6546f8ed/librt-0.15.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5500eeae393a184d14e1f35645962c27129d20c81afa4069e6ef826ebc2b3aaa", size = 496670, upload-time = "2026-08-07T10:47:03.675Z" }, { url = "https://files.pythonhosted.org/packages/29/39/ab57cc2f5b276156da02bb7f5a8921bada1cb1993ffec99acf811c602c23/librt-0.15.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6ecfc32dfb46fb7b565bcd6abf9412acf978775a998273d22888a6d7953730dd", size = 513688, upload-time = "2026-08-07T10:47:04.981Z" }, { url = "https://files.pythonhosted.org/packages/a7/b9/bdbb0b648b5c2befb031f4c6f3b1dd857415e8fb492a25a3c764a6681e6c/librt-0.15.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cc46cfd15022e35084355478c9ac809d90b1152222706ac9a7655ec21df6fa", size = 531904, upload-time = "2026-08-07T10:47:06.211Z" }, { url = "https://files.pythonhosted.org/packages/93/26/473c2e4b6c104e9e58e27ce95fc8005c8bd4fc36cae4f254371125a92db8/librt-0.15.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5f51401d102c885b9ca509e62c79b1dbff286e1b9b047fde6f763780789356d", size = 524427, upload-time = "2026-08-07T10:47:07.592Z" }, { url = "https://files.pythonhosted.org/packages/26/60/03b3abb82b41714671b907bf6989b228e31e6a8af52dec82b5b0728dc250/librt-0.15.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cc30523e3f1a23fb7511cc659834a0d01a1042bb9de359bc1c131cc4ec6c9656", size = 543155, upload-time = "2026-08-07T10:47:08.866Z" }, { url = "https://files.pythonhosted.org/packages/f2/0e/9bb1f0a4affbd0a1888f4f79dc03ed2a299d9a2c26c59ab2a97dcbf11903/librt-0.15.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:59fe030d8ae4a57e3fb7756bf35a858de74e04066fc8555c53d0af979132af81", size = 546890, upload-time = "2026-08-07T10:47:10.327Z" }, { url = "https://files.pythonhosted.org/packages/dc/84/6937a280d461f7de6e031ffb02edc2b7c3c90d49d630565ce8ff27cbc5f2/librt-0.15.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a6526a2a956bbb1e4ae3568c82e650fc99119c66bb011ea60715744955a2b4d", size = 555163, upload-time = "2026-08-07T10:47:11.798Z" }, { url = "https://files.pythonhosted.org/packages/bc/95/2a2853c1ee014bf102116e7f897a04beeaeb2461b45b79af98bdfb95f1ef/librt-0.15.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:85ea21ec6730194d67156b0e0b5430ccb1d61f8b8b907e39b37f9812b74a13f0", size = 535812, upload-time = "2026-08-07T10:47:13.279Z" }, { url = "https://files.pythonhosted.org/packages/c9/4c/cf9601c1b4c5f09280acd5d83abdb2e68527a2be8257136eb42304218622/librt-0.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1e47b8ba865d7ede071a91a7163073bbaeb72541f1ef8a07d512c45c7b5007f2", size = 573688, upload-time = "2026-08-07T10:47:14.727Z" }, { url = "https://files.pythonhosted.org/packages/47/6d/9ac7cbec46189a7625af4b5acbd25f10d827f4141b2002181848c8418923/librt-0.15.0-cp312-cp312-win32.whl", hash = "sha256:a5207ec414d1c4a2a7231b2086970dc036f94293cdf338190984958a013a42f1", size = 106138, upload-time = "2026-08-07T10:47:15.973Z" }, { url = "https://files.pythonhosted.org/packages/38/d0/2ae99c83be86ce23f925ac1aeeedc777e97f427c4a8d190c70d0a16e9a87/librt-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:73b30cfa976659b3917c8f6153bdb0591c6a9ec6583599fd24a689b690622022", size = 126974, upload-time = "2026-08-07T10:47:17.049Z" }, { url = "https://files.pythonhosted.org/packages/5d/ef/dd24f9635c730b86b87587967dda7516b1845e8b17684603d31607fed598/librt-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:a54cf9e0ef47b96af580849db5471142200568ce1e02cbf416addab551369570", size = 112292, upload-time = "2026-08-07T10:47:18.222Z" }, { url = "https://files.pythonhosted.org/packages/e7/42/467b53a601b406ccd7b97c1fd54b59cb34f9185ad5ce7e9d5c3c4e8961c8/librt-0.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:db13ca398005abcbe538deda87b686d9bd08b7001cf40c4c06b444960ae10a26", size = 151029, upload-time = "2026-08-07T10:47:19.312Z" }, { url = "https://files.pythonhosted.org/packages/3e/e6/36c2299b7a94b84fdd01220d8a777a71be5be0925bb0dbdf71c0a06a34d9/librt-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa1f1995789dca3698bc550aaceb09a51bd5df0a057ff84ff15296cd1975b801", size = 155194, upload-time = "2026-08-07T10:47:20.398Z" }, { url = "https://files.pythonhosted.org/packages/c9/b6/ed5071f9325845e670bd36012757419767fbf56af77ed483077b9e4db541/librt-0.15.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55456ea87d8df21808446d03817be2f65e20391c1c615d9187440dff28cd08dc", size = 502568, upload-time = "2026-08-07T10:47:21.652Z" }, { url = "https://files.pythonhosted.org/packages/7f/81/6450c67c3615d87704bcbc21323fafc69c799b06a044c447529f725d4b01/librt-0.15.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5a86a5a08c2235316bdb359d5dbb6ce0abfca7fac06363103e2c5af571d92f95", size = 496153, upload-time = "2026-08-07T10:47:22.925Z" }, { url = "https://files.pythonhosted.org/packages/e1/d6/5f52b722bc75076954b3bfd49be15ea362df4d580c6fb315d0f617100d30/librt-0.15.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e56b6a368529bed262da40ce13f8fef590db0479819cca84f16a1f01ac356d0b", size = 513336, upload-time = "2026-08-07T10:47:24.213Z" }, { url = "https://files.pythonhosted.org/packages/8d/e2/c08fd1d36ce63ea5a12b85c5d37f4550b5f86a692167e41e5a74222607ae/librt-0.15.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:234d8d394721fa0d786af15ebf1f3fb7f3ed82fd1cd0cde45c2f247b5d4281d2", size = 531661, upload-time = "2026-08-07T10:47:25.507Z" }, { url = "https://files.pythonhosted.org/packages/3f/d8/d9482fcbeb177b9eb87bb3899eeb3b42be690313c652f9e146b1d0681fb2/librt-0.15.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d8363d7accb0286ac3a0e633f396e93800dafb8150494505daf9515bbda591f3", size = 524487, upload-time = "2026-08-07T10:47:26.79Z" }, { url = "https://files.pythonhosted.org/packages/10/cc/075171517b41f861753034fbb151b42cfc83bcc853849f24f5e66fd60ccf/librt-0.15.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0f0ee3644d951f31055ad07d77d92520e84505dd7a432cc4cd501dd70ee06785", size = 543201, upload-time = "2026-08-07T10:47:27.999Z" }, { url = "https://files.pythonhosted.org/packages/b0/03/42c2330f37eeb475b6affeedd06518f60035f323af3a839335e3fc9fef2d/librt-0.15.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2cfd1a81a648806e6a7717be4cc4d1bb392fa229752bf8444ba365e381e984d6", size = 546467, upload-time = "2026-08-07T10:47:29.396Z" }, { url = "https://files.pythonhosted.org/packages/57/1e/1ad4c5638f7e64d8560328bd25c54b409a661bdb6ff254b38ff90744288d/librt-0.15.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a6cd22c9da0d866558e46a041f1cc0c2bbb26b61b137b2347fa834c332e1d101", size = 555139, upload-time = "2026-08-07T10:47:30.815Z" }, { url = "https://files.pythonhosted.org/packages/49/41/39fa7d15db1204cd1cbe6514680fbdc243adf754a0885061308f43afc013/librt-0.15.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6d5225ef8801e4ea5e482fa9b5dfb891dd9ef6f6d870f1f25d449ca2c70ac218", size = 536050, upload-time = "2026-08-07T10:47:32.222Z" }, { url = "https://files.pythonhosted.org/packages/1e/88/c6dcf0dd8e26dc0c9a499a2abab8646c86dcaf9ecea9524cb46d3686331a/librt-0.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d28a05796b99f749bf8794f17ba9ba1612d0076b802e9cfc62c554634e9ce3b", size = 573700, upload-time = "2026-08-07T10:47:33.527Z" }, { url = "https://files.pythonhosted.org/packages/1b/9b/ab54c71a7918a7c34fa5327fb61390a77446a07a146fbfb1165250a61035/librt-0.15.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:2067ff438048cead9d223ca5675bae2a25e520a7c3e6c1498bf9c6892d22caab", size = 82194, upload-time = "2026-08-07T10:47:34.835Z" }, { url = "https://files.pythonhosted.org/packages/8d/b2/4f9a243bb892395f3becb80789ade13771701091f9f07ab8230247953ba8/librt-0.15.0-cp313-cp313-win32.whl", hash = "sha256:1cd3b721f24c206398b9e26da3c3a9c011e6e89d06f318ba8ebefc30f1003890", size = 106231, upload-time = "2026-08-07T10:47:36.251Z" }, { url = "https://files.pythonhosted.org/packages/bf/af/64aff4885a40b93132382f2c314647d722574605416504379184ef3045ea/librt-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:f395a4a9a03ac062dbe9a9f82e0c720502e590a38feee6a757bc82e9c63afbd8", size = 126996, upload-time = "2026-08-07T10:47:37.453Z" }, { url = "https://files.pythonhosted.org/packages/27/83/335bccf6c7cb9028cb0b54aead27d9ece3f01f83bc6baa2abace5da655c1/librt-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:0a15cb554761247d84a3ec0cbdf4078d70725384f0e4662c0fa3b26266eb60ad", size = 112188, upload-time = "2026-08-07T10:47:38.729Z" }, { url = "https://files.pythonhosted.org/packages/a8/93/949053fb462eecc4a9a5ee770a81f4b40be7b79538b245545d4aebc6b58b/librt-0.15.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f5de7feedc56337a088eb15cd9fafa9938367362221d8cc62c642b7f94821993", size = 149833, upload-time = "2026-08-07T10:47:39.86Z" }, { url = "https://files.pythonhosted.org/packages/61/ca/8281aa6cd560a3420e4497729f6b704b53be3eeaaef82d5aeadddaf7441f/librt-0.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c0eb900c0e91f4aebe680845242e614f1864edfd44106380d0752ac29522bf8", size = 154088, upload-time = "2026-08-07T10:47:41.065Z" }, { url = "https://files.pythonhosted.org/packages/dd/02/1a1662dceaba6a086360891448d5ce9a7d3555976cae59a31a39d744b9c7/librt-0.15.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e8c9a650a188e38bac005048cbe6342e81407782944d01934540ab75e417df21", size = 494215, upload-time = "2026-08-07T10:47:42.388Z" }, { url = "https://files.pythonhosted.org/packages/69/84/99211619dc656370a3740c33d2b0b6d5a3fb1e73689314f6ed477a397dc4/librt-0.15.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:92bfed8deec93df30286b9fe9e3b1dd17329cc076a192b4ee5ec223841d54953", size = 491173, upload-time = "2026-08-07T10:47:43.683Z" }, { url = "https://files.pythonhosted.org/packages/d4/aa/5448d0b05f4579b635d3899176817ebf561af0e57bacd425b5b1887264c1/librt-0.15.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ec4b19788f835711a2072f9dbe6b03b3bf32ed1f0fb30cf399bdd59d9f0c33fa", size = 505512, upload-time = "2026-08-07T10:47:45.314Z" }, { url = "https://files.pythonhosted.org/packages/95/82/01940e40b83c43a546c4a3c896cf34ca272a9690899d55914e4827b3dcce/librt-0.15.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4c7bacb70930f3d0a56f4ecf1be474a1f0d941b01dd73b756f3c256d42cb879", size = 523073, upload-time = "2026-08-07T10:47:46.66Z" }, { url = "https://files.pythonhosted.org/packages/88/fa/759c0030f3ee371439eb26de34fc745807caf0abb878af7af4b8b7c3dd3d/librt-0.15.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3e79f05e4a08b4d880342673312bbc895b56df7765605796f15902eb5367d3ae", size = 515080, upload-time = "2026-08-07T10:47:48.319Z" }, { url = "https://files.pythonhosted.org/packages/0b/27/894e072228fcb159703c655da69f8cd10dbed489c36e3df7dd032a2483be/librt-0.15.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a417149c0cba4d50b61e992e5a15e69eaf96746609b461cc4ed168aeef6b79dd", size = 534164, upload-time = "2026-08-07T10:47:49.875Z" }, { url = "https://files.pythonhosted.org/packages/98/a3/0078e91c1f36f8815db17827de15650b9a3fe56c55fbf998c854b34e40d3/librt-0.15.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:da7a94d6a3411f579d72aa3e3bc5fbca7ed4549f3dbd7e5de3aa567333374285", size = 540616, upload-time = "2026-08-07T10:47:51.408Z" }, { url = "https://files.pythonhosted.org/packages/86/33/81a29b796dd52a45e9ef7974c7732926e8f10f15b8d2be505665979f896d/librt-0.15.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:856f743ae607f2c1380eccb566c0038a9fb3eabf0fc2be2704d76d9f73557239", size = 545890, upload-time = "2026-08-07T10:47:52.818Z" }, { url = "https://files.pythonhosted.org/packages/05/82/8be1baa1350e5d30cfd70ae79d0a6f4dc5862ef47f7bb2808aabc9bb86e5/librt-0.15.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:779a6e7c894737e5983e7790a9c78c4000c30e23c9aada08081bdbea53b0fa60", size = 523287, upload-time = "2026-08-07T10:47:54.165Z" }, { url = "https://files.pythonhosted.org/packages/c6/4f/d1be6a01a35c20ef734e0e44113f87d4af756a9354a89dcfbe3b4f8af5e1/librt-0.15.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:96bb17dbe8bab3c0954fbebfc69ed395599de75b6bbc35e3270a878e15d4dd65", size = 565868, upload-time = "2026-08-07T10:47:55.566Z" }, { url = "https://files.pythonhosted.org/packages/67/88/649cfa33f5825927b160610f670bdab012a64d627eddb94fa795ea4292fd/librt-0.15.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:7220697efaa6e5348fc3d18ee7f8563d4bfecd9872b37ffb915bfc1d08840622", size = 81619, upload-time = "2026-08-07T10:47:56.886Z" }, { url = "https://files.pythonhosted.org/packages/22/31/8e88a8d5e48fc8d1a817787fb6811dfff6499acd6c8683dd83934aa6ede0/librt-0.15.0-cp314-cp314-win32.whl", hash = "sha256:f54598964d357b1c5ab77cf5d92f21e598fe0e23cdbe9618480807f81b4eba15", size = 100138, upload-time = "2026-08-07T10:47:58.093Z" }, { url = "https://files.pythonhosted.org/packages/80/92/20fd6c4b6a1b1a564b076d55cd3d427d8428217d7638dc25a654cc4791d4/librt-0.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:3ff5893a2c23d886aa9ce786de5ac6ddc74aeeaf90743682b74d920e117d2e28", size = 121258, upload-time = "2026-08-07T10:47:59.564Z" }, { url = "https://files.pythonhosted.org/packages/fc/28/6af430b44d9ebb897b865a3c363b6dcace51357be2347cc0f8f869656a86/librt-0.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:3722a099730704c9a3d70c879fc0f51daec25fe5f1555672d97bc595abeafb95", size = 106467, upload-time = "2026-08-07T10:48:01.097Z" }, { url = "https://files.pythonhosted.org/packages/7e/aa/b42bb798942ced219f6d63b27e07f91237887a8d0bd0921666db79a13790/librt-0.15.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:38c0c7d4b6fc06c3324b3f9162c8391bfc4fd9dde53afe1033ce7edb48d5a714", size = 159523, upload-time = "2026-08-07T10:48:02.442Z" }, { url = "https://files.pythonhosted.org/packages/75/03/1b53cd4ef904e73b1d828a5f90143bf94a2967d7cfff0b9ccf93e12aa9b4/librt-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8b2fdd7ead3c995c37940a790690660d0ca006c302db26cc51933f6766866fc3", size = 161638, upload-time = "2026-08-07T10:48:03.725Z" }, { url = "https://files.pythonhosted.org/packages/ac/c4/9f9c9fba097d49e9e694c2b4dc331df31884645ecbc58a93b4b5fc69d2c5/librt-0.15.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fde98cf1fc4bac144ce23c2c4c017b924ba714509ea9334977b0b27050c837d", size = 701795, upload-time = "2026-08-07T10:48:05.135Z" }, { url = "https://files.pythonhosted.org/packages/4c/05/0966840bda0380c8ae167b9043c6230202941cc90ea29c48e096964c765e/librt-0.15.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:e3b461183c5fa7681b48560f91515f53a953122fb30c71e07abc67d7ddf58c38", size = 682147, upload-time = "2026-08-07T10:48:06.555Z" }, { url = "https://files.pythonhosted.org/packages/18/af/1c47ca573c30ea47d195aec26133af522fea1104afaace028d7b32247ea8/librt-0.15.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4bbcc257e3babea20a91715c361b24554ec4e8f51aa578568afc230799fe1a19", size = 696397, upload-time = "2026-08-07T10:48:08.03Z" }, { url = "https://files.pythonhosted.org/packages/2e/0f/1aed6223d4f9f9d1171a8596ff100ea4c3f7699fea7a4ba657c3e60daa6c/librt-0.15.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b845b8d48088fad0cadc84be4b8fda63203be7e9237b71015b3925443c1f35ab", size = 722542, upload-time = "2026-08-07T10:48:09.569Z" }, { url = "https://files.pythonhosted.org/packages/c6/22/9e3a929aea456c97d69e6ef3884efea56d4807f97399471cc946baebd8af/librt-0.15.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b30e600e8f337b9bd7f39b86d9fdfedc73cc46e3d0f745931a23a234220bb7e2", size = 729709, upload-time = "2026-08-07T10:48:11.129Z" }, { url = "https://files.pythonhosted.org/packages/e9/1b/c327ef6018e3a9ca0b8e7c5eddeeb331ba8f9b76c24e126d37d0f6d62faf/librt-0.15.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:64b0c8c35aa4c4ed79896359f3e0b285cbe4e610042106500da4811c322cc108", size = 752891, upload-time = "2026-08-07T10:48:12.558Z" }, { url = "https://files.pythonhosted.org/packages/d7/d1/d5f1ea02c56930087009e39db9b70660a663e76c730b27b925d786718457/librt-0.15.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0da0d94cb802f32a0524653e7201f2cef72d5f700a5407678f5290483d4fcd08", size = 745301, upload-time = "2026-08-07T10:48:14.55Z" }, { url = "https://files.pythonhosted.org/packages/d9/3c/5f7c585d15ebb2250c73e7c0ee4e9e47be72c65d520c07ddbcdc62037674/librt-0.15.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4a6369168d371207339b1e50d4532b06a7121586141f82599505a3f315751d47", size = 747921, upload-time = "2026-08-07T10:48:16.453Z" }, { url = "https://files.pythonhosted.org/packages/7f/52/1443a446486eba966bcbca1696b472e4f210320ec42f490a47f48fbf0fdc/librt-0.15.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c434e072557ade9cbc642d052c89d031efe47d5c9614523619d0d74a02378e81", size = 727561, upload-time = "2026-08-07T10:48:18.089Z" }, { url = "https://files.pythonhosted.org/packages/79/91/2270a9380f11725cf83ce1925a5e32dd1dde2be9bba597f25c10a38644e7/librt-0.15.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7eec6a42018bc1d45763b1c162d3d2bf7c3b9a1b0ed30d3e91dcba390efefcc", size = 774417, upload-time = "2026-08-07T10:48:19.611Z" }, { url = "https://files.pythonhosted.org/packages/9e/3b/f4b1548d4f5b99186737fe27aec238e9823e8d5d23bf4df007c030689dc5/librt-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:6912fa5e635d74529ac7cdb1bdf6ca3af4453da8d1edbe0110ee1cb4ad407ebf", size = 104381, upload-time = "2026-08-07T10:48:21.048Z" }, { url = "https://files.pythonhosted.org/packages/80/b6/134afad262def1de04c0843c376d02135f1168af43f22e09a52bd8394727/librt-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8e11699ed745931c395acd3621b07062e0f840efa6935aad87a64ed0995f0915", size = 127034, upload-time = "2026-08-07T10:48:22.561Z" }, { url = "https://files.pythonhosted.org/packages/99/5f/1b6846b20572bd699c9e9ec321a5f781845bee477df2aa2a43b28bc40119/librt-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:5d2a91724463bfed4f573cd7a9fdc856d2e230d0c0e5a61416a93481dccd8605", size = 110827, upload-time = "2026-08-07T10:48:23.804Z" }, { url = "https://files.pythonhosted.org/packages/c6/44/4de9f4ddadb009a55c7758eb5736d62534a7daaf27bd71bc50e64b606b06/librt-0.15.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:8443e38dcfcfdbcf5add5118c623efd788d65ac2e25756d6251a54a06a4d0aca", size = 149843, upload-time = "2026-08-07T10:48:25.148Z" }, { url = "https://files.pythonhosted.org/packages/1f/eb/5d9ab71e30119c44094e0275f38b47dd327aea0f843a080396677029d508/librt-0.15.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:6d15a29033c57490cfe2069097c6fc4049e4e65ffbb749be7dc453b7c4c68965", size = 154510, upload-time = "2026-08-07T10:48:26.485Z" }, { url = "https://files.pythonhosted.org/packages/d0/9c/8505d1b8f5e8c19587bd03f7429993b3e9ce5c06819d856bfb11d919374c/librt-0.15.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d2c05c729b589e734c09578bf5964be48a911765484840d017bbc84f49d4c4ad", size = 497543, upload-time = "2026-08-07T10:48:28.045Z" }, { url = "https://files.pythonhosted.org/packages/1d/9a/3a8390775cb095765aded027ac9c63e7c8ea74e731498607544c6505de0e/librt-0.15.0-cp315-cp315-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:fa60887537e1d0cd2d9982269d33a709bf54b195cd2b9364fc0a758022af5bd9", size = 480452, upload-time = "2026-08-07T10:48:29.531Z" }, { url = "https://files.pythonhosted.org/packages/e7/40/258a4a7117ee915d66de5cd9b8ade65a440993161107ce3a686f1859955c/librt-0.15.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d8bc24219b24c0af375718942ab75e3544b2763085f40f965be4326734ae8328", size = 507768, upload-time = "2026-08-07T10:48:31.007Z" }, { url = "https://files.pythonhosted.org/packages/6b/c6/2f4dd296c97a0b85b98894519b279408ec9dd602d4f692b1ea0e25dee670/librt-0.15.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86a21a7bd3fe3a419512ef424cc1c020f6771d0b29cfddff36d1635a855e63f0", size = 525122, upload-time = "2026-08-07T10:48:32.7Z" }, { url = "https://files.pythonhosted.org/packages/49/dd/29eab42be13b2bf0ea8cb227135a45d44693e30a7e8b92871981ff56b82b/librt-0.15.0-cp315-cp315-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dbab647e88d90b3167b91efe7091e248653688ed4337e4f90907a722c7361bb9", size = 520371, upload-time = "2026-08-07T10:48:34.294Z" }, { url = "https://files.pythonhosted.org/packages/91/ed/4bad71adeca8fe208b775c2a35417fa5a2584c8f4791daaf89a89450fea1/librt-0.15.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:d8edcf6f550e918dca779c069b9e156385c60b406f99fc7641f32c52f7193659", size = 537258, upload-time = "2026-08-07T10:48:35.88Z" }, { url = "https://files.pythonhosted.org/packages/4c/63/59dba6143fdcc7240c54458b629f3250000a61b8945890fc9efd451b19c5/librt-0.15.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:8b62076030baa2d8b1501a46bf0e19c27a489aa90671c55665bff7887f7660b0", size = 527432, upload-time = "2026-08-07T10:48:37.466Z" }, { url = "https://files.pythonhosted.org/packages/ec/21/21a24c6a2327d8362580efebe77286bf47b0f4062ec5ea41766e609d3c7d/librt-0.15.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:d00d20d1818e82a07a0ee0aa89a98b17ed7916b92441090b683719cb20a59b6d", size = 548108, upload-time = "2026-08-07T10:48:39.384Z" }, { url = "https://files.pythonhosted.org/packages/5a/6d/fc68c89a7971418b41f9a873623ff935cb864097544c6a2f8ce491c8ef5d/librt-0.15.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:4e6ee93fc3cf848dcbf0cce2eca73d8e7dcd0cc2b6df3a529d57750b30a4c55c", size = 529681, upload-time = "2026-08-07T10:48:41.392Z" }, { url = "https://files.pythonhosted.org/packages/65/7e/c2d98766124400d722063a630b0fde38a9fc768705d37eecca15c47dc192/librt-0.15.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:32896a0af72508ea979e0acb4e4c04cbeeae04938167950d535c83c45597167d", size = 567736, upload-time = "2026-08-07T10:48:43.124Z" }, { url = "https://files.pythonhosted.org/packages/55/6c/f8c34a95e3a515c6e1c192b89511e7253c89a7760c6b500d57ffdb8d2dc8/librt-0.15.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:ec3ba415afaf951f6951b1dd16d3c8e4f540065fc382d7e70b823a79567ca374", size = 81673, upload-time = "2026-08-07T10:48:44.645Z" }, { url = "https://files.pythonhosted.org/packages/c9/9e/e23fa8e78679ec45728188650b39e8ff476c83b691c96f749217df3b1b7c/librt-0.15.0-cp315-cp315-win32.whl", hash = "sha256:d2813ba2503764f0450680c533d13df7cff9b49df1411062eded5f67db4195b9", size = 100081, upload-time = "2026-08-07T10:48:46.171Z" }, { url = "https://files.pythonhosted.org/packages/e1/dc/3eb4c5e297343f0620a55532cd7c8d764d3001fa2159212dadf480464827/librt-0.15.0-cp315-cp315-win_amd64.whl", hash = "sha256:b87d67e33afaf265262f2a66db578284b88ee2e6fcd224579cb5c15518677ad8", size = 121228, upload-time = "2026-08-07T10:48:47.631Z" }, { url = "https://files.pythonhosted.org/packages/97/70/43abce19f04e49762f8ec834c8fafee13cc40fd6b94a72a24e534febfcd0/librt-0.15.0-cp315-cp315-win_arm64.whl", hash = "sha256:713bd7df21170b982e729e46870f31d6b437bd1a9b4648cffb529bd3c2ec5c4b", size = 106487, upload-time = "2026-08-07T10:48:49.095Z" }, { url = "https://files.pythonhosted.org/packages/de/15/83f2deddb9368b8951ec8c9477269b5b9b8bd9bbf15e57402d0f38817dca/librt-0.15.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:3de789c82752730f94782a5ee518baf9c05edf85733aeaf73bb6e518755cdf54", size = 159448, upload-time = "2026-08-07T10:48:50.649Z" }, { url = "https://files.pythonhosted.org/packages/06/bf/043097353f9b3c73b583d07f6b8e552795463f4bfc8caf85e42eee50c26a/librt-0.15.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:e0b5deec9a8664eb722c797241970fd4aa1894d25fda36a1ddac0f7407606bd6", size = 161686, upload-time = "2026-08-07T10:48:52.174Z" }, { url = "https://files.pythonhosted.org/packages/f4/2a/8ae77f9719d42ce71cd708560a3557b38ac3c17a0383e57f87084de45bbe/librt-0.15.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5563302a8359bc2295bb7084d1a8ed1519df96afb30eb2aa4e0bff7b54228988", size = 710668, upload-time = "2026-08-07T10:48:53.782Z" }, { url = "https://files.pythonhosted.org/packages/61/34/c0436ea134deb9a0d6da80a396a2739a81cb31e0418f7227239e23140898/librt-0.15.0-cp315-cp315t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:22d6263b9d39d7bbb286fa791945646e3218f1be2d693e36fb630f1d0e59cd13", size = 679396, upload-time = "2026-08-07T10:48:55.645Z" }, { url = "https://files.pythonhosted.org/packages/4a/9f/001e0d99aa9250d5cd5715a9081291a20656083459f9019cda15255329e1/librt-0.15.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39ffd14646190c454f0d86e0d256b33f00a87a26ab410e619773b841d0e41416", size = 704313, upload-time = "2026-08-07T10:48:57.46Z" }, { url = "https://files.pythonhosted.org/packages/2d/53/b34fa9d0ff00f136f4d58ebb4c411ff634baed1eb412bb602a2bc8dcafcb/librt-0.15.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c47318cd3a61401452de11282242937e3e057c4fd3dbaf601e269d0928a06c0a", size = 729847, upload-time = "2026-08-07T10:48:59.231Z" }, { url = "https://files.pythonhosted.org/packages/86/ac/fa4d7a424665040e95baf480a6d523446057684b6758624c85338e8a23b2/librt-0.15.0-cp315-cp315t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a56a1d4f859a82ca5b99fc4b82c9b027b15e3c455c5cd99e7d0719f27bb20b6c", size = 742736, upload-time = "2026-08-07T10:49:01.151Z" }, { url = "https://files.pythonhosted.org/packages/8a/f1/e17a9bb5de6fb8c3186ed1a7d68d21618b027ac2d3633e03d3b6109c67ae/librt-0.15.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:077471b3182db4e17c36ae91555f36a4d2c00080b267f749bcad34a478a9a302", size = 763454, upload-time = "2026-08-07T10:49:03.039Z" }, { url = "https://files.pythonhosted.org/packages/1d/ec/ecd02cd30935b931b9cdbfed6ab5a099c51b280b4e7baa274da80978ed27/librt-0.15.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:411ca4d1b905b860ceba7570dd6717a71dedaddcc4b0f77ece710aa41ee11f8d", size = 743296, upload-time = "2026-08-07T10:49:04.941Z" }, { url = "https://files.pythonhosted.org/packages/e6/b5/b3c2b8353ce820a4854f78d19321344242f89fa71c975b71132ba9bf242a/librt-0.15.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:1256589e0b0adb31751d685a68bce29d73407ddf4ef05d4188f49d5dcf9566d9", size = 756217, upload-time = "2026-08-07T10:49:06.825Z" }, { url = "https://files.pythonhosted.org/packages/3c/52/6cc22542ba59146b05cca2a656f9ff8bb67e38e63d12c3b0cc183d837bf1/librt-0.15.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:f42b74a53e5f26a0ba0007411a7455b66c67ce4022a39cc1f56fc4efd65bcbab", size = 741934, upload-time = "2026-08-07T10:49:08.839Z" }, { url = "https://files.pythonhosted.org/packages/40/32/a04b72b1aa86e3be23b2ecff8c1aad2dcc955bd3956d6d26e7e34267e57a/librt-0.15.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:291bf73caf78b9e88d6fae9bfd693207ff7d832e2fdbe2cf8e746bc13f5f892b", size = 783763, upload-time = "2026-08-07T10:49:10.661Z" }, { url = "https://files.pythonhosted.org/packages/6c/f0/89eb11dffbe9279ff37144dec786927314502ae0b114f1449dc78c458aab/librt-0.15.0-cp315-cp315t-win32.whl", hash = "sha256:c16d15ee371643ab48dc8248a3e680ebbeca573a13af2c3dd0c985b142d77162", size = 104313, upload-time = "2026-08-07T10:49:12.305Z" }, { url = "https://files.pythonhosted.org/packages/6d/4a/1f1978c200f563beda63c36adff2d65bbecb81e365e8e69e572f5f70fbc6/librt-0.15.0-cp315-cp315t-win_amd64.whl", hash = "sha256:dbd605739f228912dc49027cb764456b9757750bdc2b6b7773164db7096c6fd1", size = 126889, upload-time = "2026-08-07T10:49:13.881Z" }, { url = "https://files.pythonhosted.org/packages/38/a6/800800bfed7b1fb10fc3f3d557785c3854e80d3f7a9800d784b176a1fc2d/librt-0.15.0-cp315-cp315t-win_arm64.whl", hash = "sha256:84d244b00604d17df3fc7736c327892d6bba66181254aa4087be807b6c342bdc", size = 110700, upload-time = "2026-08-07T10:49:15.499Z" }, ] [[package]] name = "markdown-it-py" version = "3.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ { name = "mdurl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, ] [[package]] name = "markdown-it-py" version = "4.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", ] dependencies = [ { name = "mdurl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, ] [[package]] name = "markupsafe" version = "3.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" }, { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" }, { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" }, { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" }, { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" }, { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" }, { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" }, { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" }, { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" }, { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" }, { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" }, { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" }, { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" }, { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" }, { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" }, { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" }, { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" }, { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" }, { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" }, { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, ] [[package]] name = "mccabe" version = "0.7.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, ] [[package]] name = "mdit-py-plugins" version = "0.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655, upload-time = "2025-08-11T07:25:49.083Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f", size = 57205, upload-time = "2025-08-11T07:25:47.597Z" }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] [[package]] name = "more-itertools" version = "10.6.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/88/3b/7fa1fe835e2e93fd6d7b52b2f95ae810cf5ba133e1845f726f5a992d62c2/more-itertools-10.6.0.tar.gz", hash = "sha256:2cd7fad1009c31cc9fb6a035108509e6547547a7a738374f10bd49a09eb3ee3b", size = 125009, upload-time = "2025-01-14T16:22:47.626Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/23/62/0fe302c6d1be1c777cab0616e6302478251dfbf9055ad426f5d0def75c89/more_itertools-10.6.0-py3-none-any.whl", hash = "sha256:6eb054cb4b6db1473f6e15fcc676a08e4732548acd47c708f0e179c2c7c01e89", size = 63038, upload-time = "2025-01-14T16:22:46.014Z" }, ] [[package]] name = "mypy" version = "2.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ast-serialize" }, { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, { name = "mypy-extensions" }, { name = "pathspec" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/82/6a/878cc1097d4035f82bd516658d0c528d2a9955bc7b363afcbd0b07fea11b/mypy-2.3.1.tar.gz", hash = "sha256:47c1b1207258513a9d93495f69c8be9de73916186f0e52703e8c461b7a623419", size = 3992554, upload-time = "2026-08-15T03:03:38.549Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/eb/b9/de8f67e12d721cdcc8ba6cfc440b989a4ba4dfabe4402ae94dfdd8bb30a4/mypy-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:57a936373fc690c43a8cd7e7e12a35148e4ec5aa7698ad7fc0a9f918bdc5be41", size = 14015541, upload-time = "2026-08-15T03:01:53.104Z" }, { url = "https://files.pythonhosted.org/packages/f1/8a/9e746ab012c67ed8ea3232a613716c306ee8c0b5682c80d8103b4f04568e/mypy-2.3.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d00d769056bde2f4e69c175071eba45cfb44fa1ed92bdfbfe64a93e0543b0cf0", size = 14248142, upload-time = "2026-08-15T03:02:43.201Z" }, { url = "https://files.pythonhosted.org/packages/f7/5c/c99ff2d8d0e2c53393e32dfe22d9aa43a5d959d30db46c786dafd24527d3/mypy-2.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2166b29228835e1f88ff411e96639e6ca3c7fdde84b62ec211f70f86b4051167", size = 15193309, upload-time = "2026-08-15T03:01:28.714Z" }, { url = "https://files.pythonhosted.org/packages/64/39/124638f745243faae1ff4b37d5426fe41c0f0454535edc82fe8102b56a3c/mypy-2.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:83d36c2924df7426333abe7faf4724a7e1aab0d9fd41625e81b4683034b80c13", size = 15498246, upload-time = "2026-08-15T03:02:46.29Z" }, { url = "https://files.pythonhosted.org/packages/b2/83/31c0781e243836505c0fb5f4e865487d6df1023e4ad959f4ebd4b84a0226/mypy-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:f12fdb70459d0060dea40b29e52163a961b156106d68d57882a6a9f648983a53", size = 11155028, upload-time = "2026-08-15T03:01:39.08Z" }, { url = "https://files.pythonhosted.org/packages/a0/ab/bc2eb0129e72d7d7d93d5e981a78084a9abefda7efa732a7e02f97d6e27d/mypy-2.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:e099200a1b1b1223a4951f0a90cbff1b8c91b250ba599dab1f7217a628144d90", size = 10151438, upload-time = "2026-08-15T03:02:19.04Z" }, { url = "https://files.pythonhosted.org/packages/a4/be/c624d4241484f37dc62839e177ab607a9b8b3e96f0866544ca99e8e41d51/mypy-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94f04929f1c44c35fb0061e912087edaf504acede963a4a7d00680bd089d8531", size = 13936739, upload-time = "2026-08-15T03:03:26.475Z" }, { url = "https://files.pythonhosted.org/packages/53/84/e3cf72f90dce5960871c82551c8fba6da05fc1018f79be41c047bd126bdd/mypy-2.3.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5d716048611e85ca9eefb2e1baa5d73ede389b5820ded260ea27c757d667af8", size = 14166460, upload-time = "2026-08-15T03:01:50.565Z" }, { url = "https://files.pythonhosted.org/packages/4a/ff/6b97d58aa0f79a5ab9b472db1f6d6df1b11a51d74d0c08ab3760d3a613ba/mypy-2.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b091a455111214cb5c9d54a57b9618e9a49f9fe2a42e4e1ac86e9d104ed96ce8", size = 15100476, upload-time = "2026-08-15T03:03:12.079Z" }, { url = "https://files.pythonhosted.org/packages/da/f0/cbb4b7d2ae3ac635f6b4f2d9b04070b8a92edf50da599d3b39e5ed109001/mypy-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df12e20c9efd614738c71b390007ecd0181125afc4ccafca04d78a1d2eed2c01", size = 15347826, upload-time = "2026-08-15T03:03:02.856Z" }, { url = "https://files.pythonhosted.org/packages/5f/10/91dcdc6f8d43fc08e6a06ab1f9732f3abaaf835ac1b2e67b9dff56910855/mypy-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:52eaf3a155f35cf80b40220288c861eb45f14a2340c1f6cbfbdb0feff32879d1", size = 11142615, upload-time = "2026-08-15T03:03:36.316Z" }, { url = "https://files.pythonhosted.org/packages/3d/8a/28d54535bf4b9aa43b2d8918c2ef660378b9f66b23d78dcee052744ae622/mypy-2.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9b4eacbee8a69836c06eff6d0dd4e134a07c2b047755b30c08625fe214f322c6", size = 10141145, upload-time = "2026-08-15T03:03:07.406Z" }, { url = "https://files.pythonhosted.org/packages/85/da/d6effc4f808a842d91edc22535dc9e799d2ff6e91449168b7f47a0771f54/mypy-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a32bbbb940af990d3be0b8af321c7b6815bb1b3b48142fe7459b9cc5f58959ff", size = 14047547, upload-time = "2026-08-15T03:02:57.707Z" }, { url = "https://files.pythonhosted.org/packages/e4/e6/478229701dab76f26485fc8ff5d6f241f393da22447400bbc56f6946aebe/mypy-2.3.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff715e45b2231a8e85de1d163d1b42791e4d7aab8f5145f85fee1b710b735aff", size = 14216515, upload-time = "2026-08-15T03:01:26.496Z" }, { url = "https://files.pythonhosted.org/packages/8d/fe/7c42327a3b21e84681f691982cbfe43f334a3685f3b683b72c376476c4fa/mypy-2.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:858fc57d3d91fa728e33e7ad71def60fc6272694607b306cd3292db53ae39080", size = 15307789, upload-time = "2026-08-15T03:03:31.62Z" }, { url = "https://files.pythonhosted.org/packages/59/f4/7e597edbe01b5a56fa958ce541302dcaabfed979966f1dffedbea0ea0fc2/mypy-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:851833db876e7b650f93719c74b7879a08e338979c96054fdfc3bfd90a486355", size = 15548831, upload-time = "2026-08-15T03:03:15.55Z" }, { url = "https://files.pythonhosted.org/packages/a3/52/cb31e084bc0314a1e384bdd677a4b80e55af04ccac077545e2238b9d320a/mypy-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:4c5095a327483591c94e0c8d3ef9e50d4ab1369b541eae007c1f23bc2a41f6bb", size = 11226359, upload-time = "2026-08-15T03:03:29.002Z" }, { url = "https://files.pythonhosted.org/packages/7a/47/88fcf6217b43fa2da81a8c2611370af18141536a4f0294bbf98b457d456d/mypy-2.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:bbfe022634a2a195406bd469e888d2eaf193b02ba7e607391cd7640374aaae3b", size = 10214707, upload-time = "2026-08-15T03:02:48.807Z" }, { url = "https://files.pythonhosted.org/packages/de/cf/862010ee800ca9c2bd0c4c0dacf0f092e5411824a09b8f97ad4be8fe250e/mypy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:114dff494000f18bd10d5d95d84b8567b26da60279ecbe838131841df20e635d", size = 13964542, upload-time = "2026-08-15T03:02:21.43Z" }, { url = "https://files.pythonhosted.org/packages/75/5a/3f3a2107b41e3e92e617e25daaee121413b91e9784bea733131ed4fecc5d/mypy-2.3.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8637731bb5eee3671eb2c3200827aa3564ed8a9309ecee4d1afe77e6d031bdb", size = 14168922, upload-time = "2026-08-15T03:03:00.351Z" }, { url = "https://files.pythonhosted.org/packages/8b/41/04dc4fe7e63d7820fa4eff272e95157d30cbea921388f3ab3fe77794cd0b/mypy-2.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c80fbc405ed8020f5ff3802dc18cf060197bcdd3fbdd6a26ef2fd34dfdd5226", size = 15244791, upload-time = "2026-08-15T03:02:31.089Z" }, { url = "https://files.pythonhosted.org/packages/96/fc/c3053b26b9054949285aa868cb6af8c10e7591541cacd79c5dcc06a1fcf9/mypy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:84081f538ce27375045c02e3d7f81bd11d853400621ae245d87ce7b6c420ec74", size = 15501627, upload-time = "2026-08-15T03:03:34.128Z" }, { url = "https://files.pythonhosted.org/packages/70/4e/d77daab008bbc4e5001374d7928f4a260d28f0e6747af444fc4763f7a310/mypy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:e9144ac16fde007096f9563eb2041b4433c2d705c4218edeb79e7e9d01035ee6", size = 11243961, upload-time = "2026-08-15T03:02:11.952Z" }, { url = "https://files.pythonhosted.org/packages/f0/f8/7eb68c136e4abd30569fe31ef2bfcb7eceae9952cab80017c04cd09f5d0c/mypy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:77ad9529e67dca28e511f5cd5671436584ce91f6d3bac159a353158187b986ac", size = 10213219, upload-time = "2026-08-15T03:02:26.361Z" }, { url = "https://files.pythonhosted.org/packages/be/c4/42a49d44aeff804edf1b19acce0b49e8bd1a9c57dee9605dd8d980aa43d7/mypy-2.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:192abaedf75da1bc0b1cef104927e70ec49c1ef0031cc4825c7ee10a438ed24d", size = 13986778, upload-time = "2026-08-15T03:01:33.69Z" }, { url = "https://files.pythonhosted.org/packages/45/13/9331fd2dfed7194d66c5304072894a8be3e51e9deda6863c1eceaa35a43d/mypy-2.3.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf678dffd16efcda2c15cbd30e9ecc0081388e29ea23687a88e686ed92638dc3", size = 14188467, upload-time = "2026-08-15T03:02:40.554Z" }, { url = "https://files.pythonhosted.org/packages/78/f7/f4a34edab45667c5465855dc585a20e87978ffa8aee711445b7239d120c6/mypy-2.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e036f06b41630f4c8a1d48f9ac6aa26acc65f8be089973f5519da643318f03f", size = 15225538, upload-time = "2026-08-15T03:03:09.761Z" }, { url = "https://files.pythonhosted.org/packages/40/05/534b3590757bd05794f73e07f6666c2a77b8597ffed795c94ce570096aa0/mypy-2.3.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:71af9c8a894e862b58e92abb08e53b05a384a1e5e5d6dc7cda59126211a53d82", size = 15480805, upload-time = "2026-08-15T03:01:41.134Z" }, { url = "https://files.pythonhosted.org/packages/55/da/bdfba852e2562f599624af5bb7d29e36b0b4f526f2b8bac85efe0dd1803d/mypy-2.3.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:3c80cd23d85368bdd9f37d5231dfd97d35bcbf5bf41af96ef3a9b078ad1957f9", size = 7761712, upload-time = "2026-08-15T03:02:36.008Z" }, { url = "https://files.pythonhosted.org/packages/98/31/60fc64a74cdba4f2a5d642d32317993e479163e1ac7d91b695e5d15e2264/mypy-2.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:4956f34d145e145562a0a0bf367f642bbc85c04ec2baf47ae015947c3169a85d", size = 11423968, upload-time = "2026-08-15T03:02:06.931Z" }, { url = "https://files.pythonhosted.org/packages/a9/23/eb5950b24cd26ba3b78f87707a275568d633c77dae8e61c9661be6055ca6/mypy-2.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:cfb12e360242d23d91f5e978d94f58ea66acf5804c4fb6f2f794a20d4cb1b595", size = 10399323, upload-time = "2026-08-15T03:02:33.671Z" }, { url = "https://files.pythonhosted.org/packages/82/c7/f80f4e46c0b9a00eb5f78a79d49dda8bdf56a5230f7257fb33e76be04da7/mypy-2.3.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e5f1c50bb05b64e2026b52867e8d21106f01313c744a2c4ecc34c90d12e8d6e2", size = 15121308, upload-time = "2026-08-15T03:01:46.053Z" }, { url = "https://files.pythonhosted.org/packages/5d/74/9b04f17c7074cc5188f02fb63a2ca1d43fedf479e84fe3091c39061a1d7f/mypy-2.3.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:667196b352f4cf304ded4c10f90cfc179263a1acfb3cdcfa984bdfd340d498bc", size = 15536590, upload-time = "2026-08-15T03:01:35.941Z" }, { url = "https://files.pythonhosted.org/packages/26/04/c837ef6208e567774e2ed1f863f8ba6ec4817b1b6dd426315e5d559b6ec9/mypy-2.3.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9c53e395c12cad2c6d4b67d5da7c6057638a132d85c08b73646b18f802a0045", size = 16791074, upload-time = "2026-08-15T03:01:31.073Z" }, { url = "https://files.pythonhosted.org/packages/37/68/48730230afa45192d5bd429a6a2ff24a6f8dedda90fdf2b221792b54518f/mypy-2.3.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:18162b128c3f9c703cd35f5537446900b0d21a2549aa7a95d21380d2ef643fb0", size = 17069183, upload-time = "2026-08-15T03:02:28.566Z" }, { url = "https://files.pythonhosted.org/packages/1c/ea/ca23fc9c20eeda09a15c9cbcf50015d0e73f409f6ead059e42aa69a608ff/mypy-2.3.1-cp314-cp314t-win_amd64.whl", hash = "sha256:30c0477d4aab7b7f39c8397dc877f2c96b9fe5588ec379f372c56eb63d599f63", size = 12154679, upload-time = "2026-08-15T03:02:04.809Z" }, { url = "https://files.pythonhosted.org/packages/3b/67/8d982126034990869466f73b8db80dcb2234a7ac39b4dad093e047a79835/mypy-2.3.1-cp314-cp314t-win_arm64.whl", hash = "sha256:6941ab3619377bc3f32ca02876b07d27f216f5201604b664d3937ea0fdd23bb4", size = 10969159, upload-time = "2026-08-15T03:02:38.152Z" }, { url = "https://files.pythonhosted.org/packages/ee/f7/41e7f2d8117fbc7a7587286162ffe2f688984b69c46ed63cf5f2e4fc3bae/mypy-2.3.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:6f041a6de52c9217ca125e78ba0a335cb7fd98a1c0580978e49ab2b126f70b57", size = 13990694, upload-time = "2026-08-15T03:03:21.919Z" }, { url = "https://files.pythonhosted.org/packages/06/85/8f665811a0c8f3bf6fa1d9acd665ec2d97a2bcc453ae68dcd92340941cd6/mypy-2.3.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5159ae60f5dbc3a498af5ba8365505808ac8031bc63f9e00304ad545d40bdd9b", size = 14203518, upload-time = "2026-08-15T03:01:48.455Z" }, { url = "https://files.pythonhosted.org/packages/2d/82/91b866c8546b120bff83b73a439d90d2d63ef3aff113599e6b8e4d566848/mypy-2.3.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47a8a7a0a7f6f6e63995c0ac36fa0c07b127413fdc81f0439b7f3dccafd33561", size = 15220224, upload-time = "2026-08-15T03:01:23.577Z" }, { url = "https://files.pythonhosted.org/packages/c8/78/c226c99208ee40de7c768369fa533f933afa003dfdc606ff021450724e91/mypy-2.3.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:2329c0501293d4e1f33bc15d04d6304d65a1cdda967ee93a05c1e681a3923133", size = 15501512, upload-time = "2026-08-15T03:02:09.453Z" }, { url = "https://files.pythonhosted.org/packages/a9/e7/7cfb3f106c393979f4cc37ad6c0586044d50401e3c35b0c003e4f3ba6bc9/mypy-2.3.1-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:bb26deed807bdb0457cf3e3f1cd7c4a1cf9d66864eaf1b4a61e06805d4c6b1f9", size = 7761913, upload-time = "2026-08-15T03:01:55.65Z" }, { url = "https://files.pythonhosted.org/packages/99/3c/52affefa273b97939a1f474ae4a349c8718635c15b941112dfab4291b0c1/mypy-2.3.1-cp315-cp315-win_amd64.whl", hash = "sha256:375d7013876a8233b2d05be185bfa09f689696cd999ce8b1cfe6acac5c80e8a3", size = 11422533, upload-time = "2026-08-15T03:03:24.101Z" }, { url = "https://files.pythonhosted.org/packages/2a/b7/75643e70c72a5b346d8a9b1543c967ea8824df2ee3fb7ccba652c272b7bb/mypy-2.3.1-cp315-cp315-win_arm64.whl", hash = "sha256:586b3612214cceabb3c0f588c97e7d1e535393f06a60e912e994f6b3ace97523", size = 10397931, upload-time = "2026-08-15T03:02:55.265Z" }, { url = "https://files.pythonhosted.org/packages/10/ce/53be21f2d4adfcd26f63f1184a13ed797015ab463853f117e2e11e4d726f/mypy-2.3.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:ef0c6335cda9d807f8193d8ff6204a72bc909fa9882aacbca14f43cdb7188306", size = 15118669, upload-time = "2026-08-15T03:02:51.479Z" }, { url = "https://files.pythonhosted.org/packages/62/43/20de757cd42989d291a17fad607742c4c74e875ce5cea00e5a5225020ac1/mypy-2.3.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e598c8c66401d26b150872154a286e6d484cf2789c3bb28a7556806298423021", size = 15545627, upload-time = "2026-08-15T03:03:05.132Z" }, { url = "https://files.pythonhosted.org/packages/7e/fc/092bdf77ad280eaf501422f0f3b966012b528076cc13e41a774861c907d1/mypy-2.3.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eda22fd4efa9dcd39331d1dede9b5b8b8a7fd69af07592e778433da98610d29e", size = 16764157, upload-time = "2026-08-15T03:02:23.958Z" }, { url = "https://files.pythonhosted.org/packages/94/5c/c94c4d62d909b07f552d0d9356d7acc943825558e602a64822ffa2231536/mypy-2.3.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:2a0ba2e57847849fb0d1fcdabb32786d223095ed8bc121dfe322bcdb3d9c46bc", size = 17073258, upload-time = "2026-08-15T03:02:14.573Z" }, { url = "https://files.pythonhosted.org/packages/c0/f7/511a88b89e478053c02d22039bb8f3ce4183efe8fd7a4f0a5910a8bb0a32/mypy-2.3.1-cp315-cp315t-win_amd64.whl", hash = "sha256:3f7e865dd51f235f60a2dbcd8728a1c095f5ca28f095d48a725b84cd935735c4", size = 12135505, upload-time = "2026-08-15T03:02:16.714Z" }, { url = "https://files.pythonhosted.org/packages/71/bf/02573b56964ecb0f7c644f915f53c325ae15c3faec521c5adf11599a32df/mypy-2.3.1-cp315-cp315t-win_arm64.whl", hash = "sha256:8ad80807dc3ab8ea978b1b2b6e4a657194ace1d4ef03e0e731aff1abd517da29", size = 10962647, upload-time = "2026-08-15T03:01:43.712Z" }, { url = "https://files.pythonhosted.org/packages/8e/41/9675c7a1e78edecfba0b79e587a52594c56e189368261dc7b3a7fffb9527/mypy-2.3.1-py3-none-any.whl", hash = "sha256:6ed5c7e3419083268e5c9258bd1c1ef91af44a9e89374dbcaf37b775716e72eb", size = 2754338, upload-time = "2026-08-15T03:02:53.4Z" }, ] [[package]] name = "mypy-extensions" version = "1.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] [[package]] name = "myst-parser" version = "4.0.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" } }, { name = "jinja2" }, { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" } }, { name = "mdit-py-plugins" }, { name = "pyyaml", version = "6.0.2", source = { registry = "https://pypi.org/simple" } }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985, upload-time = "2025-02-12T10:53:03.833Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579, upload-time = "2025-02-12T10:53:02.078Z" }, ] [[package]] name = "myst-parser" version = "5.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", ] dependencies = [ { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } }, { name = "jinja2" }, { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" } }, { name = "mdit-py-plugins" }, { name = "pyyaml", version = "6.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, { name = "pyyaml", version = "6.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/fa/7b45eef11b7971f0beb29d27b7bfe0d747d063aa29e170d9edd004733c8a/myst_parser-5.0.0.tar.gz", hash = "sha256:f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a", size = 98535, upload-time = "2026-01-15T09:08:18.036Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d3/ac/686789b9145413f1a61878c407210e41bfdb097976864e0913078b24098c/myst_parser-5.0.0-py3-none-any.whl", hash = "sha256:ab31e516024918296e169139072b81592336f2fef55b8986aa31c9f04b5f7211", size = 84533, upload-time = "2026-01-15T09:08:16.788Z" }, ] [[package]] name = "natsort" version = "8.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575, upload-time = "2023-06-20T04:17:19.925Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268, upload-time = "2023-06-20T04:17:17.522Z" }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] name = "pathspec" version = "1.0.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, ] [[package]] name = "pbr" version = "6.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/35/80cf8f6a4f34017a7fe28242dc45161a1baa55c41563c354d8147e8358b2/pbr-6.1.0.tar.gz", hash = "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24", size = 124032, upload-time = "2024-08-27T13:18:17.792Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/1d/44/6a65ecd630393d47ad3e7d5354768cb7f9a10b3a0eb2cd8c6f52b28211ee/pbr-6.1.0-py2.py3-none-any.whl", hash = "sha256:a776ae228892d8013649c0aeccbb3d5f99ee15e005a4cbb7e61d55a067b28a2a", size = 108529, upload-time = "2024-08-27T13:18:16.26Z" }, ] [[package]] name = "pep8-naming" version = "0.15.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flake8" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8d/59/c32862134635ba231d45f1711035550dc38246396c27269a4cde4bfe18d2/pep8_naming-0.15.1.tar.gz", hash = "sha256:f6f4a499aba2deeda93c1f26ccc02f3da32b035c8b2db9696b730ef2c9639d29", size = 17640, upload-time = "2025-05-05T20:43:12.555Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a6/78/25281540f1121acaa78926f599a17ce102b8971bc20b096fa7fb6b5b59c1/pep8_naming-0.15.1-py3-none-any.whl", hash = "sha256:eb63925e7fd9e028c7f7ee7b1e413ec03d1ee5de0e627012102ee0222c273c86", size = 9561, upload-time = "2025-05-05T20:43:11.626Z" }, ] [[package]] name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, ] [[package]] name = "platformdirs" version = "4.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, ] [[package]] name = "pluggy" version = "1.6.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] name = "ptyprocess" version = "0.7.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, ] [[package]] name = "py-cpuinfo" version = "9.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/37/a8/d832f7293ebb21690860d2e01d8115e5ff6f2ae8bbdc953f0eb0fa4bd2c7/py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690", size = 104716, upload-time = "2022-10-25T20:38:06.303Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5", size = 22335, upload-time = "2022-10-25T20:38:27.636Z" }, ] [[package]] name = "pycodestyle" version = "2.14.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/11/e0/abfd2a0d2efe47670df87f3e3a0e2edda42f055053c85361f19c0e2c1ca8/pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783", size = 39472, upload-time = "2025-06-20T18:49:48.75Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d", size = 31594, upload-time = "2025-06-20T18:49:47.491Z" }, ] [[package]] name = "pycparser" version = "2.22" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, ] [[package]] name = "pydantic" version = "2.12.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, { name = "typing-inspection" }, ] sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, ] [[package]] name = "pydantic-core" version = "2.41.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, ] [[package]] name = "pydantic-extra-types" version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226, upload-time = "2025-12-31T16:18:27.944Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296, upload-time = "2025-12-31T16:18:26.38Z" }, ] [[package]] name = "pyflakes" version = "3.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/45/dc/fd034dc20b4b264b3d015808458391acbf9df40b1e54750ef175d39180b1/pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58", size = 64669, upload-time = "2025-06-20T18:45:27.834Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c2/2f/81d580a0fb83baeb066698975cb14a618bdbed7720678566f1b046a95fe8/pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f", size = 63551, upload-time = "2025-06-20T18:45:26.937Z" }, ] [[package]] name = "pygments" version = "2.20.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] [[package]] name = "pyproject-api" version = "1.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/19/fd/437901c891f58a7b9096511750247535e891d2d5a5a6eefbc9386a2b41d5/pyproject_api-1.9.1.tar.gz", hash = "sha256:43c9918f49daab37e302038fc1aed54a8c7a91a9fa935d00b9a485f37e0f5335", size = 22710, upload-time = "2025-05-12T14:41:58.025Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ef/e6/c293c06695d4a3ab0260ef124a74ebadba5f4c511ce3a4259e976902c00b/pyproject_api-1.9.1-py3-none-any.whl", hash = "sha256:7d6238d92f8962773dd75b5f0c4a6a27cce092a14b623b811dba656f3b628948", size = 13158, upload-time = "2025-05-12T14:41:56.217Z" }, ] [[package]] name = "pytest" version = "9.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, ] [[package]] name = "pytest-benchmark" version = "5.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "py-cpuinfo" }, { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/39/d0/a8bd08d641b393db3be3819b03e2d9bb8760ca8479080a26a5f6e540e99c/pytest-benchmark-5.1.0.tar.gz", hash = "sha256:9ea661cdc292e8231f7cd4c10b0319e56a2118e2c09d9f50e1b3d150d2aca105", size = 337810, upload-time = "2024-10-30T11:51:48.521Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9e/d6/b41653199ea09d5969d4e385df9bbfd9a100f28ca7e824ce7c0a016e3053/pytest_benchmark-5.1.0-py3-none-any.whl", hash = "sha256:922de2dfa3033c227c96da942d1878191afa135a29485fb942e85dff1c592c89", size = 44259, upload-time = "2024-10-30T11:51:45.94Z" }, ] [[package]] name = "pytokens" version = "0.4.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5", size = 161522, upload-time = "2026-01-30T01:02:50.393Z" }, { url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe", size = 246945, upload-time = "2026-01-30T01:02:52.399Z" }, { url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c", size = 259525, upload-time = "2026-01-30T01:02:53.737Z" }, { url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7", size = 262693, upload-time = "2026-01-30T01:02:54.871Z" }, { url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2", size = 103567, upload-time = "2026-01-30T01:02:56.414Z" }, { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" }, { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" }, { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" }, { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" }, { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" }, { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, ] [[package]] name = "pywin32-ctypes" version = "0.2.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, ] [[package]] name = "pyyaml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", "python_full_version < '3.11'", ] sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, ] [[package]] name = "pyyaml" version = "6.0.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version == '3.14.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] [[package]] name = "pyyaml-ft" version = "8.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/5e/eb/5a0d575de784f9a1f94e2b1288c6886f13f34185e13117ed530f32b6f8a8/pyyaml_ft-8.0.0.tar.gz", hash = "sha256:0c947dce03954c7b5d38869ed4878b2e6ff1d44b08a0d84dc83fdad205ae39ab", size = 141057, upload-time = "2025-06-10T15:32:15.613Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/68/ba/a067369fe61a2e57fb38732562927d5bae088c73cb9bb5438736a9555b29/pyyaml_ft-8.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c1306282bc958bfda31237f900eb52c9bedf9b93a11f82e1aab004c9a5657a6", size = 187027, upload-time = "2025-06-10T15:31:48.722Z" }, { url = "https://files.pythonhosted.org/packages/ad/c5/a3d2020ce5ccfc6aede0d45bcb870298652ac0cf199f67714d250e0cdf39/pyyaml_ft-8.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:30c5f1751625786c19de751e3130fc345ebcba6a86f6bddd6e1285342f4bbb69", size = 176146, upload-time = "2025-06-10T15:31:50.584Z" }, { url = "https://files.pythonhosted.org/packages/e3/bb/23a9739291086ca0d3189eac7cd92b4d00e9fdc77d722ab610c35f9a82ba/pyyaml_ft-8.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fa992481155ddda2e303fcc74c79c05eddcdbc907b888d3d9ce3ff3e2adcfb0", size = 746792, upload-time = "2025-06-10T15:31:52.304Z" }, { url = "https://files.pythonhosted.org/packages/5f/c2/e8825f4ff725b7e560d62a3609e31d735318068e1079539ebfde397ea03e/pyyaml_ft-8.0.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cec6c92b4207004b62dfad1f0be321c9f04725e0f271c16247d8b39c3bf3ea42", size = 786772, upload-time = "2025-06-10T15:31:54.712Z" }, { url = "https://files.pythonhosted.org/packages/35/be/58a4dcae8854f2fdca9b28d9495298fd5571a50d8430b1c3033ec95d2d0e/pyyaml_ft-8.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06237267dbcab70d4c0e9436d8f719f04a51123f0ca2694c00dd4b68c338e40b", size = 778723, upload-time = "2025-06-10T15:31:56.093Z" }, { url = "https://files.pythonhosted.org/packages/86/ed/fed0da92b5d5d7340a082e3802d84c6dc9d5fa142954404c41a544c1cb92/pyyaml_ft-8.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8a7f332bc565817644cdb38ffe4739e44c3e18c55793f75dddb87630f03fc254", size = 758478, upload-time = "2025-06-10T15:31:58.314Z" }, { url = "https://files.pythonhosted.org/packages/f0/69/ac02afe286275980ecb2dcdc0156617389b7e0c0a3fcdedf155c67be2b80/pyyaml_ft-8.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d10175a746be65f6feb86224df5d6bc5c049ebf52b89a88cf1cd78af5a367a8", size = 799159, upload-time = "2025-06-10T15:31:59.675Z" }, { url = "https://files.pythonhosted.org/packages/4e/ac/c492a9da2e39abdff4c3094ec54acac9747743f36428281fb186a03fab76/pyyaml_ft-8.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:58e1015098cf8d8aec82f360789c16283b88ca670fe4275ef6c48c5e30b22a96", size = 158779, upload-time = "2025-06-10T15:32:01.029Z" }, { url = "https://files.pythonhosted.org/packages/5d/9b/41998df3298960d7c67653669f37710fa2d568a5fc933ea24a6df60acaf6/pyyaml_ft-8.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e64fa5f3e2ceb790d50602b2fd4ec37abbd760a8c778e46354df647e7c5a4ebb", size = 191331, upload-time = "2025-06-10T15:32:02.602Z" }, { url = "https://files.pythonhosted.org/packages/0f/16/2710c252ee04cbd74d9562ebba709e5a284faeb8ada88fcda548c9191b47/pyyaml_ft-8.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8d445bf6ea16bb93c37b42fdacfb2f94c8e92a79ba9e12768c96ecde867046d1", size = 182879, upload-time = "2025-06-10T15:32:04.466Z" }, { url = "https://files.pythonhosted.org/packages/9a/40/ae8163519d937fa7bfa457b6f78439cc6831a7c2b170e4f612f7eda71815/pyyaml_ft-8.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c56bb46b4fda34cbb92a9446a841da3982cdde6ea13de3fbd80db7eeeab8b49", size = 811277, upload-time = "2025-06-10T15:32:06.214Z" }, { url = "https://files.pythonhosted.org/packages/f9/66/28d82dbff7f87b96f0eeac79b7d972a96b4980c1e445eb6a857ba91eda00/pyyaml_ft-8.0.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dab0abb46eb1780da486f022dce034b952c8ae40753627b27a626d803926483b", size = 831650, upload-time = "2025-06-10T15:32:08.076Z" }, { url = "https://files.pythonhosted.org/packages/e8/df/161c4566facac7d75a9e182295c223060373d4116dead9cc53a265de60b9/pyyaml_ft-8.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd48d639cab5ca50ad957b6dd632c7dd3ac02a1abe0e8196a3c24a52f5db3f7a", size = 815755, upload-time = "2025-06-10T15:32:09.435Z" }, { url = "https://files.pythonhosted.org/packages/05/10/f42c48fa5153204f42eaa945e8d1fd7c10d6296841dcb2447bf7da1be5c4/pyyaml_ft-8.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:052561b89d5b2a8e1289f326d060e794c21fa068aa11255fe71d65baf18a632e", size = 810403, upload-time = "2025-06-10T15:32:11.051Z" }, { url = "https://files.pythonhosted.org/packages/d5/d2/e369064aa51009eb9245399fd8ad2c562bd0bcd392a00be44b2a824ded7c/pyyaml_ft-8.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3bb4b927929b0cb162fb1605392a321e3333e48ce616cdcfa04a839271373255", size = 835581, upload-time = "2025-06-10T15:32:12.897Z" }, { url = "https://files.pythonhosted.org/packages/c0/28/26534bed77109632a956977f60d8519049f545abc39215d086e33a61f1f2/pyyaml_ft-8.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:de04cfe9439565e32f178106c51dd6ca61afaa2907d143835d501d84703d3793", size = 171579, upload-time = "2025-06-10T15:32:14.34Z" }, ] [[package]] name = "requests" version = "2.33.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "charset-normalizer" }, { name = "idna" }, { name = "urllib3" }, ] sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" }, ] [[package]] name = "rich" version = "13.9.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pygments" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload-time = "2024-11-01T16:43:55.817Z" }, ] [[package]] name = "roman-numerals" version = "4.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/41dc953bbeb056c17d5f7a519f50fdf010bd0553be2d630bc69d1e022703/roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2", size = 9077, upload-time = "2025-12-17T18:25:34.381Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676, upload-time = "2025-12-17T18:25:33.098Z" }, ] [[package]] name = "ruff" version = "0.13.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/c7/8e/f9f9ca747fea8e3ac954e3690d4698c9737c23b51731d02df999c150b1c9/ruff-0.13.3.tar.gz", hash = "sha256:5b0ba0db740eefdfbcce4299f49e9eaefc643d4d007749d77d047c2bab19908e", size = 5438533, upload-time = "2025-10-02T19:29:31.582Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d2/33/8f7163553481466a92656d35dea9331095122bb84cf98210bef597dd2ecd/ruff-0.13.3-py3-none-linux_armv6l.whl", hash = "sha256:311860a4c5e19189c89d035638f500c1e191d283d0cc2f1600c8c80d6dcd430c", size = 12484040, upload-time = "2025-10-02T19:28:49.199Z" }, { url = "https://files.pythonhosted.org/packages/b0/b5/4a21a4922e5dd6845e91896b0d9ef493574cbe061ef7d00a73c61db531af/ruff-0.13.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2bdad6512fb666b40fcadb65e33add2b040fc18a24997d2e47fee7d66f7fcae2", size = 13122975, upload-time = "2025-10-02T19:28:52.446Z" }, { url = "https://files.pythonhosted.org/packages/40/90/15649af836d88c9f154e5be87e64ae7d2b1baa5a3ef317cb0c8fafcd882d/ruff-0.13.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fc6fa4637284708d6ed4e5e970d52fc3b76a557d7b4e85a53013d9d201d93286", size = 12346621, upload-time = "2025-10-02T19:28:54.712Z" }, { url = "https://files.pythonhosted.org/packages/a5/42/bcbccb8141305f9a6d3f72549dd82d1134299177cc7eaf832599700f95a7/ruff-0.13.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c9e6469864f94a98f412f20ea143d547e4c652f45e44f369d7b74ee78185838", size = 12574408, upload-time = "2025-10-02T19:28:56.679Z" }, { url = "https://files.pythonhosted.org/packages/ce/19/0f3681c941cdcfa2d110ce4515624c07a964dc315d3100d889fcad3bfc9e/ruff-0.13.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5bf62b705f319476c78891e0e97e965b21db468b3c999086de8ffb0d40fd2822", size = 12285330, upload-time = "2025-10-02T19:28:58.79Z" }, { url = "https://files.pythonhosted.org/packages/10/f8/387976bf00d126b907bbd7725219257feea58650e6b055b29b224d8cb731/ruff-0.13.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78cc1abed87ce40cb07ee0667ce99dbc766c9f519eabfd948ed87295d8737c60", size = 13980815, upload-time = "2025-10-02T19:29:01.577Z" }, { url = "https://files.pythonhosted.org/packages/0c/a6/7c8ec09d62d5a406e2b17d159e4817b63c945a8b9188a771193b7e1cc0b5/ruff-0.13.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4fb75e7c402d504f7a9a259e0442b96403fa4a7310ffe3588d11d7e170d2b1e3", size = 14987733, upload-time = "2025-10-02T19:29:04.036Z" }, { url = "https://files.pythonhosted.org/packages/97/e5/f403a60a12258e0fd0c2195341cfa170726f254c788673495d86ab5a9a9d/ruff-0.13.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17b951f9d9afb39330b2bdd2dd144ce1c1335881c277837ac1b50bfd99985ed3", size = 14439848, upload-time = "2025-10-02T19:29:06.684Z" }, { url = "https://files.pythonhosted.org/packages/39/49/3de381343e89364c2334c9f3268b0349dc734fc18b2d99a302d0935c8345/ruff-0.13.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6052f8088728898e0a449f0dde8fafc7ed47e4d878168b211977e3e7e854f662", size = 13421890, upload-time = "2025-10-02T19:29:08.767Z" }, { url = "https://files.pythonhosted.org/packages/ab/b5/c0feca27d45ae74185a6bacc399f5d8920ab82df2d732a17213fb86a2c4c/ruff-0.13.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc742c50f4ba72ce2a3be362bd359aef7d0d302bf7637a6f942eaa763bd292af", size = 13444870, upload-time = "2025-10-02T19:29:11.234Z" }, { url = "https://files.pythonhosted.org/packages/50/a1/b655298a1f3fda4fdc7340c3f671a4b260b009068fbeb3e4e151e9e3e1bf/ruff-0.13.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:8e5640349493b378431637019366bbd73c927e515c9c1babfea3e932f5e68e1d", size = 13691599, upload-time = "2025-10-02T19:29:13.353Z" }, { url = "https://files.pythonhosted.org/packages/32/b0/a8705065b2dafae007bcae21354e6e2e832e03eb077bb6c8e523c2becb92/ruff-0.13.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6b139f638a80eae7073c691a5dd8d581e0ba319540be97c343d60fb12949c8d0", size = 12421893, upload-time = "2025-10-02T19:29:15.668Z" }, { url = "https://files.pythonhosted.org/packages/0d/1e/cbe7082588d025cddbb2f23e6dfef08b1a2ef6d6f8328584ad3015b5cebd/ruff-0.13.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6b547def0a40054825de7cfa341039ebdfa51f3d4bfa6a0772940ed351d2746c", size = 12267220, upload-time = "2025-10-02T19:29:17.583Z" }, { url = "https://files.pythonhosted.org/packages/a5/99/4086f9c43f85e0755996d09bdcb334b6fee9b1eabdf34e7d8b877fadf964/ruff-0.13.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9cc48a3564423915c93573f1981d57d101e617839bef38504f85f3677b3a0a3e", size = 13177818, upload-time = "2025-10-02T19:29:19.943Z" }, { url = "https://files.pythonhosted.org/packages/9b/de/7b5db7e39947d9dc1c5f9f17b838ad6e680527d45288eeb568e860467010/ruff-0.13.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1a993b17ec03719c502881cb2d5f91771e8742f2ca6de740034433a97c561989", size = 13618715, upload-time = "2025-10-02T19:29:22.527Z" }, { url = "https://files.pythonhosted.org/packages/28/d3/bb25ee567ce2f61ac52430cf99f446b0e6d49bdfa4188699ad005fdd16aa/ruff-0.13.3-py3-none-win32.whl", hash = "sha256:f14e0d1fe6460f07814d03c6e32e815bff411505178a1f539a38f6097d3e8ee3", size = 12334488, upload-time = "2025-10-02T19:29:24.782Z" }, { url = "https://files.pythonhosted.org/packages/cf/49/12f5955818a1139eed288753479ba9d996f6ea0b101784bb1fe6977ec128/ruff-0.13.3-py3-none-win_amd64.whl", hash = "sha256:621e2e5812b691d4f244638d693e640f188bacbb9bc793ddd46837cea0503dd2", size = 13455262, upload-time = "2025-10-02T19:29:26.882Z" }, { url = "https://files.pythonhosted.org/packages/fe/72/7b83242b26627a00e3af70d0394d68f8f02750d642567af12983031777fc/ruff-0.13.3-py3-none-win_arm64.whl", hash = "sha256:9e9e9d699841eaf4c2c798fa783df2fabc680b72059a02ca0ed81c460bc58330", size = 12538484, upload-time = "2025-10-02T19:29:28.951Z" }, ] [[package]] name = "secretstorage" version = "3.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "jeepney" }, ] sdist = { url = "https://files.pythonhosted.org/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", size = 19739, upload-time = "2022-08-13T16:22:46.976Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99", size = 15221, upload-time = "2022-08-13T16:22:44.457Z" }, ] [[package]] name = "shellingham" version = "1.5.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] [[package]] name = "sniffio" version = "1.3.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] [[package]] name = "snowballstemmer" version = "3.0.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, ] [[package]] name = "sortedcontainers" version = "2.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, ] [[package]] name = "sphinx" version = "8.1.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ { name = "alabaster" }, { name = "babel" }, { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" } }, { name = "imagesize" }, { name = "jinja2" }, { name = "packaging" }, { name = "pygments" }, { name = "requests" }, { name = "snowballstemmer" }, { name = "sphinxcontrib-applehelp" }, { name = "sphinxcontrib-devhelp" }, { name = "sphinxcontrib-htmlhelp" }, { name = "sphinxcontrib-jsmath" }, { name = "sphinxcontrib-qthelp" }, { name = "sphinxcontrib-serializinghtml" }, { name = "tomli" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125, upload-time = "2024-10-13T20:27:10.448Z" }, ] [[package]] name = "sphinx" version = "9.0.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ { name = "alabaster" }, { name = "babel" }, { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } }, { name = "imagesize" }, { name = "jinja2" }, { name = "packaging" }, { name = "pygments" }, { name = "requests" }, { name = "roman-numerals" }, { name = "snowballstemmer" }, { name = "sphinxcontrib-applehelp" }, { name = "sphinxcontrib-devhelp" }, { name = "sphinxcontrib-htmlhelp" }, { name = "sphinxcontrib-jsmath" }, { name = "sphinxcontrib-qthelp" }, { name = "sphinxcontrib-serializinghtml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c6/3f/4bbd76424c393caead2e1eb89777f575dee5c8653e2d4b6afd7a564f5974/sphinx-9.0.4-py3-none-any.whl", hash = "sha256:5bebc595a5e943ea248b99c13814c1c5e10b3ece718976824ffa7959ff95fffb", size = 3917713, upload-time = "2025-12-04T07:45:24.944Z" }, ] [[package]] name = "sphinx" version = "9.1.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", "python_full_version == '3.14.*'", "python_full_version == '3.13.*'", "python_full_version == '3.12.*'", ] dependencies = [ { name = "alabaster" }, { name = "babel" }, { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } }, { name = "imagesize" }, { name = "jinja2" }, { name = "packaging" }, { name = "pygments" }, { name = "requests" }, { name = "roman-numerals" }, { name = "snowballstemmer" }, { name = "sphinxcontrib-applehelp" }, { name = "sphinxcontrib-devhelp" }, { name = "sphinxcontrib-htmlhelp" }, { name = "sphinxcontrib-jsmath" }, { name = "sphinxcontrib-qthelp" }, { name = "sphinxcontrib-serializinghtml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/73/f7/b1884cb3188ab181fc81fa00c266699dab600f927a964df02ec3d5d1916a/sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978", size = 3921742, upload-time = "2025-12-31T15:09:25.561Z" }, ] [[package]] name = "sphinx-immaterial" version = "0.13.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "appdirs" }, { name = "markupsafe" }, { name = "pydantic" }, { name = "pydantic-extra-types" }, { name = "requests" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "typing-extensions" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/3d/42/6e958fc5d80ccd18c87d1b7d7c0e17fed04c0ed8a72933dd41c8643622d4/sphinx_immaterial-0.13.9-py3-none-any.whl", hash = "sha256:5ea92d2ddc6befcd0fedbd3e6766ea4746e94d9a8a5cc0ab092a946e1fde4254", size = 13742592, upload-time = "2026-02-06T16:53:11.262Z" }, ] [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, ] [[package]] name = "sphinxcontrib-devhelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, ] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, ] [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, ] [[package]] name = "sphinxcontrib-qthelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, ] [[package]] name = "sphinxcontrib-serializinghtml" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, ] [[package]] name = "stdlibs" version = "2026.2.26" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d5/cd/2710eaacaefc8be2f520b55c313498a50a295a8378e932c70d4ea34250aa/stdlibs-2026.2.26.tar.gz", hash = "sha256:10f911bdd8d3e45b452cc187b3527e6f9d288c8a943c5f973da94c71b2757d5b", size = 20203, upload-time = "2026-02-26T23:30:04.775Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/90/ec/b6a5a568d584659e037c8f53fc25acc79950ac32796b8861b2015446b7b2/stdlibs-2026.2.26-py3-none-any.whl", hash = "sha256:3257486216eac5ac627a3a4c5665802aca72fe7fc9e4ab1f232b1fb47bfd3db6", size = 59288, upload-time = "2026-02-26T23:30:03.597Z" }, ] [[package]] name = "stevedore" version = "5.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pbr" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4a/e9/4eedccff8332cc40cc60ddd3b28d4c3e255ee7e9c65679fa4533ab98f598/stevedore-5.4.0.tar.gz", hash = "sha256:79e92235ecb828fe952b6b8b0c6c87863248631922c8e8e0fa5b17b232c4514d", size = 513899, upload-time = "2024-11-20T10:08:35.894Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8f/73/d0091d22a65b55e8fb6aca7b3b6713b5a261dd01cec4cfd28ed127ac0cfc/stevedore-5.4.0-py3-none-any.whl", hash = "sha256:b0be3c4748b3ea7b854b265dcb4caa891015e442416422be16f8b31756107857", size = 49534, upload-time = "2024-11-20T10:08:34.145Z" }, ] [[package]] name = "tomli" version = "2.2.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, ] [[package]] name = "tomli-w" version = "1.2.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, ] [[package]] name = "tomlkit" version = "0.13.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885, upload-time = "2024-08-14T08:19:41.488Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955, upload-time = "2024-08-14T08:19:40.05Z" }, ] [[package]] name = "tox" version = "4.30.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, { name = "chardet" }, { name = "colorama" }, { name = "filelock" }, { name = "packaging" }, { name = "platformdirs" }, { name = "pluggy" }, { name = "pyproject-api" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "virtualenv" }, ] sdist = { url = "https://files.pythonhosted.org/packages/51/b2/cee55172e5e10ce030b087cd3ac06641e47d08a3dc8d76c17b157dba7558/tox-4.30.3.tar.gz", hash = "sha256:f3dd0735f1cd4e8fbea5a3661b77f517456b5f0031a6256432533900e34b90bf", size = 202799, upload-time = "2025-10-02T16:24:39.974Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e2/e4/8bb9ce952820df4165eb34610af347665d6cb436898a234db9d84d093ce6/tox-4.30.3-py3-none-any.whl", hash = "sha256:a9f17b4b2d0f74fe0d76207236925a119095011e5c2e661a133115a8061178c9", size = 175512, upload-time = "2025-10-02T16:24:38.209Z" }, ] [[package]] name = "tox-uv" version = "1.28.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "tox" }, { name = "uv" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f9/9a/f4b675ebcbd623854129891e87045f80c1d8e91b2957496f1fe6e463f291/tox_uv-1.28.0.tar.gz", hash = "sha256:a06ff909f73232b2b7965de19090d887b12b44e44eb0843b2c07266d2957ade2", size = 23265, upload-time = "2025-08-14T17:53:07.909Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/1f/ac/b32555d190c4440b8d2779d4a19439e5fbd5a3950f7e5a17ead7c7d30cad/tox_uv-1.28.0-py3-none-any.whl", hash = "sha256:3fbe13fa6eb6961df5512e63fc4a5cc0c8d264872674ee09164649f441839053", size = 17225, upload-time = "2025-08-14T17:53:06.299Z" }, ] [[package]] name = "trove-classifiers" version = "2025.1.15.22" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f3/cb/8f6a91c74049180e395590901834d68bef5d6a2ce4c9ca9792cfadc1b9b4/trove_classifiers-2025.1.15.22.tar.gz", hash = "sha256:90af74358d3a01b3532bc7b3c88d8c6a094c2fd50a563d13d9576179326d7ed9", size = 16236, upload-time = "2025-01-15T22:41:11.712Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2b/c5/6422dbc59954389b20b2aba85b737ab4a552e357e7ea14b52f40312e7c84/trove_classifiers-2025.1.15.22-py3-none-any.whl", hash = "sha256:5f19c789d4f17f501d36c94dbbf969fb3e8c2784d008e6f5164dd2c3d6a2b07c", size = 13610, upload-time = "2025-01-15T22:41:08.949Z" }, ] [[package]] name = "types-colorama" version = "0.4.15.20240311" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/59/73/0fb0b9fe4964b45b2a06ed41b60c352752626db46aa0fb70a49a9e283a75/types-colorama-0.4.15.20240311.tar.gz", hash = "sha256:a28e7f98d17d2b14fb9565d32388e419f4108f557a7d939a66319969b2b99c7a", size = 5608, upload-time = "2024-03-11T02:15:51.557Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/83/6944b4fa01efb2e63ac62b791a8ddf0fee358f93be9f64b8f152648ad9d3/types_colorama-0.4.15.20240311-py3-none-any.whl", hash = "sha256:6391de60ddc0db3f147e31ecb230006a6823e81e380862ffca1e4695c13a0b8e", size = 5840, upload-time = "2024-03-11T02:15:50.43Z" }, ] [[package]] name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] [[package]] name = "typing-inspection" version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] [[package]] name = "urllib3" version = "2.7.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, ] [[package]] name = "userpath" version = "1.9.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d5/b7/30753098208505d7ff9be5b3a32112fb8a4cb3ddfccbbb7ba9973f2e29ff/userpath-1.9.2.tar.gz", hash = "sha256:6c52288dab069257cc831846d15d48133522455d4677ee69a9781f11dbefd815", size = 11140, upload-time = "2024-02-29T21:39:08.742Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/43/99/3ec6335ded5b88c2f7ed25c56ffd952546f7ed007ffb1e1539dc3b57015a/userpath-1.9.2-py3-none-any.whl", hash = "sha256:2cbf01a23d655a1ff8fc166dfb78da1b641d1ceabf0fe5f970767d380b14e89d", size = 9065, upload-time = "2024-02-29T21:39:07.551Z" }, ] [[package]] name = "uv" version = "0.11.15" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/da/34/609d5d01ba21dc8f0974610ca7802fbb2c946a0c38665cfe5c5aeddbefb5/uv-0.11.15.tar.gz", hash = "sha256:755f959ec6a2fd8ccb6ee76ad90ab759d2eb1f4797444078645dd1ee4bca92d6", size = 4159545, upload-time = "2026-05-18T19:57:48.133Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a6/7c/dcc230c5911884d8848145dabcac8fb95a5ed6f9fe1c57fae8242618f28a/uv-0.11.15-py3-none-linux_armv6l.whl", hash = "sha256:83b04ab49514a0a761ffedb36a748ee81f87746671e72088e5f32c9585e5f1a9", size = 23110183, upload-time = "2026-05-18T19:57:23.051Z" }, { url = "https://files.pythonhosted.org/packages/f4/f3/efd4e044b60eb9c3c12ee386be098d56c335538ccec7caa49349cfba9344/uv-0.11.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cae61f737be075b90be9e3f07d961072aed7019f4c9b8ed5c5d41c4d6cade3", size = 22637941, upload-time = "2026-05-18T19:57:26.752Z" }, { url = "https://files.pythonhosted.org/packages/a6/b8/48627f895a1569e576822e0a8416aa4797eb4a4551de21a4ad97b9b5819d/uv-0.11.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9accae33619a9166e5c48531deb455d672cfb89f9357a00975e669c76b0bd49f", size = 21258803, upload-time = "2026-05-18T19:57:05.473Z" }, { url = "https://files.pythonhosted.org/packages/af/50/4bc8a148274feabee2d9c9f1fa15009e10c0228dfe57981ee3ea2ef1d481/uv-0.11.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:c0cf52cd6d50bb9e05e2d968f45f80761107e4cbc8d4a26d9758f9d8274aaec1", size = 23066178, upload-time = "2026-05-18T19:57:33.058Z" }, { url = "https://files.pythonhosted.org/packages/a9/56/139fc3bec9a8b0a25bfe2196123adb9f16124da437bf4fbcf0d21cfcafb2/uv-0.11.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:49dc6ed70bff00937384f96cdc4b1a4742d18e5504ec2c4a1214dba2dee5687a", size = 22705332, upload-time = "2026-05-18T19:57:36.714Z" }, { url = "https://files.pythonhosted.org/packages/ca/b0/b18b3dd204f8c213236a1ebd148e009861637129a8cce34df0e9aa22ed40/uv-0.11.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:adb9a89352539fdd8f7cd5f9966cf9f94fc5b98e0ccdf5003a04123dc6423bec", size = 22707534, upload-time = "2026-05-18T19:58:04.117Z" }, { url = "https://files.pythonhosted.org/packages/76/36/3ca09f95572df99d361b49c96b1297149e96e120d8d1ecf074095a4b6da4/uv-0.11.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40ff67e3f8e8a7533781a2e892a534975a93acb83ea35460e64e7b2bf2111774", size = 24096607, upload-time = "2026-05-18T19:58:11.625Z" }, { url = "https://files.pythonhosted.org/packages/64/be/3bdee21a296bbf5336a526e3613d0e7d4538dacc39c62d7fcba55d15f6b0/uv-0.11.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6463a299ed7e6b5a800ed6f108af8e1588352629424133ddef7572b0e1e1118", size = 25082562, upload-time = "2026-05-18T19:57:40.69Z" }, { url = "https://files.pythonhosted.org/packages/cd/73/f371f3689ffe741066468d001d85f739fc4b5574de83b639ef19b5e8a7f4/uv-0.11.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68c1e62d4b78578b90b833553286b65d6a7e327537716441068583ba652ec4f5", size = 24253391, upload-time = "2026-05-18T19:57:18.47Z" }, { url = "https://files.pythonhosted.org/packages/d3/16/fe392d618af6b00c064b3e718d585dcf791546a77c5123a5bec07ce53a0a/uv-0.11.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98edf1bdaf82447014852051d93e3ee95012509c567bf057fd117e6bdbd9a807", size = 24415871, upload-time = "2026-05-18T19:58:19.651Z" }, { url = "https://files.pythonhosted.org/packages/6e/24/2e92a052fb6334fcd746d1c7cb57847c204b118c84f5da53c0f9e129f7b7/uv-0.11.15-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:be8f76d25bcf4c92bb384240ac1bf9aa7f51063d0bdeca4c9cf0ec3ed8b145e0", size = 23159007, upload-time = "2026-05-18T19:57:10.653Z" }, { url = "https://files.pythonhosted.org/packages/3d/2e/6923d0658d164bb2c435ed1868aa2d49b3074594679917a001ff92dc95bb/uv-0.11.15-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:f9f4fbbf4fe485522054f3c7496c6e8e932d6436e4200ff3daf718db0b7c7bd5", size = 23769385, upload-time = "2026-05-18T19:58:15.856Z" }, { url = "https://files.pythonhosted.org/packages/a4/99/7e34cd949e57360814e8064cc9fb7104df445d0f6a663504e5f7473480aa/uv-0.11.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0ed920e896b2fd13a35031707e307e42fbb2681458b967440a17272d86d49137", size = 23860973, upload-time = "2026-05-18T19:57:55.575Z" }, { url = "https://files.pythonhosted.org/packages/28/98/8fe1f5f9d816e94569a0298dd8e0936801097625fa1952162951f0d628b6/uv-0.11.15-py3-none-musllinux_1_1_i686.whl", hash = "sha256:41d907611f3e6a13262807fd7f0a17849f76285ca80f536f6b3943732bdc6656", size = 23431392, upload-time = "2026-05-18T19:57:59.814Z" }, { url = "https://files.pythonhosted.org/packages/cc/6b/76a1ce2fa860026913a5941700cdc7d715fce9c3277a3fa3489cf2523ca0/uv-0.11.15-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:e3b68f8bf1a4568710f77e5bda9182ce7682811d89a8e7468c22460e032b234d", size = 24519478, upload-time = "2026-05-18T19:57:51.165Z" }, { url = "https://files.pythonhosted.org/packages/43/60/1d58e8a05718cb50494763115710b73846cacb651fd735d285233fd72c59/uv-0.11.15-py3-none-win32.whl", hash = "sha256:8e2da3076761086a5b76869c3f38ef0509c836046ef41ddd19485dfd7271dca9", size = 22020178, upload-time = "2026-05-18T19:58:07.64Z" }, { url = "https://files.pythonhosted.org/packages/55/53/40fcefcb348af660488597ed3c01363df7344e60611f8883750dc596f5c6/uv-0.11.15-py3-none-win_amd64.whl", hash = "sha256:cc3915ab291a1ecaf31de05f5d8bd70d09c66fe9911a53f70d9efa62ff0dbd8a", size = 24668779, upload-time = "2026-05-18T19:57:44.894Z" }, { url = "https://files.pythonhosted.org/packages/e5/7d/fa3a9960c95af9bbe2a629048760d0b9b4fead8ccd4f2235af747ec7cdf0/uv-0.11.15-py3-none-win_arm64.whl", hash = "sha256:4f39426a13dee24897aed60c4b98058c66f18bd983885ac5f4a54a04b24fbddf", size = 23198178, upload-time = "2026-05-18T19:57:14.68Z" }, ] [[package]] name = "virtualenv" version = "20.36.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba", size = 6032239, upload-time = "2026-01-09T18:21:01.296Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258, upload-time = "2026-01-09T18:20:59.425Z" }, ] [[package]] name = "zipp" version = "3.21.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545, upload-time = "2024-11-10T15:05:20.202Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630, upload-time = "2024-11-10T15:05:19.275Z" }, ] [[package]] name = "zstandard" version = "0.25.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/56/7a/28efd1d371f1acd037ac64ed1c5e2b41514a6cc937dd6ab6a13ab9f0702f/zstandard-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e59fdc271772f6686e01e1b3b74537259800f57e24280be3f29c8a0deb1904dd", size = 795256, upload-time = "2025-09-14T22:15:56.415Z" }, { url = "https://files.pythonhosted.org/packages/96/34/ef34ef77f1ee38fc8e4f9775217a613b452916e633c4f1d98f31db52c4a5/zstandard-0.25.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4d441506e9b372386a5271c64125f72d5df6d2a8e8a2a45a0ae09b03cb781ef7", size = 640565, upload-time = "2025-09-14T22:15:58.177Z" }, { url = "https://files.pythonhosted.org/packages/9d/1b/4fdb2c12eb58f31f28c4d28e8dc36611dd7205df8452e63f52fb6261d13e/zstandard-0.25.0-cp310-cp310-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:ab85470ab54c2cb96e176f40342d9ed41e58ca5733be6a893b730e7af9c40550", size = 5345306, upload-time = "2025-09-14T22:16:00.165Z" }, { url = "https://files.pythonhosted.org/packages/73/28/a44bdece01bca027b079f0e00be3b6bd89a4df180071da59a3dd7381665b/zstandard-0.25.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e05ab82ea7753354bb054b92e2f288afb750e6b439ff6ca78af52939ebbc476d", size = 5055561, upload-time = "2025-09-14T22:16:02.22Z" }, { url = "https://files.pythonhosted.org/packages/e9/74/68341185a4f32b274e0fc3410d5ad0750497e1acc20bd0f5b5f64ce17785/zstandard-0.25.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:78228d8a6a1c177a96b94f7e2e8d012c55f9c760761980da16ae7546a15a8e9b", size = 5402214, upload-time = "2025-09-14T22:16:04.109Z" }, { url = "https://files.pythonhosted.org/packages/8b/67/f92e64e748fd6aaffe01e2b75a083c0c4fd27abe1c8747fee4555fcee7dd/zstandard-0.25.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b6bd67528ee8b5c5f10255735abc21aa106931f0dbaf297c7be0c886353c3d0", size = 5449703, upload-time = "2025-09-14T22:16:06.312Z" }, { url = "https://files.pythonhosted.org/packages/fd/e5/6d36f92a197c3c17729a2125e29c169f460538a7d939a27eaaa6dcfcba8e/zstandard-0.25.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4b6d83057e713ff235a12e73916b6d356e3084fd3d14ced499d84240f3eecee0", size = 5556583, upload-time = "2025-09-14T22:16:08.457Z" }, { url = "https://files.pythonhosted.org/packages/d7/83/41939e60d8d7ebfe2b747be022d0806953799140a702b90ffe214d557638/zstandard-0.25.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9174f4ed06f790a6869b41cba05b43eeb9a35f8993c4422ab853b705e8112bbd", size = 5045332, upload-time = "2025-09-14T22:16:10.444Z" }, { url = "https://files.pythonhosted.org/packages/b3/87/d3ee185e3d1aa0133399893697ae91f221fda79deb61adbe998a7235c43f/zstandard-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:25f8f3cd45087d089aef5ba3848cd9efe3ad41163d3400862fb42f81a3a46701", size = 5572283, upload-time = "2025-09-14T22:16:12.128Z" }, { url = "https://files.pythonhosted.org/packages/0a/1d/58635ae6104df96671076ac7d4ae7816838ce7debd94aecf83e30b7121b0/zstandard-0.25.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3756b3e9da9b83da1796f8809dd57cb024f838b9eeafde28f3cb472012797ac1", size = 4959754, upload-time = "2025-09-14T22:16:14.225Z" }, { url = "https://files.pythonhosted.org/packages/75/d6/57e9cb0a9983e9a229dd8fd2e6e96593ef2aa82a3907188436f22b111ccd/zstandard-0.25.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:81dad8d145d8fd981b2962b686b2241d3a1ea07733e76a2f15435dfb7fb60150", size = 5266477, upload-time = "2025-09-14T22:16:16.343Z" }, { url = "https://files.pythonhosted.org/packages/d1/a9/ee891e5edf33a6ebce0a028726f0bbd8567effe20fe3d5808c42323e8542/zstandard-0.25.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a5a419712cf88862a45a23def0ae063686db3d324cec7edbe40509d1a79a0aab", size = 5440914, upload-time = "2025-09-14T22:16:18.453Z" }, { url = "https://files.pythonhosted.org/packages/58/08/a8522c28c08031a9521f27abc6f78dbdee7312a7463dd2cfc658b813323b/zstandard-0.25.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e7360eae90809efd19b886e59a09dad07da4ca9ba096752e61a2e03c8aca188e", size = 5819847, upload-time = "2025-09-14T22:16:20.559Z" }, { url = "https://files.pythonhosted.org/packages/6f/11/4c91411805c3f7b6f31c60e78ce347ca48f6f16d552fc659af6ec3b73202/zstandard-0.25.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:75ffc32a569fb049499e63ce68c743155477610532da1eb38e7f24bf7cd29e74", size = 5363131, upload-time = "2025-09-14T22:16:22.206Z" }, { url = "https://files.pythonhosted.org/packages/ef/d6/8c4bd38a3b24c4c7676a7a3d8de85d6ee7a983602a734b9f9cdefb04a5d6/zstandard-0.25.0-cp310-cp310-win32.whl", hash = "sha256:106281ae350e494f4ac8a80470e66d1fe27e497052c8d9c3b95dc4cf1ade81aa", size = 436469, upload-time = "2025-09-14T22:16:25.002Z" }, { url = "https://files.pythonhosted.org/packages/93/90/96d50ad417a8ace5f841b3228e93d1bb13e6ad356737f42e2dde30d8bd68/zstandard-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea9d54cc3d8064260114a0bbf3479fc4a98b21dffc89b3459edd506b69262f6e", size = 506100, upload-time = "2025-09-14T22:16:23.569Z" }, { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" }, { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" }, { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" }, { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" }, { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" }, { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" }, { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" }, { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" }, { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" }, { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" }, { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" }, { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" }, { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" }, { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" }, { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" }, { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" }, { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" }, { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" }, { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" }, { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" }, { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" }, { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" }, { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" }, { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" }, { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" }, { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" }, { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" }, { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" }, { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" }, { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" }, { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" }, { url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922, upload-time = "2025-09-14T22:17:24.398Z" }, { url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276, upload-time = "2025-09-14T22:17:21.429Z" }, { url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679, upload-time = "2025-09-14T22:17:23.147Z" }, { url = "https://files.pythonhosted.org/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735, upload-time = "2025-09-14T22:17:26.042Z" }, { url = "https://files.pythonhosted.org/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440, upload-time = "2025-09-14T22:17:27.366Z" }, { url = "https://files.pythonhosted.org/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070, upload-time = "2025-09-14T22:17:28.896Z" }, { url = "https://files.pythonhosted.org/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001, upload-time = "2025-09-14T22:17:31.044Z" }, { url = "https://files.pythonhosted.org/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120, upload-time = "2025-09-14T22:17:32.711Z" }, { url = "https://files.pythonhosted.org/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230, upload-time = "2025-09-14T22:17:34.41Z" }, { url = "https://files.pythonhosted.org/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173, upload-time = "2025-09-14T22:17:36.084Z" }, { url = "https://files.pythonhosted.org/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736, upload-time = "2025-09-14T22:17:37.891Z" }, { url = "https://files.pythonhosted.org/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368, upload-time = "2025-09-14T22:17:40.206Z" }, { url = "https://files.pythonhosted.org/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022, upload-time = "2025-09-14T22:17:41.879Z" }, { url = "https://files.pythonhosted.org/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889, upload-time = "2025-09-14T22:17:43.577Z" }, { url = "https://files.pythonhosted.org/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952, upload-time = "2025-09-14T22:17:45.271Z" }, { url = "https://files.pythonhosted.org/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054, upload-time = "2025-09-14T22:17:47.08Z" }, { url = "https://files.pythonhosted.org/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113, upload-time = "2025-09-14T22:17:48.893Z" }, { url = "https://files.pythonhosted.org/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936, upload-time = "2025-09-14T22:17:52.658Z" }, { url = "https://files.pythonhosted.org/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232, upload-time = "2025-09-14T22:17:50.402Z" }, { url = "https://files.pythonhosted.org/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671, upload-time = "2025-09-14T22:17:51.533Z" }, { url = "https://files.pythonhosted.org/packages/3d/5c/f8923b595b55fe49e30612987ad8bf053aef555c14f05bb659dd5dbe3e8a/zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3", size = 795887, upload-time = "2025-09-14T22:17:54.198Z" }, { url = "https://files.pythonhosted.org/packages/8d/09/d0a2a14fc3439c5f874042dca72a79c70a532090b7ba0003be73fee37ae2/zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f", size = 640658, upload-time = "2025-09-14T22:17:55.423Z" }, { url = "https://files.pythonhosted.org/packages/5d/7c/8b6b71b1ddd517f68ffb55e10834388d4f793c49c6b83effaaa05785b0b4/zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c", size = 5379849, upload-time = "2025-09-14T22:17:57.372Z" }, { url = "https://files.pythonhosted.org/packages/a4/86/a48e56320d0a17189ab7a42645387334fba2200e904ee47fc5a26c1fd8ca/zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439", size = 5058095, upload-time = "2025-09-14T22:17:59.498Z" }, { url = "https://files.pythonhosted.org/packages/f8/ad/eb659984ee2c0a779f9d06dbfe45e2dc39d99ff40a319895df2d3d9a48e5/zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043", size = 5551751, upload-time = "2025-09-14T22:18:01.618Z" }, { url = "https://files.pythonhosted.org/packages/61/b3/b637faea43677eb7bd42ab204dfb7053bd5c4582bfe6b1baefa80ac0c47b/zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859", size = 6364818, upload-time = "2025-09-14T22:18:03.769Z" }, { url = "https://files.pythonhosted.org/packages/31/dc/cc50210e11e465c975462439a492516a73300ab8caa8f5e0902544fd748b/zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0", size = 5560402, upload-time = "2025-09-14T22:18:05.954Z" }, { url = "https://files.pythonhosted.org/packages/c9/ae/56523ae9c142f0c08efd5e868a6da613ae76614eca1305259c3bf6a0ed43/zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7", size = 4955108, upload-time = "2025-09-14T22:18:07.68Z" }, { url = "https://files.pythonhosted.org/packages/98/cf/c899f2d6df0840d5e384cf4c4121458c72802e8bda19691f3b16619f51e9/zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2", size = 5269248, upload-time = "2025-09-14T22:18:09.753Z" }, { url = "https://files.pythonhosted.org/packages/1b/c0/59e912a531d91e1c192d3085fc0f6fb2852753c301a812d856d857ea03c6/zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344", size = 5430330, upload-time = "2025-09-14T22:18:11.966Z" }, { url = "https://files.pythonhosted.org/packages/a0/1d/7e31db1240de2df22a58e2ea9a93fc6e38cc29353e660c0272b6735d6669/zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c", size = 5811123, upload-time = "2025-09-14T22:18:13.907Z" }, { url = "https://files.pythonhosted.org/packages/f6/49/fac46df5ad353d50535e118d6983069df68ca5908d4d65b8c466150a4ff1/zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088", size = 5359591, upload-time = "2025-09-14T22:18:16.465Z" }, { url = "https://files.pythonhosted.org/packages/c2/38/f249a2050ad1eea0bb364046153942e34abba95dd5520af199aed86fbb49/zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12", size = 444513, upload-time = "2025-09-14T22:18:20.61Z" }, { url = "https://files.pythonhosted.org/packages/3a/43/241f9615bcf8ba8903b3f0432da069e857fc4fd1783bd26183db53c4804b/zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2", size = 516118, upload-time = "2025-09-14T22:18:17.849Z" }, { url = "https://files.pythonhosted.org/packages/f0/ef/da163ce2450ed4febf6467d77ccb4cd52c4c30ab45624bad26ca0a27260c/zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d", size = 476940, upload-time = "2025-09-14T22:18:19.088Z" }, ] isort-9.0.1/.github/FUNDING.yml0000644000000000000000000000006013615410400012752 0ustar00github: "timothycrosley" tidelift: "pypi/isort" isort-9.0.1/.github/dependabot.yml0000644000000000000000000000047613615410400014000 0ustar00# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" groups: github-actions: patterns: - "*" isort-9.0.1/.github/labels.yml0000644000000000000000000000361313615410400013131 0ustar00--- # Labels names are important as they are used by Release Drafter to decide # regarding where to record them in changelog or if to skip them. # # The repository labels will be automatically configured using this file and # the GitHub Action https://github.com/marketplace/actions/github-labeler. - name: breaking description: Breaking Changes color: "bfd4f2" - name: bug description: Something isn't working color: "d73a4a" - name: build description: Build System and Dependencies color: "bfdadc" - name: ci description: Continuous Integration color: "4a97d6" - name: dependencies description: Pull requests that update a dependency file color: "0366d6" - name: documentation description: Improvements or additions to documentation color: "0075ca" - name: duplicate description: This issue or pull request already exists color: "cfd3d7" - name: enhancement description: New feature or request color: "a2eeef" - name: github_actions description: Pull requests that update Github_actions code color: "000000" - name: good first issue description: Good for newcomers color: "7057ff" - name: help wanted description: Extra attention is needed color: "008672" - name: invalid description: This doesn't seem right color: "e4e669" - name: performance description: Performance color: "016175" - name: python description: Pull requests that update Python code color: "2b67c6" - name: question description: Further information is requested color: "d876e3" - name: refactoring description: Refactoring color: "ef67c4" - name: removal description: Removals and deprecations color: "9ae7ea" - name: style description: Style color: "c120e5" - name: testing description: Testing color: "b1fc6f" - name: wontfix description: This will not be worked on color: "ffffff" - name: skip-changelog description: This will not be added to release notes color: "dddddd" isort-9.0.1/.github/release-drafter.yml0000644000000000000000000000150413615410400014731 0ustar00--- template: | ## Changes $CHANGES categories: - title: ":boom: Breaking Changes" label: "breaking" - title: ":rocket: Features" label: "enhancement" - title: ":fire: Removals and Deprecations" label: "removal" - title: ":beetle: Fixes" label: "bug" - title: ":raising_hand: Help wanted" label: "help wanted" - title: ":racehorse: Performance" label: "performance" - title: ":rotating_light: Testing" label: "testing" - title: ":construction_worker: Continuous Integration" label: "ci" - title: ":books: Documentation" label: "documentation" - title: ":hammer: Refactoring" label: "refactoring" - title: ":lipstick: Style" label: "style" - title: ":package: Dependencies" labels: - "dependencies" - "build" exclude-labels: - "skip-changelog" isort-9.0.1/.github/workflows/build.yml0000644000000000000000000000422313615410400015021 0ustar00--- name: Build "on": pull_request: types: - labeled push: branches: - main workflow_dispatch: workflow_call: permissions: contents: read jobs: build: if: github.event_name != 'pull_request' || github.event.label.name == 'build-wheels' name: Build sdist and non-compiled wheel runs-on: ubuntu-latest steps: - &checkout name: Check out the repository uses: actions/checkout@v7 with: # To ensure hatch-vcs can get the correct version from the git history. fetch-depth: 0 - name: Set up Python 3.14 uses: actions/setup-python@v7 with: python-version: 3.14 check-latest: true - &setup-uv name: Install UV uses: astral-sh/setup-uv@v7 with: enable-cache: true version-file: "pyproject.toml" - name: Build sdist and wheel env: HATCH_BUILD_HOOK_ENABLE_MYPYC: false run: uv build - name: Upload sdist uses: actions/upload-artifact@v7.0.1 with: name: cibw-sdist path: dist/*.tar.gz - name: Upload wheel uses: actions/upload-artifact@v7.0.1 with: name: cibw-wheel-pure path: dist/*.whl build_wheels: name: Build wheels for ${{ matrix.os }} runs-on: ${{ matrix.runs-on }} strategy: matrix: include: - os: ubuntu-latest runs-on: ubuntu-latest - os: windows-latest runs-on: windows-latest - os: macos-latest runs-on: macos-latest - os: pyodide runs-on: ubuntu-latest platform: pyodide steps: - *checkout - *setup-uv - name: Build wheels env: CIBW_BUILD_FRONTEND: "build[uv]" CIBW_ENVIRONMENT: "HATCH_BUILD_HOOK_ENABLE_MYPYC=true" CIBW_PLATFORM: ${{ matrix.platform || 'auto' }} CIBW_SKIP: "*-win32" uses: pypa/cibuildwheel@v4.2.0 - uses: actions/upload-artifact@v7.0.1 with: name: cibw-wheels-${{ matrix.os }} path: ./wheelhouse/*.whl isort-9.0.1/.github/workflows/continuous-integration.yml0000644000000000000000000000446013615410400020454 0ustar00--- name: CI "on": push: branches: - main pull_request: merge_group: permissions: contents: read env: DEFAULT_PYTHON: "3.14" jobs: lint: name: Lint runs-on: ubuntu-latest steps: - &checkout name: Check out the repository uses: actions/checkout@v7 with: fetch-depth: 2 - &setup-python name: Set up Python ${{ env.DEFAULT_PYTHON }} uses: actions/setup-python@v7 with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true - &setup-uv name: Install UV uses: astral-sh/setup-uv@v7 with: enable-cache: true version-file: "pyproject.toml" - name: Install dependencies run: uv sync --all-extras --frozen - name: Lint run: uv run --with tox-uv tox -e lint test: strategy: fail-fast: false matrix: python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] os: [ubuntu-latest, macos-latest, windows-latest] compiled: [true, false] runs-on: ${{ matrix.os }} name: "Tests (Python: ${{ matrix.python-version }}, OS: ${{ matrix.os }}, Compiled: ${{ matrix.compiled }})" env: FORCE_COLOR: 1 steps: - *checkout - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v7 with: python-version: ${{ matrix.python-version }} allow-prereleases: true check-latest: true - *setup-uv - name: "Install dependencies (compiled: ${{ matrix.compiled }})" env: HATCH_BUILD_HOOK_ENABLE_MYPYC: ${{ matrix.compiled }} run: uv sync --all-extras --frozen - name: Run tests env: COVERAGE_FAIL_UNDER: ${{ matrix.compiled && '0' || '99' }} run: uv run --with tox-uv tox -e py,coverage_report-ci - name: Report Coverage uses: codecov/codecov-action@v7 integration: name: Integration tests runs-on: ubuntu-latest steps: - *checkout - *setup-python - *setup-uv - name: Install dependencies (compiled) env: HATCH_BUILD_HOOK_ENABLE_MYPYC: true run: uv sync --all-extras --frozen - name: Test integration run: ./scripts/test_integration.sh isort-9.0.1/.github/workflows/release.yml0000644000000000000000000000221413615410400015340 0ustar00--- name: Release on: release: types: - published jobs: build: uses: ./.github/workflows/build.yml release-pypi: name: Upload release to PyPI runs-on: ubuntu-latest needs: ["build"] environment: name: pypi url: https://pypi.org/p/isort permissions: id-token: write steps: - &download-release-assets name: Download release assets uses: actions/download-artifact@v8.0.1 with: merge-multiple: true path: dist/ pattern: cibw-* - name: Publish package on PyPI if: github.event_name == 'release' uses: pypa/gh-action-pypi-publish@release/v1 release-github: name: Upload assets to Github release runs-on: ubuntu-latest needs: ["build"] permissions: contents: write id-token: write steps: - *download-release-assets - name: Sign the dists with Sigstore and upload assets to Github release if: github.event_name == 'release' uses: sigstore/gh-action-sigstore-python@v3.5.0 with: inputs: | ./dist/*.tar.gz ./dist/*.whl isort-9.0.1/art/isort_loves_black.png0000644000000000000000000016772213615410400014620 0ustar00PNG  IHDR; (iCCPICC profile(}=H@_S"vqP,U(BP+`rФ!Iqq\ ~,V\uup'G'E)IExwqeYf:104"2YIJw|#׻8?U" 3Lxxr68GYIVωGL #8]xf̤爣b6f%S# NBcg\e{i",BUl qZuR,i?prmch]?*yI8C@hhq'@[J$bG@dnip?)RP(gM9YzkPW.Rϻ{LRrfbKGD pHYs.#.#x?vtIME #AtEXtCommentCreated with GIMPW IDATxy|Tי=;$ !AA֥.׺>Wֶh[[{_mRw{mJrm"}!{&3sd&@6`Bf,391 """"""""d\IF DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))JDDDDDDDD+IJ DDDDDDDD$))c@SCmmO}=46B0D"-g~z!%RS!=22`,: AMMuucpq+v33ӣWQDD DDDDHm`X,u`-{(9&O2(-hR~r͛n\w.+OJJ1''x31hDDDD޽`6\~9wL#FD,龚ظ>$z 9? 4)P)~AH2mر>^y!9e8|?:jXx{.Za `:n/IJ DDDDAU,Z_{ S1hO+Wa櫮W8B$JDDDXqش)ZUu#ȹ7ÙgS `lxo?Çk*$W""""GmGr?~ .>1eeeW}drp0v,9\-D#C~=D+w>XD+~]u$tSϨQ`Y QJDDDhرx~gnjXmBEʼns,]z2De9\IMM0sfa[]wa1/^|<_¥?A$=W""""Gѩd?ǣUG_|0zBDD8W""""}-hhxu_)_}e ||;h][ii;a~KkJDDD^y45F?N=b |!|s?t]ZZ?.CDD+Ƣ7~c~ۆ^ֱ6z5""'\uAޝ/kDD䄰Wj#Wc~hn g̙:}{߆@@c!"":^hj\U_9~;ݲ~SV}{g?~FDDDzIHwꗿX_ ==몭4}^HPp%"""7 ;ٳ;߁#:fx)̈+mx1я4GړOw{d*jk~IF?;"""=JDDD^z nMp<wllY˖ۣPp%"""~W]q8fώknҸmW] t+پnIp#nfe7ٱsMϒH7(9tի5b_ eȱzut4(""ݠJDDD3/dpb.YϖH(̲epd7 ۶u5۶E_РKwlt+=(H6Ww۵R_}y&g^c!""JDDD#=qHF?> s'x%_~DDDC6nVH9S$ysO&""r DDDDڲmx!X$oݻ}cܪ5XH\d <ơ?"֯E"ѯUTh|~DDD:JN~'zppkhDDpXU;w_=Xa8;_ S7E Ev MTVհrN&}DDKOX9Gofx/PVoôiiǣ!eSG1./ Jx!7w?ྐྵUnP~%"rD8G`Y ٳaD8ضM$!`! aG"  7P_߀mGXeYqeY|>|>nϏeYn^/. [sK.PUv[Qvc͆m+Fުt+ ]q'9.5rTضmۄa0MMM477@8qh5qNE,n(x:\u2Ztpbna \.~EJJ >ԔRRRx^|>. b.eN/sG3ܔWS֩ 3G r,͙&DODzm=ZS__O]]= 444 TeY`5}: o L@.jk08TRFZj*|^p"}vSUqta:=99&-'W˟B:pV\m#F rmCjlۦ&:MAlj^\VEE/ۧK>'EAU;2mcLrMUD@aY}I_" `0,@tHOO'55~*һMώUiq^ K:Ұy!7Oc }J  Af_e%aZb^Pmb L4>-VuDWcŧx}>-_=')}񭍅]&0z9MsKXQ8C vpXfYm=_\0ro%Ntd4Dx`РA 8t@H_Sp%Ǎ܁~*˧A\:%iV=gYnJyV4\GFcd/߯8E"{R__c;^N8sPO%ge|^^oTJ >Tn7].O4\`qԫ"-`C]]mH(2EH$BsKCxZl@8LO*0UUUTUUE+vJnn.ss 55W{ Q^C-܌,.dw ;B.(Ǐ 76R^/eSp \Npk‚d0>[cj,N0MMMQUu={PUUEssˊV3dy8ѿT+ϜC^r\|>H :6AY*u`f kkkij jnn ,ˊN%ʰhZ lnMqlGv@rss&33͒5r( fgŽU.૞LT<p>HNd￯1IϤ^՞={رc555Il[K(L%@qa CZZdggEFF)))x<:4 =hXOMM khP8X$Z,H$޽{ٳgO.##B dI)FC_x=nGr:.i|V _ 2r:)$9157siDɳuׁWBuuuܱm۷q8=֮RpoJvv6 ";;t~Bl&IM$f{ŚzvdeYUݡ*'AR[[˪UdС -Jff&~M(F}8B5=DԵZŖ-Ou&\!Ʃ@i>-8$E`(/Xcٻw/۶oc]B!n78X5n|J*GfF& "g rZ*6D pjkk,&vͧ~ߞ -[^[]] ߲lc}rrr,2&L Vaa!΀z<ݲ,~?~lF܅Cmm-UUU۷}EBѾW-`bi1cX΢յX&??"c,+9nl6w8P&1/h!h{p+'.#***hnnSb"H)77]].rssox= <>Qnw.Aeֈ$u뫮.h1{a=D"lfƍرݻ-#yTG۶;fMƔ)S;v,999 0[S\.YY@YYm ;vsNjkk#az.]ŋ t#F ;;[w+<ѮkL_N=Np-~ \3W{{񐚚Jcc#HP8d@T @J ݿJb\ e9^6MM԰sN¡0uulٲFvþJn7x7df̘YgŸqˋ7IeYss3vb۶mڹږ[-s m!%5#SZz999 N @U1(?zϘ,~oQŕ_~_ǹw<7~jO`'Tpf L..O8$X?͛7|rɶm<yyy2l0222xSUU֭[Y`?Xp!pT0p8V3thwB"DC)'s`YuN=|3؅duZXK+,,+zh  555444{.TW{.֬]KCCPǃ磱ˤI 1c' 233wl۶͛6knB[l&0zh8pPxRp ڂ+,ދ2ݪMۗ'uj(s\9ϓbUv /Xto \\~HzEB1Ν;Yd {9lNJMMr ՟(PUUݻO}}=^E$v3zhrss:t(999瓖P15`> yNQhM˴pL3޽{ٿ?۶mիWq NʕW^),,$''[Mmf޽_-[PWWA锶c3(g&L8ޘ^q%c1ᛟ=ٰm%:Mm> wTrBYNc QWDŽ8߿+Wj*"H<8h23(--.WD"ˎ;7o/'55SN9aÆKQQ}mSmcm;-SBă-[ī/_N8I&qeqyQ\\Lnn!,MAA̘1{n:6oLuuut\Vؿ?o#;v,}_-Wrܱmomӽu|Q1` }U nT(\G$ $"kٲ.7 غu+-ro琅L؝Ϯ];ټy36l`ǎxFz*7|3ӧOþ1߿ V^Mc~ke?ی8x<Əτ zT^W] -r<.R]n "v1D <_ \Æ逋$O?<`e˗[6Mcوc\C2vXF٥Ul߾7xzŋFcc#iiiSZZBIII4sT۹P:Ӭ ͤ:k%SE8yaKV^M043=]mvm8t'C>0}il޲7R_{2Z.b FVVamlڴ˗e˧DH 1p1c0ydܥU=W]JDW'Lpz5L.tg>ga}{]֭[ru>8Czz:ƍcҤIdddvlٲ9s#~LKA3axFM IDATAAA))p/I{<fdյ%َY=oorvhnnfϞ=_رǃ&%%뮻~1K!Vcc#+V`TvwP2?Y`%9W]JDW'Lpp!|:"a4C_ PYYɼyXf n;~FtŐC6mÇ?lsz>Syx *++,H8I#sȓ9餓ϏOҔn-]~Il@w=ڰts>Z2FKxo>6mڵkYf c \}\s5 >!1neᢅl޼v ;6EŜ Qcٌu5Pj$$^l58,uRo\EPDR˝D$h z)X|/_m;]ƭhueԨQL6C.7}v^yfϞ͆ pANyy9ee 4(C$i +M­xAm2ZAmdŃ7Ӧz1S2̥MS򖀋xcƲzAXP 뉷w{εYzeT|Ăh:[-kIٕ0 Kb+*ك^%6wopl˲bҤIL, `%U\u*DzƇa~xvQFk+> 叡Ǥ.f=Hl4nQ3CQŕ Sqsp5:"+5=T]]믿NŊhUKP98IKK;2ǢEo~ )cNadHrrrcڥ?-jԫӻЅev妕{%|b{@ّ^y_-euu5ׯgժUl޼ǃ㡼ロ>C6\,^fmی1.HPp ^nK6KLZD s50j&kk "3,2LO-z70λV%n½A/N."cx=4WLvhs*ENj02Y!F(r5ICIPv=۾jNL֝Iv\3\\' 9~0գ7.xnAM̟?{|˅8s32e >B!6nٳy'inn&ɘ1cu( !K981]KzoYiSvWn@J+ĖmpޣA!q,VVvV\Iee%^e]Ɲwȑ#III5aŊTWWo޲~qe,GSHqmgoc|)|)2ךH{ORO%3/ni8pΘ)Sؼe ֭c͚5ĉ;v,1UUUN x,].'v8-QW"GnJx?&ݪi *Mg1wMgsl+\$W72 nY eSx_"hI!LR:xWaE0tkڅW6 pk+] LǸkCC{9wxih<~K? j DY=޽9sm۶X1@ y,gq!+*++3gw{!LjJ vOUJ-U'^cLOV봴X6=X%OkMm6to[ЗͲDL-,Ÿq1L Zt)=\)[KŊϳ42Pmi26XthPˈp&k6UTZ!VufbN|bۮd r[7Wx黖xjerI'1}tV^ xb׾5 Үp3e,YܹsJ /;pUWQTTt)J_ȱlnw`UQQGCg13S\>[Ǩ*õ-|9֞#qp{|߳3ߡ:B3׹#9b<IKYN]8::,<+Ϗ#7">DžF|M͛oZclj 77Mgm1OE -RΝKSSˊissꩧr%h+)sc- GLqpb+̔9>Bh > R0=_qp[\x 2MuW3W07TzW`kSRWe|kFяl[c jfa˖-@UXvxES &p%0pCV?0hjj"PV^رڦZ%0-SV}SPGAA!EEE 4+YVXE""HGؼy3!##Çr[aJ6ݻwSQ5W裏OsWsmQZZ.z|3aĉk,ZH㏩ꫯrՅHn')s)w9UGʫSjG)n`V[v8-һ3a|x/ pہOX^c/*nIya}r^iH;Ip]=`C\N4EYǚ;w.o6.JX`CQQW^a8۶mGq=z4'N$''qp`ZimGh6>a%7nFbРAݺiYezz+Í1̝;[o{RZZʔ)S1N4l0]6:hуsS'tRVX?O?UW]waEΜq&y֮]JXvCCS'sW(Pp%F8u.ϭᕃ,7ʞGԵm^ q0{>'YVuPlgpnx2wZ߇} SRJoEYn?ξ[nBKOh/W\N;+1ܹ￟G}x`5~xqp8چSVGAV|82fN;4.0L8p8L(¶m mxxv |UXW\ɓyǨ:'䌲mo-M'Ӄ$|m郦4W˶[!iZ%Zmf&bu&; ڌhfc17 KDZIMM3 ƍcŊ,_G}/|_;老g%wp.ɲe˘kCT >#***1b*\$,ᮔZsLChx6V9=Bxw=?NI{Lk-բbyEK/>29? 'C3NѶNRS}opnp pIR'W+\D",Zgy&!qaI|K_"++eTUUs`67qEr1bӧOg„ lnZvŋ`*>ẅeiyoQRRˆ#(++SNa̘1 2vTU\\=_~7x'֟+tpoʃX_nIh~8,05O;QFbW'`Μ9r-|`WVtmyy/2oj\K8su#@C қt7zy>>нSlj;NA|/oq\B^lHII!##t<wlg1Ca MMMB!v͛ygn`֬YCI_ΐ!Cx'.rv\o¡ʩoWO^yXKꐖƴӧ11,^k'x\q'*55|+z'Nd!-"N!33 /gh<-[;wF+KaA!sMNvwO7;hb˰HO*cغu+^{-~!m_˲:u*u'\dN cTǨ]\C;mkCaSRR1LNy2,fxw'NLjYeee̚5[« 6pr-P^^}HH/ W2تJm ,^k5RׇAMl¤huc}5RCO7>D]s+y]&M|A72] >=>|]b}ӧw[<#w}TWW89r$n;zǴlSJJ \p پ};ӟxgعs'Q{~~>yyyB.u:D4uIh}1' 禛obРA^a:-ZСCٵ{o&]tQa#Ou]C=tucuy!Y?'Mg[ڝM<+]% S t6OjcpGpcӦM,[>?n#///5@~55 zu]ԩS(w鞏d Mz<:E<\{3*˺ELˬ'w2ߢ`wr2n 9 zWQ@:}V8̛7|%1PTT-BAAAfz뭬YP(Ĉ#8q"cv;e:9ÿ뿶Je^{7Y`0@aa!%%%pK8VNoWAea͚5\s5,Yb1 /1c(..x-ĉ9s;wnlx,JWu:r. ,J|}gԑ{6|ޣ\_̙3￟??g[QRRgf5XVkf6=;vK/ s DzbVrؖI*2>,3xp`r"ҧwmncx Zx߇$ .,8)T0 Sj`{?#=9ɳjTooz\5yR%RfZZ 83ffV}p=~GK6LQQl"] 87p+W"GWfks1z{y}G ɰ W>~J.jx4atXxxI b@sge*zBOt^3cE+`U`Vx.}z\dg%ޥwY1n/#X:4@?4"Aj Z=3=~;nƶ:ijj⩧{aqg Z OurmfҤI  ,(Qƈ#Sm4 z8@vNaoc [x:t('L_bU7sss9sfBf^yfΜɶmۢMч@n*bGƏZzz:& fεu]=755q]w1w\wOĢ]Y…  2z-O=yV$v?2ۼYm6XVeef2c V^ͺu~Ƃ x衇8餓Bϒ~ӟr}i愭z*z@HpQt6Q2\[VƤZ2~*T&,^$ IDATQ=7ڃV'A;3j4<7*,c/{ɷ1VNlbq'$X] 'D xg?Ref9aժU\{,_q7n%%X.WM/1swfa|^!C0IȈ?tL5le]q233ihlnfU^u+@3+IZY;y-xB%BdJ(0DO%#;)r'ŵH >h9S0(UaYG3\3#<5"sHg}o 0`@~8&ƶm^}UouVҘ8i1Qt IÇKFK D?֭[ L:Ypʸ`Z0|S;vl%#FPTTDFF]van߾=>SRR„ xg9t͚5}m6u{B*ɠALI n^7rƃ>HjH;wCQPPiFBBB%B,` Gee5gggvZkA,q)0ni!tߣvgӳgV^M~N^ ?!LNN橧d۶mXx17/K2U aV,X;<1gLIiDۺfm"Vݧ{%?؇`җ×I:땭R/Ӌj_!FJV kV,˛()ba±NO@(X|9/P _ ##g&L 333✲2Ǝɓ)))YӦkM0|QȟS#E_ U-вeKFEbb5tP>StޝlU!$8Bu5[JFWBe~ÇW"S+?ҬY3+_3Bu4-jUŠ(++ \0r"sXVjOႼP<*"l>cPU\: N*bHV^(Sl ^<^U! ^F߁t`RJ!Bs.$&&?ΦMxgINJLLdܸq<#믁ʊۼdddpԺ`E\Y`Nاup>3 fİ1ݛe觾zhT@"ʹ8D=4}6ie1d3τsXXp3k$~wy|^(27~\%Ҫ3i$iӦ ͛7?RF22fqrss=/TJsNn6 ܹ3III " hRrWc+~*q'@ `Ȑ!yUnRX^{ If*KÔEV7^R"v1'QR?TT՜yL!Ɋ$M̨bW"`&L);a6Gat k j Bt=}6k[~0x#Tbb"GfĈlzmK,",X8.1c2hoj[8SY$bx}Hnn-Ҙ-6ZQf3:w.Zt!)'\fR`؅CVV,X8Ѧ`(--eĉ߿3C\qJɮ]p=akRV,ʂOlh/U]:s5aO*|K=yא?~־ZVV7pI,Y~vmۡizK5pTY5+V`ۑaYLqqq9 @>)[K232Ð&BлwoZԐy[VVƬY@n)k\wuzuy<%?cI#td_]V(.B?@w s1GCFF0ذq#ӦMA1¼>}pM71cƌ0^SN4lzG`BLi̵Trc}/iZӼ}X֝o7~xq {9WGd >l(GotwF7T<NQ%z[`[`cRJ>Cl81C w>b7n' #[D*0 *..'cۢV!+ uThٲex^M6#Gu5Ƴ^zh7xlU8`5J;.\HZjHjLO>׼iӦcE '3*I7d9O&*U(E*)`=ƪs|2̣ˌQ4{؇+ٰ*)^tE?|xUII ?8֮aÆ$&$W+җsFf&aif&L@NNrI_W{֭[kfݻ_ÇG%{OvhV,X ۤo=.E^N{|J+C5.z;/Vɔx8`g/q[ ~9zqR=e JNddej±4زeFJ!dx ”9(M5j'33ƹH%TB)/#{0_WJ!gDJ0s~ 33$y=q Ha&#z~?0ȨEO>СC8vZ 7P-˗d2q:f D㢤0B7mѣݻw瞣 c)U YMܥ!q 2ZdϞ=3ꩰxה00 פ(Pic*^32,P1xa73 9! 2 _Z`<RfVQ|#zܰN{Vbܹ||>3O*4^I?ߤ  #3JKK3f ~mxq̾g}ƚ5k`A"iny\jKk wLwq+Q(;1k4KWo$BRtlc; ǛošCS\vet5؂֯_qƁrh!} !KJɵ]gnn7dee~a&`T$ |>wQ3w\H)iۮ-ߪ5c7<f̘IKO #H#I<)%s뭷VK&[oł HMMtV"ZB5L*06lXYg,[,̸ʰxP 1dE#p݊OX?ȪJ**@j'KO Ej(*h0=,Zp@סO+,ǿFO|g>s37! zL2K"E˖؄m Z1B_a6~b{ß Ү];ufs㏤aH+}E\','wa?_zUJɂ ذa(HJJbUè/͛cYz5>(!]MӸKرI>~\#˖->,ʂ#݊Ec_2D{]?YA H|8bDǰpBʹpcG+,w%I!R,ZiӦzr3; 뗎EbFLdvOib˖-! nUjtQR*=zy7\3i޼yic޽tarư/T!x衇9s%~.C&M鶰0)} 2lذjJKKy'پ};h:*%q!+f K꤬ y)CQ"]WY`Uv~Qc(LUʂ5izQ5kVN).]ʛotUf*2mvM60 >cCBBÆ 3kPc}D3-XpX8z_D'}qtt|3U{ĉrR/Ie+ ,@VV ,X8QxW d`[IaaiH uWzp76k˗/G G'pi=5Yr%iiih_yW+_+m۶1k,.+2FUq<ӜuYQ}[d 7|3yyydggTDF_4M6i\J># HLLx\( k&&k%$XY[N'!{.̛g‚cqkײn:3I^{-g̙3߰$''Iʟ@]*@V{Rm۶BideeizLr(MK.aĈՒcĉY xI%dM駟iР;w_$3ruﴴ4FŅ^HϞ=$?Ά R(ĤPfZ-@z T{~44"jNϷשq1^jsmY s;vl~2ql޼ّ$$$r(,,d@\~,\!;]wO?qik' GƸ IDATv|&jNH8sD z3(Rokd2( dD9~:[hettY-O9AQR@4d~[Ƀ -X`Å.BRIJvZ{= 0I)GVBJih駟ٳcڤk3#GtVy_'%%ƍ3lʂ Ɯ9s8p;v䩧JZ\}V~~U ܹsygX|9$$$ͦ !#I.]=.sTTTDnn#G鏒G;l.~iZ+/XE>@u0 KOOgǎ,^e˖qgNQ\vas2`ZiOdX``,ɩZg9նFo8;@?<ˡV/F4p/7lU~v_7x;/+yzI%-\D Չ.W*\s휯`f-d 'ڶ()bah/P(4MgϞ&Y~O]vaو3%Dait֍**UMAl6СC%R,X)SHIIaqճ۞=|Scuh8p1SLa͚5bu6lH||P+G;AUd.-"!!yijz'PLUGpl<2N?Ze*0fh`t:={'55ꫯR~}u4Xĕ I ^&i桻v&!hNprioד^ |VEGmk7]]ލJr饜o>F`ј'şn#ݦ]E<-^K.hdAN_}e‚?}q۷cG! 9~{=HKKLvŊ+BбcGn&s\dffO0`*=Xjw}7dgeѿOyyyl޼D|Yf1~xlق脕I5|!-[d#LWTB)~&M%+++Зh-*UQ=irj}MʚS~ DY ST- ,|JP""S)%Cѽ{ 7o}I ,X1XvؑѣGW=QYY&L ??&" XrbPv 񔗗3w\s@&MB}WrT%*+ Nu L~hm YvVyFq::ٹ\AioS$0ϗql4s&<KDctYWN|i:uV,,XСn}BWW'DuC||hC]\\̂ |$%'SZ귤L:nv3h |>nMӰlPJQPPO>Ɍ38pq񤦥ҶM[.Zɷ*bƍ]O W3}t),,R^/=⦛nyĸqѣ׿\5>Q _ W^ye}ںu+O<%%%dffCI0v5udP󠎌P2~xSr[駟矣i!2H[rqA-[ƶmhxu3:%KkxjMȝ+ sm)t[٣~W:h܆īhPkeԪvK_8bJ\K<1PI|iS7(%t"Ƕ1%Y֟F*+ '2`H+,Q:Z^y֬YC||<5KUKEN%C?+Uk2?jFU{R[U5+))'Ҿ}Znرc)))!--M!hD+v A9ݻٵkI\iF֭|(@"OH _ġo+fب_:{U|i@G9Fk6ȷ$8Wqnd/;r:k+E_7ն=>c;dI3?>4*G YbWP3a;y{$"8Ĵ`pV ,X#ѯ эbonkVٽ{7EEEhfff9VJwqs 7p)`uWR S0 ày1g-[Faa!^AFquY1y}kJJ 6]mxPPP{"",Xg`V¹؎`>!$)>V(c||`TE_׬R%B̶w ussji|m"H q9D A?[!8žxvJCwR;\fwX4&%hFc yݛ&[b-XСttBtQ~#!!4sc-dӦMaP*`]Oϡ5}N3sL|Mhٲ%iiihтF!`Ϟ=l۶~mFQQ23i蚟R?:zj֭[ᨳnG~i^quGw?_+"{1CRR 'TLF@tB43.R*F\TǦ ?ZT&SwQibDΰaøkne˖1uT ]OF3["aATv~r",Xg4;c#Pro`^d I`-mї]) $mwss'ݝ.\vɸ ('UGD=f/뽽H9极mwβ-%]8SF}= tEg[^+g2ል ([{XXpqm5x D"͚5UX3<1nczmvwzѴPYVRJu _?[N I'|ILL@*YkEyxtv8쳙{TѣիWהR2k,[t]f*ݎnѨaCwNvURSٳg֭c_ǍPl׋7 RĔ iV,X?c3#0fc{ F[X*"~ D$iI6~̾Уiξt(2N@ `k«SVޘ:‘;ܐPE#$9 !@N" Y1KFDyL-6mUW^Ug,9dnf+ED VqϋSsnK.o7a^Z<}TA0.gϞ7|sk4x?ŀ?I4b|ML+ID\ BW*qGRJʢ!8Xa",XPoC89(=ڧpe xӨ#T z1IE|.Oyh xݞmkim>HR߸!=Zxݞ_͒Z8Ѻ5\y% Z:aiaʶc{QU>*6tsHEPpZ(wAƍcꚔo͛7p8*HEzT^?"PyFZnMff) n{H8ټy3NmTc:uȑ#۷/#j?p8СǪW={rVIH)Yx1m #!p:q7{7^WgMm3$>]a ) :v!Cp]wJ??FJin 7Wg1`ɠ $P5kƸqHiLJJ #F mͼkJBkr:{̝9|}rE5zȝ8~vb5qvN=M41I¢v/i& ^Shc7R47׭XG3%+ ꝴCdvOgczqcz,x ~ԛSeܛ>:l8FЦ ڴZxWJ~PMh޼9.+KVYF;NVdz*u]Gݫ$ ?dݺurvɰ Ѻuk^{5~m:wRe&Zyy9Ǐ /d^OKKo4 JMM^[n!x<̞=n>-\3gdOMӸd@^{5rrr%m6sp?@V,\k.ve' K*hB=V9?=fI+|_W^,s]lЎaKC)/&&C#Z1 H;Mx} H1.&`! `+,7߿N мys֭[;"$k999dffeiꓕ$r3͆M1Dt Çkep*XD>1)Ǽ.**bɔrETsB7o? /dԨQf*E_Ve_yȨ{w;u֕4nݺLaaa׊O`ܸq4y뭷߿p3}ETଳfҤIf iԵkר&%%xK#g$ ^Zq-(((ШQ#N + #ž@dFyo~;l$p\]L#[ P'zm w h`^iiV,XOpBФIU _~ 5kFfPJa,3zJ"raR $FӦMy'~'xM9ɔ$%*LUuKΝcs?b TRE1x6GZ-Dž,a=xHII1 tRN7lbe͜9s+j壵i& &Mݻ̝ϟalػwoZlY-6uT6oތnCE٨"--I&HZ}fΜIbb"]vYSF1 5=?躎cƌӢEja3f~@4fW yHKKC)w}5\cV@ǃ:J47CcCF"ʕ+MJ)ENNNS`B0eFvctiIǧe|XT~_ZR±gx^,32^Z>QPtFKoz  2u=tbW74lÇ[q`>1|h,MsIzYjUXE2π#0ɖJ#^V% ;'1)ŋsӨQ#hԨcĈ$ nȞ BF#)sq_[}x饗(++G_)v{@R&M$+HZ'q8t֍W^yL﷕_'dΝ&MjGg\s gϦ]v5f)X~=7|3 ,p8"HGk*Xj}akzT+DΘ1k-8k,3kyo߾l6nVv;a0{l>C.?K/vlI&L4$֮]СC)((_~axK%9&e8ꯎAZ*Lѹs+sZĕ btTlv̑W'_/a1üw+^(Buj]P'|t{#ex} d`bq ?Y t`W@B  j'KhժUrPM6믿9Czz_V pPTM[o3j(4huSt:;8LJ)YٯJhX%l]ѣG؞ > #PBawТE n6z)B^amOr]6=z IDATxp$D4nӤILB-Bχ~A.D#4{=,X4]/G]wU y[laȐ!|wUX,3+B~~)L~gG}Dkefq:KV!^ٱcGui^z)#GIG}Ĉ#{m۶҂nb"O?4ݺu{wիW϶mۢ^o׮]L6 ʬ xU{0 \\tE;vi&t*hW,Y(`t1l8f+l0۰^{ryjq=qE2-[Z<]ޑqel:Ē.3o;KU_a,!h~؊ @eM4!33Ӕ q`ΝСCj*iRҪU+z-=馛&&Wd5PIU rJRRRL1cҤIx<tMC Nҋ,];3ٳgSt8uFdggךh ǤIҥ Bo?W\QmLׯ_ĉ)++CECK%%II<=iyZ oQJDcϩZ+oٳgadsa̙Q;7wf˖-U~?;wX~:&L 99)%sfݻl+1s1:5_dWYhJAYY=mbǎu]\2@F?V# RJڶmkqFvi7n\+VΙ @^͗.dٙ4-GaTlyEzQ_ y%vgK.r,$IbقO%oCXY=ul~0.gsIňe} ,\V+f_?vY`H!'ǿN.ТE RϹꪫLU||<7x#K,Ƽ9MIIavi6"o۶-999l޼vx4h߾=^_|l.cܸqrAFŋ|3Ac6um۶"`ǎ 2͋/H^^^ߢF?7믿rz)k 0IIġCxطoUiyL6l$?>ӛJ)Ŷm3qDy*5&M~6(v+bz_}W 6$ R\q?CO 8p =M6Ea̜9g}mJS,Y A fӦM,\$[nd$?>eeeBM4",X#]heg8EۊL^ Jd7ܝRiXy'6\?sG\+aVk6,Q0&dDYWz{Zqs~$A@UhMӃw=A9Xp 7zˊ G ?_['&fK.᫯O^^嚦ѯ_?Zn͚5k8DSmmv;<~lؼyu݃%oā87p U)ŧ~ݝwɨQQF;R<oe$''sWXm.9`~9mF׮]۷oꫯ9sдj <\y啵h?N"XTX|⪫";;F飏>bf6R~RW^k4lذjÆ L0So3gsNNϞ=Xp!O^uo߾2M*_#k6x\(b5Z4)%͛7Tys۶m|駸n`5J"-ʂ M̗.DY?S4wJa-3Ǟ|h$1i`M!Xh$cəc;+kbQ|Q&;<-Y ;EHbiuf};TیMѢN JU+==iBzp"+a4Xڊ SN9a¡igqlݺ{|tܰ6mڔ[oÇI ́oE0':dhۦ-W]uUHEbb"R ULaϹ!_%.ɋg5t:?aVGyiӦxMJAY/B8pI6mbС3eʔj{nz)LCI.x e5X&aEkѢE6l؀0i$l,]9s搒Cdq^V^jqE 8ݻwSN:w={6 <#|qpL0 fX,̟?;4YX4{Xj.y x`qǐ2,]FvɼDGGۗ[trlo6{FQ}8]fsܹtLF Ɛ!C0 \zGҶmǵw^>3,);+IԫWI&o{fO?ׯ;V@pp0-#IQQQX!2k~7к/СC1 9rsI4Mz92_/:ԩSٷo7FUU\>}:[%z5nܘɓ'3d 5,fئMңG'Q4998\riӦXV%=EEM朦pVS8)Dj L7BCala8z_iN}777ƌc\4yUYJJ ֭Ȋ$IԩSXVG&66G _ɓvZFqQVBe4'O2`QU2eSO+~EGGaÆ\RN zbҤIq%NŊ cy<DPP3g71{:M"^̘1#IGe a,X `AGOOOFᅬgΜaСa*FSZ|ݵk˖-cΝ|̝;/ǏgcCe{1*UT|H'm۶m۶E^;C``ܵkwFN /Pd1ߎ@  K}ysa (4o/҃QQtBڵ9{<͝;g:m4h;#?]J\Rp*|tؑٳg;{2sZy7Pb;uC寿IPvmׯxZtoߞ]C 6^PQhԨuM?8zh ̝;www;F>}+PdJJ?qd֬Y< /III̙3{{y1uT2d-Z(Pɪw{[e3'OÃs9reG6mԩSˬYsTTUUS f͚yڧlٲԭ[7_eU\h~-cƌ`0vfrsc^ʔ)S %I\rիJ AQ>~<-[`0SOb6ood]ҩS't=:l6PY$&&rq+&L@޽yGxٱcGnOb+?^H~L&Zn]dɒDhh(gӳ?]F#;w7$f_gan8HϞ=ҥKDDD3ʘƢEHHHW1b={,/sNJx̙3!C( _}=z@ŋIJJs\bĉF.\ٹs'6 I(Uov*_~s˾F'|5k潎őj$Uƚ5kӧfUVaX(,˴jՊÇ;Q65kq#(tޝ;@; @ ƍa a0̘af`Ek׮4jmaXꫯضm率3f̠YfN qy'-UUYz5͛7o߾L>sifkϘBy8'.U4o~Eyy ݻaZ Ee^jϽ s_/SNZjl,_e˖9RN|۹qǎvza2ի , 00 ONxx8AAAԨQqFDDp< :t`ܸqL7.. 4 >C^yz=;wdС?5k\4ۗg9իWyl0ׯ5sαpBsV /O/w^R^v%5jj*{1TUeҥ_>GmeP̙O>c,f.d$_Ö@ E@,-];TR7?3g8}6$$>J*9ZرR&2-[$00TGTamy]P>}:=XԩSiYxzz$IܼyׯS;7}3 s۷og޽5… ӧzK.1qDbcc$P9f3WvmjI2|G"XF$Ze=WZ,ZSNZߒ,ѴiS6mq\+B:uXv-M4d֬Y.yVY0n8P_oQ|e"B@ (*vOlߠ lۯQݱioӦ =piƉ':ufX$5jĂ (]Lja՜?Zj1b 12HqsscȑٳP!sNzͮ]s6i$4.)d݂6mZ mw^^\.v1rU4 `۸aZ裏3g!uUm"qNm۶,X4M#)))RIII|'#IKO>K.t:8q#ϗ^'$$@ɓ޽;zNHCUƆ ݻ7zxƏt& &Pl|Yn$ 2$0St eڵiw7ƍyɜ>'pbaƌNdYSOѽ{wQI0U@ C:0@׈iC>zhWڬV[.\y>C!g_K,qlEa <8|M!9%5jǏ/TV+VХK"""\ ,aX||| r-h2i$a6p@Ξ=FU TUɓN( *U?O>Fh5i$ )2 Ν;ǹsrOO駟Rti<ƍm>|2egѱcGE!55)Sϱvl6&4(犇R^J`i&lܸ'|EQy&ÇgNyt:ݺum۶*))>kJhذ!?xm$&&xb3 k׎M7Q~}^qDFF涋oWjY0@UU+VX93[8vx|||č2p%@P\Fv3zP׬Y3f8ri@rR23g%t՞/)(X,f-[7|8͑GyUpMN( eˆd&LP(OK.ꫯ2tPbccB4֭[ĠiJ" gEL2,^#FZSU^z'O.SOl0LkF4i?>(XV>C&MTQTn,PJHHd2֭k֬L2hO^(SLb>|LÆ ټy3ڵCeҘ8q"S~(͖]lrpɲ7Ç~pTk_/}[rʌ=:_4UUټy3?S.1T\C WJXUP$x7Ynʕs/:*`qeg;m O/OON~UMعs'^{RNJ 0j<@ka(!Ƚŕ$ڵkǛox{SرcrK.|G:6R6@ƍǎ;{xx0e>S*T([)d2vC׮]ٱc=z()##KҾ}{GuDEjAAA<FǗd˛nݺoѽ{+eX͛G>}\z2ebfbbbcرlٲZjΌ3:u*)))}111ۇ~Z& IDATNCdǿ9s&| l6-Z/L\\\"bVedze4h$I$''[oިtɔ]]`c̜֭9TUGlݺa zcƌ)0A~TTs̱{JIJ*4k,6Yd  Wκu2e ^^^l6/^LpBk||5sKo?ܸqў`{ի!*ɓ' 3ϜO #`LۚE^5o WVtiXʕ(@ \x1s+#G裏,Ԯ]roZHHH`ΝTP5k"Iݻ( gΜASLDB$EAc0},t9;|/ʶmx7X`ot2M%--{___ׯOTTTZ=z`_8RqE … s'r> .]) HĎ;dŊ!ݼycDz`'OFjj*?x ڳ`EFf駟Ҿ}{F#IIIL0YfD@@/"!!!yWvm7ocǏgٲeDӰa|,Ocǎ1&6mڰh"FMʕedfϞرcs }o߾@>… Y~Z,QV-^y|8y`РA,[0E֭[L<ӧ= ,`ۛ & =<Æ sxxHLX,\AF ;0|' ; #_gv<}؈˲LFXt#Atv`Ν׏K.)t:~a^{5zZiN}Çٸq#[nСCSY`A+eC\\k֬aDDD`(VWQwμya۶mX"Ee~aFEN ]IZ|MIi֬)~w+{\#Ft1 `-J?gs'EQU˖-I&DÃ^{ބ2B*BJ$9ҞG ӧ̛^^DUU[TOd6m'|Bݺusmcbb6l6m"---OD$G(Tpp0ZRJTT@eZZqet{%11 EAӴ"&*`Lǎy衇(S B牌d׮]DGG(J!DlcgX,c2h׮M4vڎpӧO?ߓVȬ>xiٲ%ժULJ[nqm͛1!Iٓ{ wwwn޼IDD۷oHTqf0ڴiCӦMVʕ MӸ~:gΜa׮]l߾{:,h><==9s&͛7WL_h˺>24>>̝;}駟Xt)[nMX+MgQJ\\x!CΝ+R&1 B*!\ AAW%čЯl&l!BU GUU LlM'nݺ̟?-[ܧl2&L@RRR>B}]UUGxY 2$9BboR3Ǚ>Y [u38%>t:>Ӈ:E$]7Yf#$$///TU%22ұؽ'Xk4ݻ7-ZvHDdd$_%"""[7Çr{jFDDFbΝNm{zz+XSB*!\ AAW%Э;&l! C%f믿fРAܺuM bt-j%<<$ӭxG-qpQ,nΧ$!ȼgP&'( LUh6]zkLQX`;v%>i/94Ѭ;fbB*&0@ ]reXl$#)_޾hU(B.]Xlrw6mNt:ZlΝ;ݻ7nnn.îV-H%޽V&Caݡh%ݝ+V1KwLbHKK#55KOle֭ٽ{7]v%>Y,,YB^x*Yw{xAhuJ n W߁?Ze_ۂߜ2:ubŊ+W)4.11w}aÆqe獶$QlY,Yʕ+)W͡s iNkJ@=c*,hPQ7o| ժU[n1fFAl\(̒OV%qo&@ 4ib<<-l<iF\\,Ə_~}p)СC:tW\qh4ӧ6m^z&VST,QUP  ~7/嗅->w:222XnozͲ(2x饗0WU/F750.o=%Q$ F߸[sX&֊}k{$F#<s!,, Nڰa'M3l69Hxxx0aD R< `p%!\T?OBgrD}G4:ĠA-ADR,R!\$L&*T`8p{Υh_ڵk˱Z*Kz饗ؾ};Z|u&xrJ:?S_1\e!۷,hbqth9dI=0j8Ĕ>x(e}} ڹOIbp|E8! E.CDvߕ+kTV;2e 7nrÚTU%u&NHǎo4ӓN:ʕ+^:& (yzΥ5xJۄŜsrL&e˖?&""޽{痻Mc 5gϢfE!((+VpBBBBčn6 <ܼLtCesJ}KOaP*.u$d|[f4(ś Y#AJԯI<"[7XZBbj^O۶mٳgÆh4:i+޳Ν;/r\ek׮8p/5j`0;{)Bb)"*&բF#<ڝ$pF}Y1 /_%KpR8y$M6OX,4MC|77 kW^L&qCLMI.]`:a þt!*UٳgO?QfMʃ}ax Fӧ]?ٸq#?8&B!Z?]hEVgjtW:O_Iwڻkfkw6'dYd2QvmVZEDD}St&ME,_UUzʄ~zVXA  Lm"K pWhFz8 5QQ1ȴC:A c0s# 6ȚM ]+yAM*t?nFU1cJG΋ƝKL T⁙x46M[)ϰKs.m`ro ۤaɱ:=tz)W⍭vpE{&dGB_x lXWf-@ڏUW%^a1`0P,~UաꇠILF2o0},?C߈GXa=DAhfшA`ߑLٴVXd)x>գI/ogwZEU"HJJ214eHKH%,eӐίa¯$2dRQ Vl{fA1*~(S7r*͵j'Lk {|n'&z^)$˅jטg LÚ"_;H)H<Ӡcա4 tG} .NBp5kuka8{ڵklz;$!29~Qr<۳l\xիW3oC62V{Yc@6Hfg#Z` i֘Y} ]PH6% ыcub3͑?g<š?V4MYq'kIdFEqT$9D+d[5+xa蒢x}$N#eв!sgqeVm=ˁS7\ jU;*]Ӯi} gC$Q[W2x"I-{B2ˢ ?tU0S9u0G}n^<579(IP;eV/>Ōla!xdK$YrRI (Z^uaݧti{x`f$IZj,]3ydC9ÞGӰl$''1sL-[yVݮ0ۤJ*L8!Co>͛Ǯ]e sF^ybd6 -tٓ*! 3l^Yl_KY^z=-[RL5˗/_xb*fӚFj׮ʹihݺcxZ6 IDATw3 LBh/_[-$bc\F-ne:cBjkVڼ9\>30#YU%!hDQSTGD:p]Si275@Mc!맬HtkŨ(TWßaˮ=ٚ*m치U9pK*QP"y,ċYkd lqwq}qgUΦ1QWi6 Q*i6eÉ+OSvQLň5YQ|Sr"yCefqlR>ښ(THZT@ln#:3^~eh6иjֈ\Ͱ1Hy ӖN'3}-,4QLla LzjCKOk8ˣR:t]yҬ|5!+ךuzZhU+V4X,\v{5j0j(>gAAAs{4C.a~՟#Z41̀As^Եv_LUX{-Fz:LǰhYJJnXXAyx_#GRs4{`Mv'%bQ%vʖk}ݸT3>$Q֨PCGmw&ɤI^Tb".94g/r4QT~"oEr)캘@Ӻa$c|i_nk_;ݺu?pT `4>0?@ZZ6 jbʔ)DFF}v^z%L&SKgI* rF r]ߤ}D*ƴ"IW:^Cl޼(ϟOqsssybaǎ 2кu:u*iii5o-[KsQ (,LzmW"%.L[= -hEuqʰNsyO<r 쿚AFYy<[6 ?-{A-;7؃ Yx6Mj}2E-jkЪŦ2P2V5MF|:}Y40[mU1ЍQmҰ&$lUnMQ2ߥOp Xs*dUbY/*%xRTUKySh^a .sF^z) | FLҼS.YYeÑLGٗ|DMC &>y|{=Ĕ8ƪqo'@*V>#aFaGV???tB6mؼy3'NŋꨤfɬlΞ={;v,knaƍ($IwKY+q0O͡zY+l_S%6`NwfZl믿N&M(]tbi=z7tR^uu3ydvJ``9Jw\.YYo'm 16,lDFr/MR\$VD$J27ìCWѪ }Z6jI,ÆUӁMdV󢼻F\Re+ hnS1$R;gMi/$ICo 8GE+Md-\fաh >ZkF[XpTbm}}5hU 8 9܍2x{|;pJGLFz9qek? n:܍$sqSj*s4HdS:>}~h0%1h%vda@F-54oF0h]==sbb&qbZl$os^fEzi,=F . ]+*zs=ǟɸq?el6̝;9sиqcO?MժU|w ]vmۖXΜ9_ͧ~JbR"NXGĔ".[OMŭ\AcA,EQMU1Ջ>}PV- l_~aժU(jh[aZjUfϞM-7"J BP#-tnj ,xxrstY-H(tsl^%Mdpbcǀ;x[E6#PVH~MlB~Tvի͋u W ֧}Ew[يh$r%g;\ lcRu뮓`{.ӶTݛW̯I=)6:rjWqU*H"-LE'9>XFޘ}ؤܮ:U FޤixNB]I١~Jt2$H99<\%Yq&MB+x Bh6G+&>14oP\wXByA9Qz 6 wx}=ZBPJ~Ko.*'= oń29Ru5tqB^xPCi+GJQ 85r(~34kiUY|֒ =˕>;(WD}{LMmk4BrhtWԵG:SUQo)~@uo^\9TXO-w"K\GCSyzoOX >yxom^y0t1ztRJ QivUYO.ҏ jnK^oMW;L];uPFLF,6}mj._WM#!I/=NS~(f%Yh@=<KTXe*1ɩ{雙x8. rGYtPSOajw&}^a֩BC3ߋqah *R[Ouϰ:OTFokϓ4]T}`!9ѡK]H?N=tO?-wP +!!AW^y{Lo3T4epU_CkIf͚'ꪫR. tICСC_ӢEh"tp( ~9Y'/|zr:w<,R0ĉ5uب*i׮Z~VXW_}UǏT8,ŦJv,i_=C1c/Ap ~D 9a Ѵ%CUVq4fE&DžQ~Z}зvg \gI%(@",Wc5-omZ@C %_{ ޓ穦2;GHaIQ`Z7;cwӼtϨ,#CQ+cG㭚goK/h.{p$Hhv}%5|˲ Gg5+Z,Kyz/WdB ߲Qqܚ|}FV/Z*?Q\?͞r$:UqX/.ک5'GřSI`&V*&=61SߛtflJTUSkWUjИA}[][~gJBuϩl'umY4Kpv/HGtnPPSpZIOԔLE%[ND?.Tgsؒ" B*fs:RVc7џ~$U%T=vh $]VtB[G͞QR5V C%5zz| dx=)b86ڼ{~⨊hcoPȯoۢMy2 v* լY7w4u 4MW>fϞ-[גKrD B,+^z)bM0AJKKkp[#+''G<󌊋?X ,ݻt:cpy6+2~eZYXˤig M7w 9R(V߾}5m4]{P\>%%%ڼy>C͝;WUUU'FcCl!өP(1רQ_FVs:SG6ٴtozekDiK!Ynm&S~E%R#+29CK7"Lk  6YNXN=Ͱ*m}ݿT}PVJK4ϲ m/ewO҉Jm*0`פGCIVDMh|pԝ=a$"X_ Cݦ -WD`_XOM7ѩ:++RUkRyQa(3ţΈ|a Z Z4+ѡ˺'WCG|ZHO~3AP)U+ah@GWzTkaXj7G CmPsИj*>Չvd(2zKra]tRJB:χAɦVDbS;{'ؽ%?nr{&:5wu6BURJ{ɮ.KWF뜓]ܦ8B 3a(]jHVYuj5ZDT^P~u4aSzzD"* eEUUJN͓&Mvh rJ?S%˥p(>t rݺu 7hԨQKp4/d0TIIuV^ZϷFc"[&dFucFlڟi$M2EƍӨQzz޷eڰa>#-^Xr 7t?vMO<񄮻:edd4y$pֿ%0@?OQi/&}ϸn|Bm1/u҂hZx+(?S\2Iw-R |)=ZEUWW?׼y4o}ΫG*//O/֜9stqy0USS{C QNNwÇo~JNNVnԡCvWQQ\޽[k֬իgC.S`v\cr}iʔ)̔凮!joŝ>"-;v.Ԥ;*1m091ΐihJם*Z !jwOSL.=\`ڻw,Yk߾}r\b.A!40MBc&OQF)++KӧO-D"TMMJJJtQرC+V^<4e|GQ2M\.gde) /{V޽vu+++Kԩz衤$%''Hmᰊ;wjÆ z7$IN36ta ѣM)ShРAgd#j+@{ApUVJ3Ԣ=yQGJ PaavLo,X ˲bӾ|XAGӧkĈKt%''~a|>U\"釒Z۷O7nԺu짤XP Oio Cn[nw- J Ms8߿t"UDzN~H_|mۦsƎi2MSH4*'MiӦ+P߾}p ^\҃RGrӻͲG7n[o+WJ<BQKM{KHH3hTNN>}W^ڵkV;e¬ Î VIDAT ~tNNgl׿VR Pyy>㏕;wJ(-K4t:cA9RvƎ~~& WmD4*-Yb7m?9bmLr+ɒI;>HDeee*,,۵d-{YR,t !<O$1!Qߚ-eddhҥ."edd(%%唑O>Ov޽{' qm5fv4M~I_[nE#FP>}ԭ[s2 mU\ 6fZi4jі!/رmeY*--Uqqvڥ?PϏ5 8j5vZ4T0#GjܸqٳRSSկ_?%&&sαN_؍y j۔maUUUD>O~_ӡCqFZsnpn |0;4qD]veٳ^y) UҬYҦMԢ-Ζ^zIPyy${am՜NgzJLLTjjzr)%%E;wRzzz,JHH]2+XSH$ UWW۷O媪Rqq!Cip4!2d:r8uV[5f3FiivX & WmԡCzM,wRZ@USSR(??_k׮Ֆ-[~h#P0lp%ˑL_(٣ڳjҤI LEQ^Z>ﴏu8$xdP GIY,Igrt8reɒѣ5|pzڵ:8!& Wm'=4gh~:wD"|>;vLڿrssjup8d:vsrRsWZ3ZjJ\툰Z-qnh+0$CF뎺+5a  T9<9 1Z'_ nc Sh)AЗ|F_ؙT8blpC95  "1c<6Ʈ廱JNg6|/2KսW5[=d~YZ04rIOag Zְ&p4_!P_'$O1 ᗸ\Xp&MTrε #{/ Gdr,K)w;Y+x rr#}EE'=`ip < gl ?;ebє4/3+I !N 57i~I҂!*_ | ulY6`E$m^8ZosJmG ]$8 -`̣9r¥ n&*g3A#Vs/>ׂ/}N\_Rifٮ1JA:;tzE.vRAԤ/.&pm:,f95J^FR*Ѭ3GPsqMks)mI^apKՌ2s1?MIik45%/F향8|ʍ\+3bLoLYTa D#'kϚm<@.2bU89ZyDJCžy#@ ci'kMXbd#4Wfje-m=5s)bN3];(PҠ[Ԑ0neڎzĆ 9{fLԵ4TcbR {s,9 Q  %a(e)Ī+; UfIp dQ%Wn^꿟aA'dTj#5oW7VߺCh"I5ھ%W.[DWj0uʱ)]NXu"!uv8E<QB5'Qi%N9Uo{>$*4gz>#NAr ͕j]ڬ@=UӔޒ+`~+Vi*V)D սFp4[,yę&h\@@;ru93Q /|3#(:`}:J'ʅɫqt4+L#)a s G/*v4-.WcK\67ym\7&U? yv +j-S>נ+R%*nwUI;^:+1-zyQ ?$arn!Pw, }.cWQhC|mj-Ϫ׿0~Tk &k( "~k\ҏͬhEX8v(#*)6KXwl`Z[L7QP14O~֗?0M=? mZs45I8|!l+ՎR"]<6~\(H2',<^2V9E/aκ*IN]"l֟'ցj3q@(Ly:ؓRwM,g޿Jbz-'26etK_wǡaPJHŌe=lT3ZQ^Qٞ.{o'4Jg*Q&n ()d/Y pa3+LO0I̍@ٚ&Rx.xz(CPt;ݗ˥#v.7ML8ci'OWzXD$]%yW˹Dy8$'Ϲ:w6॔5ZhmYsa/I.~袋l{$0KKb{uKt }0N3Y KɶN`0D)}[& S"mDbEPz3+Y_y,`W;{'(Sj&gNW1 u [³28eq0I9{t^DH[໯^b,E@a{&]y A;Ye+A*.| d{kGAX=Ѿc33<O(z2-a98bcSޫ539\!Fi˥նqw*xÚzg1,,>F-]xCfMqQ'--dip9G5CInaXEgZ'N%pv9+9(ŦZbHqQ&(u2tb$QUN6[c<FkI@( LTm4́(ݱo O,`;=m %&΢`.K=pBu_ cs!<~ N?0j]Ώ1$jk;s`RPIݏ ޝ17rItG'uO*A2^A,XsܝaRay 4 #n䪤orkmK ipodRS uLT31EQJqxp=v=͊#TX.48d-dU4xS A2KǃhqsdnJ9Lrxp0/Ep‡<);~$hjXY@ORz7c|9rs}%|&2 [`987 |6vs&}U[̍fnUe30lN hmGɬڥiB~J|A`wl)+ui]Vs7tc .vFXto| d2CɬA`GlߏM.͑pXsOPڳ/2k|!jvpi\Lǯ˩<)I|2YN I3ef Wb8 4 9{큓hεM\g j!S6k*x QHX528J!)/6*NJ6Xc6 \fJLzadDR)A;#`5={;lzcE,UJE'CNtT:1  АR!# |n>]ӲNBw}/d$AurjrA9 )AN9rtЬK])IENDB`isort-9.0.1/art/logo.xcf0000644000000000000000000002256013615410400012036 0ustar00gimp xcf v011BBG gimp-commentCreated with GIMPgimp-image-grid(style solid) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches)  9Ci!? "     7%$#gimp-text-layer(markup "i") (font "Sans-serif") (font-size 62.000000) (font-size-unit pixels) (antialias yes) (language "en-us") (base-direction ltr) (color (color-rgb 0.000000 0.000000 0.000000)) (justify left) (box-mode fixed) (box-width 57.000000) (box-height 67.000000) (box-unit pixels) (hinting yes) y9C y9C i[F0P.-s- , , ,v-.T0N$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$[0. - , + + , - .0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . [B0P.-u, , , -w-.U0I////////////////////////////////[!0FF.!"-,++,-$%.LK0$#[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[! Pasted Layer!? "     %$#  %X%d  %(%0%@%P6r.` +$~(m&,$8"3 a+\ !+"@$S%^&e'_(V)C*,+,,^-+./ a/ !0 . 2'¤X8%ٝD:#S"D5 i ""q=*8is@>@l j"  %{ B5 Z p!!!!utuvy{}.tuy| +tu{(tuz&tw}$tx~"tx tu~t{twt|!tu"tw#tz%tu|&t}'t~(t~)tu|*t{+ty,tw,tu-tu~.ty/ tv/ tu~0 tx1 tu/ tz)|ywvtu&~zut{$|utu"|ut{"vtu!|ty ytuxtvxt|ytu{twt{vtu{tuutxzt|utu|tuvtv tx{ tzw t|u t~utu} tu{!ty!tw!twtqeYLE>8<.jE 6+l@6(h:6&~F6$q86"p76 y86E6c76@ 6m7!6T#6B$6:%67&67'67(67)68*6>+6J,6b-67-6:.6 R/6 ~06 <06 a.6;D: 7'6:` S%6;m7#6HR"6>7!6f\ 6786@r6B@6@767d64=6e6@p65G6N8646Iu6U65@6a:6 64 6C 6d!s65z h65 ]64 R64 L64μD5 ӱ{"0 ԡI-ڜ5*d({&y$^"* t"K!u"#$%&'(})]*2*+,K,-I-,W$=f%j"J}<+"j #&W&0b*66Nr 65} _6NrN6o!} 6h`N 6` 6N 6S 76G :67!>62 @63??t??m!i!n!x!!!!s!\& DO ' !8  n?ӹ O Ӊ>% ! >0s 418 m34{4:<45m6b 7(M1b& 7O܊,<f- m˫[8u " H%$ I$H$u%%,%O&&&7&b'''M&F%4$"!L R }"'$/&")c+y/] 34ptuv tuv tuw!tw!tx tuz tu| tu~!t~u t|u tzy tw} tv tuytu~ut|} {zutx xu-tu }u/tu u1t| w2tw ~3tux4t{u4tv~5tuz6tyv6tuu tz{yutz~ tvzutuy tzwt{u t~xutu tu{vtz| tw~zxvtuw t{" twu t~$ tu{ tu$tw%t|%t%tu&ty&t~&tu&tv'tz't~'t{'tz&ty$tw#tu~"tuz tvtz tu~"tw~$tw~&tv}(tuz+tvz.tux{ 2tuvy{|~tG62 B64 F62 K63 Q64 [65 g65!r6t!6Z 68 64 865y >6IU64 u6j 6h 8 6h,F 60p 6h1 62=6r3e6476N4B6r6u66866_6N76r 6?V6 6^76 r6jX66hN6V86 6Uzg6 !6:E< Rr$6 N$6 %6%6h%6&6&6r&6N&6'6'6r'68&6>%6H$6^#6x7!6F 6m76M6 =6"w:6$x=6&J6)l<6+nB6.kG7 63qdXKB<7;^ d ` X M!5 . <* b   ) n} I9&,j@*}M86/8&Jw G5 "[(# p%]  ;f'-D--",i++"*Q)('&"%.$5#."&! j5 =!o#%&ԃ)V+ r.śS1ʶq&tuv tuv tuu tvv tvu twu tyu t|!t~u t~ tv{ tzytuwtuut|utu~tzztuvt{tw|twtu|ty~ty wtz utuz!ztu~!utx"|tu~#vtw}%|tuz&utvxz})|t.ut.yt-~t,vt,zt+~t*ut)xt)zt(|t'~t&~ut%~ut$~ut#|ut"{ut xtvt|utx t|u!tv#tw%tyu&t~x)t{u+t |w.t }yu1t~}|ywutA62 A63 A62 @63!>62 :68 76G 6T {6d \6s!>65 765 t64B676XY6v765[6476TH65zm6576H:65yB64B6Z>64}7 6F^"6|8"6?~@$6}h8%6@ _8'65.6L-64-6i,6A+65+6l*6P)6<(65'64&65v&6o%6o$6r#6|!67 6D6]67~6T 6`1@HUam{??t??ttt@A isort-9.0.1/art/logo_5.png0000644000000000000000000012433213615410400012266 0ustar00PNG  IHDRz0[zTXtRaw profile type exifxڥi#)s988qGʬʜnO%C|y"S E[_eC^S,/CQxS>^;NyO b^Hw B{,ߑ;ry =ܤjMZ$V҈Ds{ sSf?ɳWx̜s)c \y|c]Gx,[o_h~[=#r,?O~QLF糺Y*KEXb Yx*{ v٤⎋N# )$K3ݐfE4Ew!'Ou.J#ɗ)Q7|޸㍷SgdK*W9qRJEH}$X왆g_9 xZfR/̝θ>X9OyXj_02IA)k)6$h%Yd 1I)9WrC0t7-sEs$+$Y(Jf-ZiסZVkVZms/]{>22hv}1'cN5J$6R}\%}uM]Hd\#@T>$p!)Мl V68Q%]tn̄4PVEX,(ɶ%U5}M'% jrP!Uyt*ھehMm 88Z`ӡ$Y u<`s\3rmT)\$?ʸ(^kBd: T]:>|i 8A  RB蟨 I^$7@ou@Ҿ@ o3βZ+d*:®t >yΓɀ,BJ h)=@)uT4QTtU9%A$x-*솁;80bJ6U@=?)k* "\zZG :GЦ钺Z ?f[p'w^h5g9>0L,`nh6r .Ƒ_Yq?ܙ$7}EI,ڈa:pHHڌ5XM"ZXЀ){LԸ:a@ !܅SrYb]2j0M7DĻi8CA/jɶ]#BBfnaF;), B 8}.NF3WhًZ8Ĭ=HUe ( ==2.a~: *ƬR72: Y:= QtL'^VT?KDPAPN& uNpC#\Z,璔@LVGq aa=/[J')4AR@K tzU>:ވ7Đ%WrT0")E\Zcts7:2bA4Pf)>NLs]$G$@UOnBV:1 z$߅F}w ;@G᎑hCod s@Dr%tu $G@L C7P ,v4wP6a;lsh2Ia&~}7JzȯF5 >WFYVG/.Z Y.,a}jWRq(C3k)Rlr2KS4) |Ec}\BrG(nё8{p&FH`2^K5he*,b` ͅ5N< M_luٜ`| ؅PMI/V7"EXKLPej@"}q-`JTP\u|>viG݋~8ߌs^wt;d:z!ˆrd&A@|~.AS3/%#oM03F "q PIe 1Dwzޒhwh58SOBs]Gq{6q7pZ#D%z!07jD|txy<\r\U:bё.(K]^&he(M=,u~ݗ'%)Df߃DecNA(!Ptdosvl1 "R!7۵t2TVeoyv׷%A gV򈯠;D Vqwn-<` 6]m*~[I5 yRohwF.Ь;4O3p IzŎm⺭){0dȦJAB7[g뭵 K]oC`Dk>3~mr]bKGDC pHYs  tIME&.WtEXtCommentCreated with GIMPW IDATxwչ93I^{/` fm1B z pSIKI(~Ini\zIL(wcq/nתϜޢժ~޼WH9gysDw_ B!R2(,B!BB!BB!BB!BB!BB!BEB!BHaccB!T,2B#B!8.[LPv)zB!ؑʚG#B!$rGLnR(zB!d(tsi>!B!XŮpDP(zB!\h#zWG#B!$Y1i~dXErQ!B)bW"(R>EB!P2!xBBYo"۔=!BD#o (GH2eG!B(y]J(uY /Q(zB!K1eDnɚeS({=B!Bɋ'y/%F\j&u2Q(zB!G^b_rR(zB!8_O !x2ʟ!BDi"Į%Q(zB!" ɋ/xOINIE)~G#B!Md%m0m=_7/EtK({G!B&-y2HKP=|OУo.| eQ=!B)=[1%/J/Bet#EF"JRt(HlS(zB!KDq:{([Q"M#=B!RbHɋ%xN2mU)u:!u^,R!BHqD)H^^̩DT1='#x"IK`wG!BJGelEMBbDJwHlL^_J=Eed7E{BtFt~|gQ=!B)Q\dtNv!Fp c4'T6!$O38J{7I5 %|NP(zb3Ը #08,!"\ŋ:e T7 &j(x;AR`8 LiY9R$ QtVlZ-B}ޢt}ETaC!E!LVxom nԷɴp 8KEѣQ)4T1U<= v!s/W!x8E_VEEt٩ x=6_M!yQEB 1čb$2^|J:39F9`cTyBs@V)A幏B g[bSlkYh uRCED4]+!} E@!ͶC8xhLiuD!(aԪe 264)2{ߪس'z{<ܻDEґ¼p#*."sEH+]H@[ЁC!{DGB6y ]vޑovpuD!7k0Y{?—CCqp,.֍WA<<"cSN"٣mPTȖ}idN {%ѼBH&Yy o dBH@uT}UWqsx gtZbnO0BD׿B[#"Q(%7 oŐdnG#$lt[dauC!$ Qnw }1J[`ݮ2^&k3 -mV!Ln.x.u Jbɋ#|Bt1ʧ!K^j)4>!91&VHWFn\gTn!@|،iˋޠcm ` Fɦ'#}B{qb+xgńGoB_z,M&OR(\6T}4wg -Ť܎ɁV5!!HE--R!vqhE|x+uin} 3B!t5w2*ywCL($PuʒHٌF_`tEHSOyݥ,a$Q6Q(z߂Z1DD o'S|<̣n/Q2E@HW/2?;U:{y%}؎{VW| !`#B"LʬE$ڼAtK uWOkJUlDoS(zk?؃K ǍIPIݴuf,\[B@R!P;Qd+qBNJ Qt͞'JZD/Q)VLw»w|WLGè2/åk0T4hlq@Aq/m{ :ˑBHp;Iҁ~JfC[F!n=+V9Z E>$?!ߥ)ŽM&DlFJ^J ǶG!BHjBr"[?݊&^r?XJHvD"=B!2V[7Tw"o&=B!Rrݓ=ksIوzloI0eB3(=O;!B ˌгQO-۔:B#B!(t1$X665N#k@Ѝz֤="G#B!$F5Y{#m̟(j=B!$`bڂJ +nSQ.p 7: ? `%5X0`eh8`Ū{f9J^)YSF~kת:QEqS)R܄P! l8I=q^P?ChN*P wLs–&ݡfIJP0+f#Ґ?EƮeQp[ѦSH9*B#RT?w$m;R?A <9i JCC;B24 OaX9+ PiYr nO0eS(xG!¥noZ8HHEhK0D[:D94;J#$ G^UThjDcf%P)Kٰr8YI:dS~&_ TIj Ni N ᅆ D6 4ZCT)iQz5}#jah*fKזbm,*p-g+hrI2լAR+NiB? C&auVNw|SА^U{1C-kU؆mt5\<0,C藵Ț6'tB#dq`mƪ1X.(6gg7TKKѶ&ܤڇpHшp</fUՑ|Xf~gV)^k;)}kۆb;obEN!4dMtє:=Kt%H_BROOBTQdO݉nmB XjVk%De; 3pPWo'į&i @MnJA[CIER(uf\@Q]*l9x0TGً [UD{(:m)~-%Y:Z62Vbnz}+Nbj䟲[ʕu`6#j겣2k4fId2m)G3Rq&FƢ4([p AqF*<`lƷ!K{_Q;Gq8\- `:~_f;G͸XRT堟e/q%xھ9[O]lŤlf#y Oה2qD WeBCx(4"͑4Λmz_m]%/yDf۟pRd<^}[/{//js"ydϱu)F ;doY`eZ;"@Rd$PWfZײIK^deBH8?£ο`/vq~v˞[!St$BAp⎲(SJ-5?wsma<({N݀[pΓtѯڵ&j>GGG#^R'M?pH˸vɞ"L~o<9輰 ?f۟7q Ж? kk돸T{8){V݄;_t2#Rad{rxR&xPEk&ia&|/_m\*uiV(V$2 .weIbG^杆 SP*3DY@]y}p[魝EQ(z.yx!6Fقۡ[C.>ъ{EraqʞPUh+{埈ܩS`m p.y kTB'-ȑX%>!Jމ6m$NK2W;_%W>)G `?|mu]Q5$ҕRŒQܡSj`1ʶ ?xa*lE,fGE:zɎ*#WIB cx}Xed.8W7K%;NELxAq}/^7L2u7]{;Tè^w?Qd/ J&W@Kxf,Lpe?dH(G)Yj~g utR%p{G9Wګ"ޫl_VMxC lte/6Җi־z}#P&pE T9Pd6gբM.J]uDeCh…ER X0QV$h}O:f7W+yH<Ǔ#dըLav2[!m4ъ < %BBJ>0BodoVQ}2gf&E W9FFyijRhͱ5`vӏS[ƤdO*~IF4F=S2eϭIBd)b6/b>!(῵y"I@0K:i 8wcax(Tڧ, ީ˾)7̀=K/рCzEHݤ/e!m3)JG)r QGHH5 /bejw;HPDĝE'/;nw1!4ъSwpsK)Z\5n-4+("ex/&m3Iy%!} P!qzWv x?w}ǫ!Lcccm+Ň2! 4Wfdqk-EqmMu%|2r!c#f(q7-F(z>sܤO %qEE$yeJ]>9@B`*k;!$߶/eek7MWDsY;^̰(pA=e/R)`ɺ^0UK:G+Dl,BH7CG]Em */9p% ˥2`E |0S63_Vcgh2퟊G*R3E<^|2"bER@ )IB'I-"2.l gE oI)z# W䑠1STr.?1V߉,,&wI/?^" .c?+3|SC~D1mEiw###{7"\x( ^4!/#G٣B0mV\{{QQE /W,$p:V25,f&(Yp'pp: ( j#Sn:ܤ{ԵGD/!+– "ewH_O$,B-i9yv 1]VܦU76O:J^Hu:EXp`[$<= ʲc P'CN$dۉ.9C[=yɓ1QD3ES&!~/|QEJ{=BHqʹQ TJ IDAT[H l;k7e_B9qE`&~럌M"YQhPe떕6k:\/^g3f e4!;EI_JQ8E?&j1Ѩ gʆ*m(2*' ;, †^!j*6bcO(ORcSƂͣ2ܞ#=  'N &8Cz2W{D,H?*}q|352G#ٶe=%osh .* ph* ӱ$4ObL|j Z`Ӑ&LmFyݰHe2#yzK+GY󔨴Fɖ>"{a)ޤ$D$T-~mr OHRr||7%}H_c P0j\%ц8阱8.>OokI) __%I٣B~57$F3}׆?E:6x,G%a8Sfj1ZݍUI}|sI #ި6QM2r(8&EΩJ2yֵ2~ ?.؅57/1%u8[x/lZ DG٣B(q\Aaa?Ƕ< "`C߅#8Uf%iV1FY>;27/ _u X4jRÞ@$ʹ",P Ɯ#p1#p1~|z}%ufm"2}({=BHߺ4[) NS?7%W!pP08bQ=)7δc椒)/z/$.t'?UaD/'\w"]0YwWJljS _{J!uQ(z5ggA`p bm7&abp:)~XۥFMazE Lm@ X㟇$\Grx4Ws&ck z;/B̻Ebrv﷗6>BHHxsF)!0Cɢ5ڶi.Dwgt2BR"ˑt'0(6s?#Q@? |Ԇ9ν :> ]d.2yİ=wwP!)jWc,P<,Ԡԍy)AM`+!I*^_E9j}I48ꝋM+ {Y' ;YPypÅõG$]bZdG# _B‹8(a z{Wqhå" 1Iվ,l;X۶-xjX$'zȟp#_t# 3BS^Q!zE4;a`8f]@Qv Nm 9SlҚ4W%:u(nxm . %8TR mGJKQ6wqJ% e?xO!x|qR ]]d^ߪ!{GMFȟr}X(zc*9ǪxU1ęq1ߚuY;wXd[` 1}]`Oh:ð:4kMʄъWLi9K$J3\E+ O'i@aDx5Xrx|i`5.!1/ovw))됼#1G#dfr1}%)aS8=8?OdNSZs\̺NԪEHP* ,H/\SVROo"^.߼OOOtoL]\xk|>ăpN!$CB\,02$|cx_ʌ7*іgUtm7+Hjra/F( xv雉굿.{7^؀wV/=׎܏+!em ~@@BД, BP(USk* ݮa048\NNU)]l[py9^#q$jLBR  )ʒATe'~R7~ ? `[C%r+zN׳btQ莹#d*5L.a)|'{}Ź?gO =M @3=hlqɋf7շaoz!G`{.2#9prP\T[XBM-:űWc{玮 J#F 'fB2z,Մ7vBdA#yB_&2uiJT՛;?X7OSCjQ[?#_ .۠#i()G,cvV71 >'po< m?m(9 >YÛxϗ s>|q| dY~m+ qESqS?^ ;M>jV{G|OK D|?%{.3 x@Zeʙq8yp- c.O7Xҿ[S. z glsP!]eœmQߐ= 3Ӻ/8m\R\)ciP?"GHl gL kwF$$¬gW1t-R! .*c,Gye#yy#VB OCF7_aòĔzg$۬]a2,W;Uj&ʖ9gA.$"wJ%BI[I=e/p tNi7}q?qzRy @/cW+ppk#17bK T {@y<!= ȘF=B$qx̚8C xG4Jw×piCԜO ^I*WB(E2K\]ʔH$o2b>-hGD>?<3~6pEWqQ_7oN+!~9+e?E[ ?(բy}((mmJSNEE"*Q-h`&a)%7 _>><>ԜkPrT/4! SǷb',4BhkL@-:‡S I]F…OdAz_^Fm둦88 WB|}H5' BG#d 8Svd7ÕJEr.wIׇx0 驦ѽ3L%xE;7>wD? TsNeF(zCqrSB1}vQF b -Ǎ ?9 ѽxS=RY8SO8%b_ _έ}zQUqىzᗽ^0ڟ,=BHFY[|WbO$T,0>@H/<痱A穭g1^}mUEPTq4E= HBbGDn fzV7}{<()=R9!\k7l;OJ WO*=BHY=3Y0.SܬL3򜏁YlĜߗK$eg'i038E*ywZX^r d/Ƌ/[R"I`d2RzHzr??#{Up$I#dZ\v޲A,?BbʹEgJ3}S*QwW`V(}*B9nrYRْ>!2PCL0҈'(#HtIɞۊ[^o.DΘK30c s!Mp_lt1V`|I/dV1/!pmgImxX`U͝hq XB`(o#GLQ"d(JE2Kf.Ds PCbnϭ͸q>0dt|ktoTfiZ[^,6$9_|ˬeEmw UP-yKom F8K=瓥Kl+Fp|[j3J4)KR *]3ǍX&_0LbG_g-s|AWD? LߤB Z\ _^6ڶ$)-~=CvJ_YꪜS;bU<1V=Pڜe3a3h◊EDb^hGOpǖ9gÔq1TzXBH/Zx7~ `P'6#s^ צԟclʼn-Fc+-)!Y_~ڒ<ˊ&zf򻓓>9`uN"|]tͳw䲂ǟ\WMc,?]֒N;-^5!9'$mw 䛌˴]8GǩksWg$/)'4[p0Ȅ"%0݆6\`5MCEL\֢j jVhvl[^v#9q=6\=R+l/v_ѭ|6:vQq 1qU5]."%M~A#TD`$`u.vbm'ƨQ 0A/7d(ԇ[WIk&P"e>@ۃ Ķ<4$6q/rvYU0s<3}=BH, âP[ aa0azSO0U5Oi.$0'I_ǾcK;d3\,Hc9y^(rL ۠cj7n2>)}EiI˟)|1eOȮpד@l=?'he*N^wtT{DXM!)"lv<_'p+ؗ>h2I樤o|3)yjvc!yLA*~, dFۜOuQҗKCϨ^niۍZ1%vJP!tQuRFQHWCŽ *a~sczxxܪURXx385he앰'{=ŐδΠ7mðHF(zANBU&+R]/&pPm_QhxFY,, LGh(O Ӳ"B\H\f}hȉg1.lɎKK 9!EZmI{Rd9|iozCyn^2!^j ]} ѻ!KǿBTjC!pc9g\msĴc9k:G@8v~=HuSr'B|z#|^:&TG 4pZh=Ju!)T|:N t.R&8"{Cc7y(^\x4#rt | 8.Q&V7%_X8%/ѥyҐx60 78qfp=ögX|*9OI^ʟD=|0Hx&7P[s9`W=(}S7 !$beV۲o KPMr2X[fHNunvm¥/t<`O[fcQp_k "cO#23q\A5Ă03^;̣Yh6ʬ"7gxe/iqZ;Ԃ[Uj0ַe:t=BH ?26cp ᆖ1@bYӫQ\92tlMHabmKֶmn#Qp:^3s }3 8N]GIC:hTĩM‚]݈)FLp)K>㱞i%}_Hym9=rw8X=BH2[u(v+{XIK;޲zB 蟋s;P a8]ieb)jF`9;BͪZ˅maNX0YqcҊjFٶc)ƓYx]2'ldb 7Na N˦+>¤8Ͱ[?Ov>ZGJT 嵪@UYC#*c/_0ߩ9 3p ?ӏXft{%Xe'j=/e!Y)!PMOk~*ӗ`(940J|цipp#kmDϰ4moILQ}6.bƁG'!$|C߆0D# #K'1&-<䝍OXdjd%$F DBfhE,yL[H .=V݄/:Ž8]BD/mp& -^fB5QR>q[x5["Ŗ$%ce^_`*8Y? ɱE$U1"nP<<ɰAB̯"RMv=BH!J|-J^ԇŢ^ L @Kx^_.p4hW2gco7˞]V&+$ZHTaY!]ۏ/\H2+n`j L|Ye,>Ls @j!q:v,řѽg "%|͒7(z}p/jPR 1Ze}(D?35h3 шInD s4!+ W)z,BH.%# ? >F1Զ5?ΔMr~靏(51̶8_*8"b_́Ki"d&18 EB 53ؼ,[+"Rǃ d5T.*zw8x:ĕj +d"dRe(zt Hgu}LSVFR9%CAz&0w*h0JQB߅:r- 4=BH^%輮arZ^KH–x{6- 52D#_sNO誒5nEcEG L *;e~Oٓ*J$⫾, =m9ddo ;èdto6.ʵlAC5EBt*H;vҋfÞ){2_!I6bn\EȒѶpU־8^l}^(ӵoebo #z=BHNqD^ `m;+$m;yg=*K Ӳm-9db`n69PK b#ICEѡfp"G!# hX@U\lu3G%ogp&NJIK9Ve ŊY<%N2 A= x+0yWlBH P-G5iR*+uZWaok[?xg1Gzőޗ9.Edzxб\(/k+XVkwHS&+!$k`OzIĬ;ԿmM`(\ SįCžq4IV&hKwg_" TǁYx-0Kqwcix%%c@wQ -m>e%zB*{ ǣsƙ ? /umuk΋!+q, A47Ie+ȺOÞ }Y-6Y;;agF7$!!D`0;$N)vv|wĉKĽb;х e=23{y ܹsiI ^<~ \NDO=̼[w3{_s-/A&BiD{yN~I™͵`y~$rX5w塺4Q Wᓶ& ]! O2KCP"vSu7-A|᰷U)8ChYtEaDqw͍|ñgx=\BpIv!rmmjc5uU6u7 }E"z ' /P'$Rp9*^FvzW?͏+β6ftrNpO֍ae?u,dio_/7r=7]90¨t;?祝 w}}G#Z#_SDOn=Uw7;JKQxvhszõVaSN9 669/g'm =|FMB/F&#x˕#NQG(](Cy?Ͼ&rH#akSEj'塂 .{$ai>!tT PxI%5Ft3[+.DA1;/P̵u4K]UC~+0'F񺹀7quxP^R^[n|nj*TQ7k:|޸N^d+H:(yzZ_@?Vzt)TA$_={vj4,`^[ WuSxçTyGsʖ Bmy\a=u>v$Ok߿7y6QC膇Y+>VM=AzG)8E)LTxF=ܙ}~^G9ui wqu-Uio>Yq=\G6(>r+0tn2ò杄Jǽ#YtviK'*d ^\}J{Z{dwXUV`vby"z 8.~3[aI=.DaO#SH*NX -7y5. '74VoqPg-a8b Ҥʍ\6Fk;}}g IK*9}DUO_ty-\S baPxO99?9W)ڞ=,xt$n .o:Xv{cy݈HQk!+\8L3rɶ 5o#n*z cw/zeV[x͕kcP&ko"TNpՀ쪎^wv b:Rd:PNq $/Po^}Nanrn5_Yצ9.sl'ޙZKyy]|n'Y[ȝ  $S/Z/I }ggJa궯y5z^Y鋙 Myڐ x~  KP3u.¤:.u&:hߋ{N^3k.}ZC(+h=cY$'\Yl:3"z  Bw[Cс%/"|M-|C |Ώ Ώ5;V;Jty1"Ji-#&~B"C:EAA%Ϗ ~+_h~*gĥBu<ݼ'no{i]ڼ -{T;rIyIzsLR  lw~ťfaW/s&lk:- ] u~&GϜ>=TǪgzZKĭߜίa\S[>I Л)'  > wkC+'895@Z`$?m-ܵ +tg^huI׏0ݸK*%GRjn8V~~AA@3QQmfScgSܷs^Y|';Z"U+jzB2tǯ?}6rSZK^7m&~,T@?G2J󻥩!","z  B]@dR ˻ ޢ/Ya7.pԥ|)x>=Ay;8m;zSBSt $ I7L[^ϟmܣ)y"z  B/w@OmEӖWAV$?/Ǧ{ygMﶷq=y}<k&v}X5bi\Ҿf:ڛש)"'  e*DPK@#2<>`C6UQ#:$!IՃ I u{X zV`Vڼ`NDOAAKvxz{ccX<38vr/wM%zB׎ pFrJH_bcT,%'\& >ODOAAbQ/$m{괟y3Mp2RG:fU!v]EE[-g= &Xʟy`W $wM'홃rhv[[xp!~`<=AA RBJ|@&Qp U/>Cl;Xg;jXUf=t| G+̈́lE>Һ9¹y"z԰x42;  W|ݵZLd!#u3'w"qX)_?;;qUZ[oئ!\3<1yJ'׮FeWmzTo^'#'  ]|Mu}F|L1Yȇ/SMYE%%UVqdU젋}fxkgn~(H`@Z+yg7(PDOAA.75,.vF+y)|"z  BuBŎ/^cog&~{ڝ'  }MNKh}埿=/5mu`^  =!k>^WxǾjLPM`@AAhw T4:Z3qH sڥ0NJ:݇RZlɼK_H  e2זkhF 3'1DpϤ.g랥 ><c;?C5EDAA D􂑽N&4?ߞVXlD-{?@AA@[y`s-^ӭ^9ߜǥ)ʜ]6ƒߦȖRJ@d=AA^ WZV6EgE IDATy\:$aasm{7Ң<;SB)''  - ԫHt[>zq߽)2yL6rRɲYD BTvVo3XQf/x~ivUM<=AA<d/N3L\>1e02;/}mA>xmԁzZ ^k Ћ_T'''   ЫI(64cTvRH/`u0)dz'7(񘚛I FK^нx"y"z  B_e5>{ZKM"_<٩qd9Nhdϡ,zgvښ Ak3/DAAD=?PZK_eDqDg@J ѤF9냫yj@yU1Nsx) Nά:DDAA=_ZXLaf.ΉcLX'FAblDOSKuM-*)*hqOT|_̭$-/w/qҋ''  \i׾>LHIp0.9Gn#"J݆na`M,f,&dB)0h60 Caxp<܍;].ꩬqRY+YTb9jWZ]O$H  ݋j)MC~_άL >+wk<-|ϗdh5dY5΍[K A ^ DDAA F}Y_ᣙFnN/*{TEObpRBs<kDAAHDE?;#}*6t_ZF/ B:K|,''  \q>ڑ>ww@v(vJ+] H  p>O5;K>J Fگc$wVMs Hޅٹ4# 1)_AN&yX"3( .E,-K{@{XM}=%~mN6*ZQ`NODOO2ì.ІVyj26,cˌ&/KJYq*"\P%1grfbŎG&mHLrw=: HZȟM՛&#X P'w"x"z)Օ#N6n7\ gD tfǃL"GywY^D -:3Y'zZ_ (~ LNu{܆SCqy1;[Â;ƃ(/)̙yqܝ |j:2%sDEȒz4;79˪}(Yb)ƎHyI 'B99UuʂecӸ*'0h8KɝAW:,\=69iǪ(,cROohOBNK'3¤8I8 0܏__ys.u3w ND#Y1uTr Rǚخ)i%uTu9COgAF,IQaJ+Oq!Ӫˮz04&ǒaN'ˣ܊[˩rʼ F׍OgIq<9JLǯJ/Sgi7#owǷ"w"zBp7X;  fݶ<ǙJ [4v,>*uX)tO≆1aD,UȾ-q`jL$FnÞ74WZ4[ADQz! |1qpi\Fm#Ƕ3AũaR 7}kYdV7򣉹x]x̬Xy3_<"&`Ԁ8n]0$򓵼a͞ԟ{Fd8t2OUu1 WeAVJ._C`c\>XEYOC$.;l.;g|O^tiџg3."խ&shɳx ļ+ 88b Ɔ:3]?fStunXOm¯.% >oŭWbǁ9Rx|U᫹C7xq)i=Иt?24\<(\v<'67 Nǂ],-mՠ1)^?ߏ!VR\@Mf%&.d1u& 7`[m(zy2y ̼fKRxjJ*8=6RVVȝp_,OMn##4.)ʫ4NlDDo1T nⱛxxh\4X"$E1Q#A{ov⮛I*لj&-6;U5'?ۨ3 5ζ:'/B ꝇb){Jt\89Y $12uqRfA4Xӥ|#l7[0ZH;֕9a_j+k`a f[q}1cLg]3nˎ Q&Y*[Rb{޼T-i_yF0?* ц6q7ytX> x|)-9,\55{1!<7̽/q3h~J?$Ϭ(ʺmŬSc^UػMpu_$bʌl~;kCf's|od5x~^~NEZ+CY)+c6O#X}GWZFsnM[xrÓ{"MTxxO[&<,:0eCǙI7i~?ƆJ C-'."CΩ>pP:=9{c0ipx|YzP6ndo)^/jpPmꮏ?EΌz\_[+OagzTۯOf˜:rW_sM#3\  FnEDub;AiXG?oTpy+qR'XR}Wyxgv-Q^m~oJ[6Ub7ne'^GbG 5n} s 4 d[?ᇋ =YάgVIM3_㑫=sw䓮N˧2l#|W xk-|.WOi:ƥ&;U)?x }Ftgť-Refhf|3#r؜ad裼E_h14SW  jUcOJx$~`_gZH@jzr-OAQr˄OXXߗ'X~$ݫqӻ٧5v8S,'*@YňHO2vG'$LxذD AiXe21O;-g/k׼-Mb -x<_>߶>ZG"z}.Ƙc{[hEW&[| ~u?5g#1\~h>j[\Q^nr )qwN( iqR8WOĄ[Zrjq VvA;Փk{i53{(/Y4lWُԻp;q{;g2|c WM=1Éz67߿n60/Xh;= ĴA xq+nc&sSNu`tK>ⴴؘ>\t~patထNWiB'/f`/GpykGy2gs6'174/ yĐ:O/?NպʽiF ȠBpj 8\ĠLIp^x MY3iթ-?#/r+TSrl l;EDNɌcvgO "ޤhpQO!E"MImr&2\?f;ƾTx؝cVoVףT ijYL[8]yiM g1fAJ[%w5n~Ηl(5uPab0X<:_a6Xa5PcRDGTH5+k1$ILoh_+Rb-3vO>4A1 wݹ:3\=] 9 `T/ pW7Aa6{YypISipv^$ ŲI՛X&;4$NbŴ-ޮLaiF> ʬqxv O|0gf-f<ȶrvd!z T,s&Fj3:Mb:BTyFY=LVׁ<̀6R_WvH6^pO,[\? 9g#-GpHqX?lt|1Om"5{kрٜH^lbhB8 UfbXGIMˬl&ǹzGe؉eZaaNN2fn!]/V؞ơ=?~Aae"ͽq][ 8R6]WTxx]WEI ZTiqm+;A6jp" PF[",?:):7 B4/+V$LƂ򎋜Rf? |#>"p\( S)OGSЫS UB# jPj>=!U]QȧN&5'W.Yօ|S.50)aV'Bk)vjdK{ 9ƪhq6mb.gJj?' (t 5xywI<@},s@>|1fsx{@wbzK:*ʉa\0{X8RY`r)kp`0ڹQ]5Y!_j ^nkՅM#Ē1{|rj1OonKd-$0؟%LTC;U(;liğL}b̅,/ZIv{2ܛkXJ)Q t(, xvcg< d;S W dX`f$V WW͢e_bU!2mr/m7HOpXErŔ[bʹŅM1"zB/g'y.Ƭqi 2c4*XWqga,~Σƞͦ?4i7U*xh2< ZOdBk/i[{ܐ^YiܗQ&um;ɖ '{ QVʌ Έc@l$[ 0x㏢^ܦ'՛Je꒞z{y}-PVu6eє,ȌY80i$o]Vwb"IDAT9Zc[M˕OMGd|ZvzhPQUB;eH򸈞7jSxϝ&a$-d]K:Ll ST`' MWG:@S#FixuAL =RN=~Z 6ͪ68Q XU<) GJ8JյݙRyvl$7eeGI;nXy7V65#R6$iY;,2å{wzUZI.N\ ]8\v":oA ]OF6Hzn,bS4mǸA GxX3QˋhGx>+q/,d1Z~|". y^'N#X@ATd.! 8;6ZPmg^ Jk (X喚HiɉVC<e[hٻ{?0@/|қꝲ^XR7[Ô:4v?Fp:*0ƑJv2(6_He/7?;lx7-Ծ5Xb1OI->! JʗV:Wh07o<<.'Tz~aoiZ#P nbrvrֶ3EԾ16EN$o WP\v V4f altqgV|ux?ǻE;ZIr$IIj07#fH+ 4.^̧b/Rs_GkJcޖVRGX +kEN(f>G<3?hNTfɱN?*12npVL~m^*fZwQ'uUu_8$ ^O8;ifr6{M(<wt$K)_3oBGX]zc~ yM-=[LL}~ ^q7 黓frGR }GD˸ŵ14`"#%"H̟?l2zc_ Sfp;e#-y|ҫ}l7{|QEmh,|S!$75=BfR(d2aQ?Xsd{qHkgp7W8l8]yiMZҖёs z:g: E^e"hˍv; Dd[:/:wS-<̌7q1a"'Ȅ8w ,K|ieFkIkQ^>p:*04fF]#skcX0#qY 7掵_<[KxT 4fi ֬adl?e?h?/W֡f1y?^·Ӹ%Bよ^!R0|U\lh(4+x.0#/k~*qksy۹MnVU䚏}ʥ:Fٰ k=a3sj=7$/eMy;^;24s>]3\+MfԲtR#<,ͣUgnoK _{+R L]ox6\3Gf|=!o(h`xaTs)cn*crl)+3vw zB1+鼑wNhM (Lt枑\`]t]^0d~Lk_چ? Nn탑ݗ]sCX~_u2)t2֫/`Ye3xۓ˫޶Crqc+YbAS辘猶 7X$:n ,vO2\e\n]FLF\39 Qa P5[a,w $8K9c-i\Ɯ[b LᲉ0@|!::}<3-d`ŌћXú 3H+ rlW۴A]/qߛ~Ͻ~V b}xiQ5QUE0oDϴ-<#sF? g kݏbo U:e`7nڇIN'~ֿpfrq'x!$Ih]WQX7J VV:r_j`G<3j} m]FuYZw9[kx ^ڙG5,ͺU܂3½lB{;%`ycR~[!ŋ :}^p+Ǚ14aTeo~e<;_-+EafRʫ jLxMfbNR]_/u͐MWQ=TgeJjnnTchLCSJ6l^I0kV6쨋e0=+/ob]NZyYW2*6 vRb"p(Muf}w|-Ch) sQN_ļ!DGXZV~YC7!^\N2oó=1KWs.G\$M'Բu =cDsZm\k=\^ LۉWH;qܝnck;=s}smU'w`g<:GE\T?_oږQF&[y5sKyĠcՖ7x޾0W({ t'پ.*n?j]zfqMMimq4lG7cI!js-+mX6-۵[VX7z?7/bXٺC܏oXkCN`w߸,cXDIQg VN^y?n=@>tKCU=8uk1,}:ff4ƆCpt*4ur޹_`R|Yj,NUX|scEZC%%h'9cvZ4)ful:TλSL yR#SpSy0ìY.c0☑Iՠ 9N'-c6#fweA<Lj0"l8S=XˏΈΤa㟹w1^VRGY1y`t@yu)V┟XML\c忧Zόu'+zO~U !I'4b~d:ZN>7jͽ3mϳ<. rۧ0Xl͖H v.///[%BAselU_#"XT缵;V"C(A 9zsCn f4JVn&% :JAglf-@ +; #u #U#=7,ㅵ' H% WcU&~x8Y%EָGP-#Q  9$ǒG_fJ u  '}Csp1b%l=^MG`K`hy .[B3ٽz(EO+?(upXB@=Bz=B z5;,IENDB`isort-9.0.1/art/logo_large.png0000644000000000000000000006154113615410400013216 0ustar00PNG  IHDRz0[bKGD pHYs  tIME0(8tEXtCommentCreated with GIMPW IDATxws]zY b؍hI1'jĨ{G RTP{]`[g&1wٽm_?Ի=sS{_aQ z A@=Az z@=A@ z=A@ r'@ ܋Dt.:o'*QMyDeBNO6aoڮkiʂf7:djS ,{ժ4vwjzfjRj k#PU%9~:goS7),z@Uӗjm÷=]@P  1 %C;8I1Yz.Dz[] avk#(:A@{)^|f fcHQ$ iVL3 v@r.b:Э:n. DOy8X*k. -!&Ml;G۪i(AI8;4̙7 0_WW+zz@F+2yaF5U<+S1?BA_v#@:GQHJ zWWG`Gڤˣk(C8,1F%N~ :lC@ J ̗ǼWJ /hz,OFkbk kj&u4)j5TB|GBrUWJs˵/6T+rp˴@s41KG4ޤ> U=2-~lr[alKu{522~OmHjku[ > $taprhPj§t6#/UZu?Mo Rx:٠ӂu@Qs=|npf3S%uOZ8;okS;4h+{#_A5j{z[{Uk5ըQ\!wjz-]$uRp&8i,AJsGz=E M7 ?j7ܤ4nӤ^Zd)brcb ux3yUusdi+W앝sr%8ً!Ǹ /DMRnF ٙWAݨGwo?;ӹ@@T*=itHz^צ=[YtmhRqq~v/+;4@{n0g4v\JuXjIj`{LxP(?#W5~'3 _R^Jn*-.(*Mߥڎiz:-B9ǘ392O~?INZ{LYvz]_wݧk-0Χ,C{V:o)Cz:0>]7^)й,E'eWg{>OY5٪{KqfMQ;yU`G {5>Uz.ggum|KJ]N=O^`ZvzRʯFXFF^ Vb=EHGV_U38l-74EޥE[j׍%w {$MR_9O7tygMAF?Rc%,e4Ze-Oe@| P82Z7)ʵZl%'mgk5iwЃ2ķVZ&E+ ;ꝩa`ӿ[@-&TGaookfj0;tF̄s\ 2.z gn = ?:<2dNAȃ=E&C%wiVCw[^NWsWat0{A@NϿYtNdSw&F' *?WwHFRz'gE3]VL*q! (tC 1#OkI㵳ۺ96Ȳ9ɉz 'XqµiSb=u0rE9 Đwo"stg.ѨӺ.bdo[ڨ|]M^^Jn)zɘbcvSGTׂn}י*5#OY0=qNl_婱y\Rzo9\pc;35\S,DTWB~-Ah.hgubH_?ȸ$kwѺ˭T=Zx~cC^A ה$#}*N6i% kWk+D%f+kwl9>SEy:X<#svB)I%>}yI6M3KR "3.pIi\h kGQz+A@޲:f-)ᚒdl}%z@*Gi?"4+tA>sKHs}3YNkw(ZLկô2@׆8ԟ;4/%N.'A[>Y U:w+BDk=GBM) kO]k^H>tę/0& ;;z6q~΄+A@3:.J .p[[ I}5Jt(-G[R43]R]䟬 iY:$ZRI)d\lIR<`tLBۄ[bI˂A@aFXKpxbVФ:='` ՚5&nHNɞ YtUpNW'2y461 XXNMG |aL#Tt}eMbt(wtݵ֭bD[|GkvqX]|4FQZ'c wad!ڤ.p^W(Y2z5qUSpK5;[,\t1 ieҝs_A@RQ.'=To>CfJ;4^Q_|ݘv{z86B/$X֮eg:Nݖ8DB =Y[1 YؐՏc8|G9+IaT75_[ӝ<^PW5UwsF a5Xf/;.Fw~2 =Tff;]j~X=E)~[VHssz/)G2ff?LV_3V#f lQ_{K~~oyޒI&>͟|cRvg45;݇ڷjT,AI;QrLe@7hլ7g`n ZWڻuWFBu|}/Ջzr)}K%E h#O?R {d4w4׭_ j5.[cey[~bquEQ54{lH;b,ݕz:X*GkJ8{Y>wzM +nv?R]TW0c55՛hh_,e4&ҷ][h-D_$Nbz~JJxXÕ*zwu]0G ȗ/z<xUf&-h{IN^72YO#+1gMRڻqglt= obv*럚01 :UKޥ͞Y uux>aV^5 =yɯەBN2q P.Zx5Ch+;5șڤ`?hӽÊ9C (*Kjs1UxG.]%^Uz-i9Ion^ 2.>q5FWmkzid"f";*mU@s٧Q'4*$@Re@VRaR~v{kWnz9q~ӊ,>VaS,5Z{QiEѿ'S$jSz4;Y6e.`ycm6 MPH# ww%Z#tJV'NЍ~ښ5zY>5W"|x;nHJV rj3S/L tVĝͫtEE*koJzTZ.wh ;NYߊ<-Z-w hOn/u`d7l#է߉ZJ? Og}xQIyUQ|F˱!@v6v)L1ڥ{/ =+ \jY^VĘ; y` uyxJ3.k_`j*fuTׯPC@mSM IDAT=Ek9դ,oQ7kWQE{,?PzrD}%9meم6ЫdFK_e}n̶ ɫ^C+L&'V>d=hC=uYZWR6Vj/UfW90˭9ZEE %f󭜬Xe<mJuօ/K@^hC&V0+zKNMt>۟n9@3r˓ä(>@׍ꪦ) or5d+wl,Ob$m ,e(=@mS pw O;2h ZzWl dgY_,J;z>Q?{zо:⦋tc,+ |فX᛾u^oC9ۋNf<۷W_zY?O QxYyxN%ݚ~zjPwZtrpAFU:6yeK ïW3gYW@֥~t^j YuG ӝZ)-9ܘ^m΄KkE܆o0zϓ^o5^Adf+:Zi՝nM {<}ɾ/W Yuo/Qe֧.(ɨ%S^4]Ɛe>(=ŷ˰eER.FiVgs|-Owc/pJYxytBwArc/!Hl7hݨVjݪnEf9jcPA^v˗/Nz/i1ORM@_OWYY53݅D[V).ҬCFtlbfA @{Ֆ;ijSmZ]I5ަaڭQ NMwN ‼1=Jߏ>s YfeN>;4/y^V+Y^#i2͊_o^R?ӝP<]H[r ZiP€KܺO"I$wB*gH}L N2>b&{4.&Yufzgb\S<|NbZ婱z-yecvGzVf( rA׵ײK׳aJ}Blw+nyoӾeqrvbwV+ݵ*GK2:#EC=@yY?VgZ꧷Tٔ|nN~O2z}=ġULܠ^&G@(tYM,w菽G5?]W4kߵF)ҽ^WQBjD/HIӨJWzUb1OKO$iǜg;O~u8yF6[:ޟ4 6&|}'~Kw`]a/w"eSTu ?z4U}'wmh.k̓ ~`[ rhFȽ:$p~;Rm9~_t}O%E| lJЦ>wY仜G||X}nEjuFc4'8ONwr8{"3C tSrL n@kO<srp2˫VčzBL nyGvٷINIݛSCUG,evjt t;X+uZv\RS3A2SZ1 VctμwYԭ>Xǃu:zwQZW=+u +on_)>z܋&O;ubpF9-=q'df+tٓwS^ tz5y~E +z& QbP%BtZd*iK[x~լ;e1Z~*w֦k+TVolTzXi 4ڧn֨L< wjZi3mp ߖꦿ43ի76%חdu`?eiD)dv=.uo cM;4W-}-_#domd oqtȩWkP`Bb0ݿm]=^O¡ޒGI+`)giZ)vӑuKMo%hGFMM%e|tw>Iҋ Zߤh{,ՓBt@? Vo<Сq]n7x x=y&^"®az|/製-0< u$aDnjSMm*Чi؋tM}EhK=wl Y(FBz%9BA'*nmٞSWB1:=J&u 5&LN ="u@ܨ #r's:wCj嫟Mk+[RG{: k=+!{CW4]خ&AZzF'jzmѷ{woOcFzStKxF4 ;kAGЫr i65vf rÿK|::n{iXX1\ B't2#st5O/R'+;o%w'e('@P|!tze:yz+qK80Ct{XQDiSm-z5ۆ-?OV?a& |LMҺyD=*7̙Df;U=p1\ļwd`' v6e|- {Fiz}& /D֯KTϚ{A@g%t!N2 yGߕEaq^XACV&jz ^HScF1Wͧio/7n5q[ c㵎VHytqՀށ7u}=ypȣBtg y#L[q$:uEg̯qz&9z -ݖݱSƙA@x&v읷۸>U _z;_L'ꕤ@hCwuW(]@[ κV͟n% bcgd uM_ok 隦řEN;X5]R_Ӆ ]lcqZϰfwkV}.V|@/՟+K7ӽuCq78-Ho&F2 e<]˝TOppݓ~PI (w[FGTBQޓtc-+9:+}st[wOg*Q|ۛG@?}/Y+z_oFdfxa53 (ްgЅ繳'_+'ZMb^H7?Y/u]Y՛mEߝ (-+Ca[ԕMiRpz^̮^Zǒ@Pa's*6ẗ_ڢ 3=jR]yG8/EvFuWA<9|=觧W2]|Y=McU̯ԡz69HONz:Vػ)E.NQl0=kZD Ϸ] gʕ.'5tʭ@FwƇo$B@V/ӪH=*&Yz:ӵ2^ȯҫDfNh7џCuUhNvfД2ݕ%PR;3QfM .{xO!MP=r1=ߥ(59bх=#K?zsx-7_YR⼮>fg (u1z,9\Y ʗIv3 [Z,{9SfPJЃZI %%2C1"O0΀_ndo=Gi4y!;T7F,Ѩ|QHJfji~Cш{?jJ$Mrvp{۫UgP36xV0ݝ짷X* DžR]d ~>oIR(vvZJ-s+W<|dO=쩈5^6 JMYgdk[Ez6[wLd7=nFĤuݤ!f{jPgQQBYq\+Rz$9H/zJ򜩽n OשNco&Wi|7!Ka5.z6]~z/5ޣzkj>uڧ)&)[)I#Wa~b~^r˵-"\Js'P O^/, ֩kT CJꖗcZvk\ZFn Fk424g4c)錺N+ +v4nK-m$OSTOiH׮=;4Z1/iC-2152T[$Kj{c{<׬eGwIgKTtd{jhڣ՛hʂf֐)iڤ8Gu:Y~\;l[+9jcAIVEPꦣWWIH!5k{Fݵjӊ;t{zVkTû6M5j5ovʰ hܷZª ZJ$hˮ횱d"|࿫CjxZe*\/ԔsRѿUKtauXu:pkמZv8ߑj>灠a!yaopZ tH};Tr6uO>t$}uC쾌Nާگ IR9:=ʔV@N}[IuGϭ.8Mui/\-M-KZTS22P0:vz8@_nr^Z1F?:yNRtўgH7̨m0tə$Tzjsokߣ%w^]U붅?bKi؆w~tfN*7F~c_FA%_TޒGt 'Մ 1G9wN~{>a`^ߝpp}vcٙ|44$}ehʌiOmdږM|>e[qYFo{]ֲcuSiBp?0]2q~KznZʹ=-ۊsTQtz]38ftҘ19+빭[ۭ}<ڵ}SCtjOhԙ 耰pN[SG}ӽDߟ4L jCSǮ/M׵/~$^}ScI%_[_ڤ;~SShP'o l,]xH}*fw?N _1IKsϕhk<ڪmuA`ppGݔPȯԢZn?IjRZ 3C ѱtm#/^=% TA)i*֪imѿ1&6N >uN6uwJY/M^vV_MJwv?=TtۜstW@]i;?С>u0rtjs=WS-?쨢TN3ꨡ蚿Gp~z?O!7 [czcOBtPvyD]C>Y\vλ5H G}Tn|%SհWBAT:`ɘ2@_G*|}{lJvh뮔⩐%!TUKNU/׊'.KǶe{ٶtlu,QH6mSS?VS?{PuXbک՛]zwR}* x~SZG}]w6߮lӜi~|鑺K{W*ݨ q5yV(]{^? 3EW>ޥ;n8BJ$waPPQUږQ/ϾC;BZ;aGFwjoSHn(NQEQ(8@V<7ߨѕݶy{aF-_ӶF_17(*u5ӵR|az>1SڿiN뾥u:u9JgxKw,lGӕQ.4W] X:Ɉު2FOʍJ\;Ot<~FC{Lϵm#@+(NǑ] mO7O#J$]ۻ9wf= {oAD@։yOkkczYjVT*.FB qs=GR4M??!9;|vsu9/Llj:΋NT0՚Ͻ rnU{grЋiar^ˌ IDAT]D25x2\#pG'ôp󪙍=O12q]25Nѽq2 _1$qݔ~DϦ='0~ep/oU7kx}&ʈ2}&|VUw{4&`}l=\gRϦyLξ !<ơ9]QIx=ŜaD((*]kBwڌ 앭z:Z7+??쿬2L4zE3j:RH~3s bUL1_ L2:>,cSЪW [/xɖ5ܹg͆/QB(߈$J rZGd̬r/##:,ru՘64I?Uח7Xqݘ<׃.3/1jO@y-b_*GfW'0e47 ?ӭU|}9C1h)^kTVK*L ZUp̩e$y 2sۊ=5M<8i;S Lgۻ >ܘ [0dN~j}s_0wr.twҹ!!$ѻjv uyъ {)}V [Yi3$Lw"mۘda͝iapg%&]}N{|۫'Nh2ߌWSQP['gѱA:K[{q%&:Cn'# /Y3`7OOKZ#zv#: 9[U @) xl.]/,] $͖'8ơs˫A-.V*ElrWmSIEcDdtl-.^ު01-1RiX,4^ߛ%+7\S4f3uNٹjpC,㣰O17Lo:q,v(F27>c/NUx:=AIGyW%8`ګBM]'(2q wy91fΒ-ߘbȯF&ѼEVWkwp8mj$|'wZ+]1BHw9n&vh>JPûL7|1Иgߊ?Q/]]VoWYRA\3̊S1qL87xkT8%6 Y/YeU_cT&^#`喰. ]g͎-l/&/~X*TZ՘ f3Oߥa^/+,MXrvN? P׆ѽڳ\]=%Fδk@%mws<{麖 gZ!-U]V?&[skbEOZ{{xQ&GJ1]IJpsI/ }͉,t'3%CҀƛƆw`؎cZb0-1Y6Ĵs*xH5ԇ^eS^\W~ӟxw]MVuuUQl(GTHy탧p o({y;M޼Vf'cre2,ጩ }K;~ ]1BHw2XH= C+eKґܼ.1h vUxFxG=;^UT{ _BfنuV=gZjinK'YfzRX2%Fy^AYm "AVq2ʼ\nԹZ4n6h^隆6ej| h<4w ߷%-n]̍SG"Y)J+ʞo)UJ,9um:vn![aDJyǏ+uxSfRUjPX;hjܳTK HYur?ܽ0 gb#mj1y -ab.(Wwc'^)\wحkeVPnifiZ]5.!V+W~Q"6Ё0wUR^UMٳ;ņJswBws*)iQ$x.ۙN=s]hD@X<2ƭ8]`LWz]<^3Tcu7=T팳lg3\@xC?ӜhBQd:VBq@U}Yޚlem"w\P;7&Q潕ydgCL6nC]RKِ;dkboeՂa|uà:(1(4MFye:x1#1Akqa$M'~/oS--؍՝JbTB}m<.J*mݹ+t)a|axꆁL Gk cXڭԻ֖85ΟfƸK ǛH'}5"&XN琨UAu3s)lզ]UVZ1o-CK~"Dt0,PRs{ѐ)Yگ'skHiCgxePۖo)nsYջG"\t%eC%JW0?LZcFuɎOfns@abմ1ϫ!7`Ri_f;W\(с׷IŁl.M8# *xADpmKRAe×ƶ,Һtzܩt,=e 0oP$E1!'=)̽:WZL֬tfU[IjQqzJ#3oDrN-_LZ7:j{G(.U$bzt+򿖨07<<>qῙ_Q۸-G%\wGjbz>1`HWg8b}H9az)ԣgv3,+}0',kck.nUʏZabwQn.]$ V֣⯂ztj78cz%OVqh^V4;<4b#;T&3UO9bVրk<k{nwrhxcWݿ\ݰ$F5/ٟO`?m[=3mhD44Mâ +wi8{4Q1ppod֨f'Ȥ,7%tK'(3Vyj1 *s\ˁz>=0n-n2XN_sc󭫱`㺚7c+Gnyj &F՟adVplYLn Y>$*XS9"RpN>+K*)d\=1G;'Y×W'n4t7kw(q['sCr9^o:\m;n>aJ>dأc֠(ދ ~8{y_yć0#AJ#Tz Kaރ,Ϩ᝜pFЌ<Yy#3XK@%em^nVw6 y}bZ$3gh.69G_I/ԨuF eְ8fEkQdƳkqhZ2߹*S-k/"I '9niy-5.7,,<#a?sgr]<:5RcvtU9+(ys@&{s{'ϯQl-mz(y\J0^w# R?zPeNmێV_~XLDOx>w{FU>C}V1 C' ӏ:e`J֎hL \cAZ}}¬۹պ0.צPZɂ~Tu Kܓo]Hn Iex'x389޾crh#|?_ןAv?R'm8[5-8 ܓW`yD]ɍ,ҏe%1f:{7˩IXQ- 3O1,ĐpQ6W2hZΓW2ؑL7UŔVTix4 _b}U pr8{)?X1SzL:\ݪ$Fh#VY_gJd(nn0M s7YΎYv(9kup@>fsƯۏ#3y$?BH W+6 ~v"p(=z|zZW/G+śLW7!afڶ WO-Y|ykjC}:"U%!>>@n,) 5l[leB~t]*sD5.W.ubK#cQ]o&aL8`]aÞ}ZN8 loX=eĴIuV)e_:lM: A~5I['o7 QMY =ƲmBLK I`zG0`u*:xrCЅ z| ZnV L?Tď2+54_; A+40+Dz sxjSݔהD)+/^NehsJ6Gano~#4Ʀ0)Ÿ0AeY2KvI[ǘMct?NWċg6)! !.IDATtRTqmٕ/ny,dvۖ&\be[Ij]1w{b6dUEbaknfߧ4k8SVþSe|VhϏ V.u!I;7!19j Jzl+ Iķ e= vxY'HH':>>Csxn&C*DbB"֚::Tհ ߪAR!Bt,!1J!D5z[ sKr:&Ey'SV N;#2 HbBIh'$0nHoB).a|HB䑪uX-ZD!1J!EoIO}#cHOcGyԏBbBI(&ϧ<?OBHB!%$X!;ؗ߯-^eN>XGIvaT%ʺבsB!"!B!$zB!B!$B!B!B!BIB!BH'B! !B!DO!B!$zB!B!$B!B!B!BH'B!B=!B! !B!DO!B!$zB!B!B!BIB!Btk>gIENDB`isort-9.0.1/art/logo_large.xcf0000644000000000000000000050763113615410400013217 0ustar00gimp xcf v011z0BBUiUnnamedBCABcCi%HC/CuCJCNCJCNG gimp-commentCreated with GIMPgimp-image-grid(style solid) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches)  1hfg h"i6nFgwz0 Layer Group!? "     %$#xz0 z0CScs#3CScs#3CScs   # 3 C S c s   # 3 C S c s                                                         Lo&7z0Layer!? "     %$# z0 z0]m} -=M]m} -=M]m} -=M]m} -=M]m}                                                        Lo&7z0 Layer #2!? "     %$#z0*z0jz *:JZjz *:JZjz *:JZjz *:JZjz                                                        Lo&7  sort!? "     %$#*gimp-text-layer(markup " sort") (font "Sans-serif") (font-size 62.000000) (font-size-unit pixels) (antialias yes) (language "en-us") (base-direction ltr) (color (color-rgb 0.000000 0.000000 0.000000)) (justify left) (box-mode dynamic) (box-width 746.000000) (box-height 289.000000) (box-unit pixels) (hinting yes) 6 jeq}  !!Y!i$'J+u/34D9>4>DD~J_S\elqequsv x{~]9%5EU !!!!!!!! !!!!!!!! 76%!76%!76%!76%!76%!76%!76%!76%!76%r =;Rj  P......... P.........   . )I&$(#!lJdn q!k"X#4$ $}%@&'a'(c( )G)**U*++ -)&$"! ""#$%%''()))*++++,, -L><:87 6)mB=96&G?76$G:6"I86!Y;6F6o=6\86T7 6R"6V#6b7#6y8$6=%6J&6j'6<'6V(69(6S)6:)6^*6A*67*6Y+6E+68+6 -/[)m& ]$" }!P7^y !z"`#;$%&W&''())o)*0*~+++ //////////////////////////// 0000000000000000000000000000 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65 .65  D*B)D(H'B&D%H$B#D"L!E!M UNY Y! YYNYY N!Y"Y#p$ $0%0*+'U$$"] ic<!S"$'%N&b(r)y*+x,r-`. H/ $0 0 u1 D2 3 w344-I'%Q#/ *'$" !#$%&()+,,-./ 1 1 2 2 3 4556+''/M@;:98765*F=;86'fA=86$H=76"cB96 XA6cA6wD 6N9!6l?#6O8$6B&6l<'6]8(6R7)6M+6K,6L-6P.6[/6 l7/6 :06 @16 L26 l726 <36L46v746@/676a(640/.-! UN?<;%62-$Z$6/% /!Lq* I'q$g", =6y!4"z$%;'`(})*+,-p. N/ *0 1 1 S2 34E4.i'ȉ^9 =b%U w#{941 .,*(&$#!  !"#$%&&'(()**(K%>#94 1 .,*(&$#!  !#$$%&'(())*+++(&32.&96520,5 61.%1 63,"/60',63**63)(63& &6/%65)#60"65"!6* 6-6/ 61!62"61#6/$6, %6(&6&64'6. (6$(65)6- *6 *62>;7(6$xH?7%64rF9$6$_6 9ݦo14Y1 S.,1*/(&m$+#m! ,E^ h!m"a#O$3%&&'?(()))%_"  fxuMhh  Q ! c" $ 0% Y& g()::::::::::::::        " # & ());;;;;;;;;;;;;;; 862 YB<;9787 686+UB=:686"UB;6865oC;686/[@7686'\@68 6 nB 68 62 G7!68 6, `;#68 6"K%68 65|?&68 60g;'68 6(Z7(68:68:68:68:68:68:68:68:68:68:68:68:68:68:68:6 d :^*$a  0G;@  , m ! 0H#  $ %& PH' f()::::::::::::::!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!67656/)86Ks574  2n2C R0 . ,f K+,*  (~l(8.& $l#Q#" "a!!! !!!!!!!!!!!!!!!!!!!!!!!!!!4 3 2 1 0 0 0 0 0 0 M1 1 1 1E M1 1 a1 2 1 E1 1E 1 2 1 1 1S E2....................................()))))))))))))))))))))))))))....................................****************************)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%....................\[QA7.(!  ! " #}$|%\&+%n#6" !L &  T(+Gn !!!!!! i< +*K**)<))W( (S'&7&q%$($J#_"i!f \D!l"$5&0)g,,,,,,,,,,       ,,,+++**)))'(&&%$$"" !"$&), --------z,6]60*#Q6& I6!@6* <65 :61 86+ 86, :61 <65 A6* J6 R65$ `6/(7+69+6G+6\+67*6E*6c*6<)6Y)6;(6\(6?'6u7&6N&6@%6:$6k7#6\7"6W7!6[7 6b96r=6G6!\;6"I96$E96&F=6)[?;86,B=;:7 68,68,68,68,68,68,68,68,6C+p;  E & ( H   j+<  ++t*&**b))~((u' 'F&%%-$O#i"r!k S1!N" $&i)*~, Bm )x(0' 'p&Y%Y$N#Y/"!$%')*+,-./ 0 0 1 2 2 3 3 44455566q r     k) 554443 3 0""#%'(*,,-//0 1 1 2 2 4 4 4 45656667     6666654 3 .65 65432/)"641."61,!65."$6.&6*'63")6**6/+61,62-62.6//6+ 06# 065 16, 26 260 36 361 46 46) 4645656%56056566UIFA<6W76! N6#!u6$!76%!76%!s6$!Q6# 96 D6B65QG965 67 6056%5646546* 46 362 36$ ʯlD"іJD!h $h%A' )/*T+n,v-k.O/* 0 0 12 2 2N 3 3E 444i55 5:6j6 Bl F & ( I ! ,h 95 54o4!43X 3 "K  2TMT   4 X o !!!!!!!!!!!!!q [ 7    UNP.!F!"N#%C'-+4 n3 2 :2 k1%"!#!                  !    ! ! """#%''*654 4 4 2<"60"]!65&> 61m 6.G6+86-Z6/B6376\6*G6496$x63Z6$J65<6+ 86 }765 d6. V6$!P6 I65 C65 ?63 <60 :6- 96+ 86+!96+ :6- <60 >64 C65 I6!P6!!W6'!b61 ~65 76# =61 J6 Y6,x696-G6[6276+B6#[65864I64o 65!?!6' `"6/<"65)^$63*=%65.'c(651//)) `RC>B.65776}746P46 =36 u726 Q26" p " xRJ8"8|Ly*w"w Fe} #?s c1   k O ; , % " & . > U t !  fJ A J~ khF H    / 1v +2)394y 4 4]6N"  akWj"l M ];o?  u _ +ڗg>=c3 t!;(e(  3)))1"U! e RHNfU> O  ~ 7 !!u O    !!!     R v   : OAYgSNZm ` <"W#Q%'*))(''%$#"!      !                  !###%'(+++))('I7"63i?"6"K!60 _8 6i96)r865i76 V6,E65:6\6&>61c65>6 \6$ 86* J60 d65 765 ?6!N6![6"!m6$!76%!76'!86(!86)!86*!86)!86(!76'!76%!m6#!Z6"!M6 >6 765 b64I6.86' [6 =6c64 >6+Y6"965D61T6$d765m86/c86!W763H!6&`=!65wF#6(kE8#65hF=&6(=<(64*6%)61)6(6*'62'6 3E!#& "]* +t  #3 h 7&M () "]> u-  99F8~8E6 6 B Wt+ i ]"  . M=! >=  x   R 8 #      # 9 T z  # fZ3x b` #y&A\K5Uig @!"&#f%'z* )T)("'b':) '%#"!    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!;. +'%%$#!                                               8:68*61/-*)0 8'62,8%62% 8#65'8"618!6/8 6.86086486!86. 8686,86862 86& 86 865 864 860 86, 86+!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*9)kC '@%#)"!T FX7,R X < * " ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! """""r"V""##~#I#$$`$%%D% &a&'h'(X)):*x* ?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""###$$$%%&&''(())*?6!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76%!76&!86*!860!;64!>6!F6"P6"X6"l6"76"<6"J6#]6#76#>6$U6$76$B6%^6%;6&T6&96'Q6':6(X6(>6)p76)L6*=6 2!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!! !!!"""S")""#s#.#$$6$%l%&& ''(|( )K)*).......................................---,,+**)('&$ "  )))))))*.......................................---,,+**)('& $ " ********)6%. ..".--5-6&-65,6),65+62*6- )6+ (6,'6.&63$ $ 6/"# 6/( 640--%656%)6%)6%)6%)6%)6%)6%)6%Z....................................9.`..-/--,z,+*c*:),(8'_&$ o " ! ˉP!| *b8l F,,,,,,,,,,,,,,,,,,,,, --------------------- 8,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,6 "Y!N YYNY BHDB H!D"C#E$I&H'I'T(I*L 2 2 1 1 0 / / .-,+*)(&%#! 3 3 3 2 1 0 0 0.-,+*)(%$#  264 26$ 163 16! 06- /64 /6".6)-6. ,60+61*6/)6-(6( &62 %6+#6/ !6/$64-#63/*6565420/$ 2p 2 1e 1 06 / / .!-?,W+b*\)G((& %C#v!_ sԴg9 0 0 =/T.f-m,s+n*h)W(;&%s$>"{ &S `"I$'G*/ @ 2 1 0//-,,+*('%%"  "$'*. @ B16 <06 u8/6b7.6W.6R-6Q,6T+6Y7)6e9(6}>'6E&6W9$6uA#6S:!6H76lB76 eB76"mC96$H>76'pB=86*D>;86.P@;:97654 @*M** *"+]++q*g)L(+'%d$%"f!( 0"#$ _'l* G. Lq @&%$#"!  !"$%')*- /25 z'&%%##"! !"$%')*-/ 25 z&6%%6- $61#63"64!65 656563616, 65'!62#6+$62 &64('65+)64*+62+- 65/' /64/+2640/( 5312.  {&%6$[#"! pG #!"?$ %''A)I*8- u/|)2ȎR5vT( z!!!!!!!!!!!!!!!!!!!!                      86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86*!86* ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! +9,a--. /& 0. 1& 2 45v6Y79a;<(>= +,,-./0 1 2 3 4 679:<> +v76,Y6,H6-A6.>6/< 60= 61= 62@ 63F 64S766j<67G69a<6:N966<H9> +C,y,- ./" 0' 1" 2 3 4u6<79B:s<> )))))))))))))))(&$B! Y`*****************&$ )6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%)6%F:'6%H>7$6%qE;"6% sE=;6%`B=;:876%/ ^ ! "@$} &/'b('1%@$Z "q } % 4> YP v^U w 6\؋#(@@@@@@@@@@@@@@@@@@@@@@@@@@@@pppp|C>!zA Layer #1!=l "     %$#zA1_1k1w1zAvO3M]m}ɍɝɭ͢~kT jz!$&)+{+++++++..$.D.d/0000000001 111"171?1G1O1W ?=<<;::9877665555555 ?=<<;::9877665 5 5 5 5 5 5  ?6=6<6<6;6:6:696867676676676576576576576576576576 ?=P<3}<h;_:?z:z9 f8E7)z7g6L61u5i5 z5}5}5}5}5}=;986 5 4 2 2 10/..--,++********=;986 5 4 2 2 10/..--,++********6=6;696866 65 64 62 62 61606/6.6.6-6-6,6%+6%+6%+6%+6%+6%+6%+6%+6%+6%+6%66%66%6=HU;)59kl8IQ6&uw:5_q4Q}k27wH2"w )1i k0 I/ u&._.}Q-w6-w,^+6+o*|*|*|*|*|*|*|*||HU|)5I:8(75l32J 1d 0n /q .k-X,4+ +}*@))a((c' 'G'&&U&%%%G%n%%%%%%%%%%%i%<%&&&K&''<((W( )S*:865 3 2 1 /.--,+**))((''&&&%%%%%$$$$$$$$$$%%%%%&&&''(())mB:G?768G:66I865Y;63F 62o= 61\8 6/T7 6.R6-V6-b76,y86+=6*J6*j6)<6)V6(96(S6':6'^6&A6&76&Y6%E6%86%z6%]6%Q6$I6$@6$<6$:6$86$86$:6$<6$A6$J6%R6%`6%76%96%G6&\6&76&E6'c6'<6(Y6(;6)\6)?67:/]~8 Av6?x5(p3 T2p 1/{ /=~ .B -=-0~,{+ p*U*,) q)C(x(G' w'8&h&~&?%c% {%"%8%N$]$i$r$x$z$z$w$q$h$[%K%5%~%y%^&:&}&b'1's(?( u);)lB&*+,-./01 1 2 3 4 5 6765 4 3 2 1 0/..%3(!"l$%J&d(n)q+k, X. 4/ 1}5"&(+, / 0 <=2A$)*+,-./01 1 2 3 4 5 6765 4 3 2 1 0/.."%&'((*-,. / 2 43AL><:876=9'6*6+6,6-6.6/60616 16 26 36 46 56 66766656 46 36 26 16 06/6.6.6%6;:8736>:9760*#G?7 6& G:"6!I8#6*Y;$65F&61o='6+\8(6,T7)61R+65V-6*b7.6 y8/65$ =26/(J=6H65456T96543256C#64226Q7&63156B)62056W8+616E-6505 6>;/612 6M163 6D=6;==6Y726A.@Sbiouxe"Xw$s!'n"(k$)s!*n"+k$,s!-n".k& /s# 0s' 1p+ 2x' 3x- 4p-5p 4p-3x- 2x' 1p- 0x- /x' .p--x-,x8+xP%ɇux"zG /]~c Av"`?x#w(p%H T%#p'}/{(}=~)$B+J=,x 0~._ {/` p2w@U6~xǶ˥#Т&촄)+-ˆ / 0 2 2 2+;U8$6]4i2c 0< / -S,*')N(b'r&y%$x#r"`!H $uDw4  I!0 Q1 " K  2TM               ;8642 0 .-+*)(&%$#""!  !!"#1 3 4 "!#!   655 4  4 3 3 2 2 1   1    ;fA=88H=766cB964XA62cA 60wD 6.N9 6-l?6+O86*B6)l<6(]86&R76%M6$K6#L6"P6"[6!l76 :6@6L6l76<6L 6v7 6@!6a"6;06 Z26 <"606 ]!65&6> 616m 6.6G6+686-6Z6/6B636766\6*69C646V86$ 6J63 6D6$ 69?65 6C96+ 6Y76 6K65 6G6. 6C6$ 6A6 69?65 6?65 06O?63 4/6<60 64/6:6- 64/696+ ;9^|84b~6My4Z2\ 0S .=z -g+=|* _)r(0{&?~%I$M#L"E"8!'} whO*~ sQ #~ i!5! r" xe" <.s! s"z n" 8 ] k$m }<s!)x%n"\vk$|xs!>}&n"g=k&}]s#<ws<n3 q @ | ` : ~ t ] ԧH r6 x/l( x8s~ xPv}  `y} D%  #%')+,. / 1 2 3 456789::;<<=(  K> i!c<S +',N- b/ r0 y1 2 x3r4`5H6$  u!D"$w$4%% I&%Q#C % !%'(+-.0 0 1 3 4 57889:;<<==>'U/*%#%(*+,-/ 0 2 3 34568899:;<<=>'=CM@;:9876532.&%F=;86520,!61.% 63,"#60'%63*'63))63& +6/,65).60/65" 16* 26- 36/ 46156266176/86, 96(:6:64;6. <6$<65=6- '6987 654 676<:987652640/.-! UN?<>;7 6=:9762-$xH?76?<96/% rF9?;7 6"XL>"6cA6B: 6wD6@?6N96;76l?*6O8+6B-6l<.6 ]8/6 R706 M26 K36L46P56[66l766: 676@!6E6L"6T6l7"6@:6<$6A6L%6D6v7%6H6@&6L6a'6R634567988986;%62-}76=<:66Z$6/% 86?<C&9HXcluy|}zwof\N@0%%C`yoS8 uT- |Y*#uE%}U'X)~N+x7,e.z7 /W 1l 2w# 3|/ 4~45~76~17}(8x9p :^:B;} Hz@e\Sց=zg =|  _ r 0{  ?~% I1 M2L3E484'}5w h!ֺO!֮ *~"֦ s$ Q% #~%i& 5&į r%|X5<#t> |X7xuMhh   Q    c  0 Y g&&&&&&&&&&(, / " """"""٤!       W6        ''''''''''),. 2 458:;=,#><1;:9 .9988777778888W862686+UB=86"UB;6865oC;686/[@7686'\@ 68 6 nB 68 62 G7 68 6, `;68 6"K68 65|?68 60g;68 6(Z768&68&68&68&68&68&68&68&68&68&68&60,8)61.%8,63," 8/60' 8"6G7 63*8"6G763)8"6G763& 8"6G76/8"6G76465)8"6G703608"6D06365"8!6/R8626B4 6.86.6G316086-6G7306486/46G764/6!86406G7664/6. 86.6G764/686-6G76306,86.56G761686036G760362 86306G76.56&86.6G765.686-6G76216586-6G76/464 86-6G7 6.6086.6G7 6256,86.5 6G76+86/5 6G76*86/4 6G76*8604 6G76*8667603 6G76*86W|26|x1P|eDn|FAm|}$W}|r  ` | X [ | 7 H} | z$p| hI| Jg| ~($v| t3}|q|&|&|&|&|&|&|&|&|&|&S8|(uT-|,|Y* |/uE |" }U|" X|"~N|"x7|"e|"ߔz7|"W |!{.|o {#|},|D|d|}|P|}|Y|)|݃x|]|B|,| ~| }| }| }| }| }| }|555555555f  4 4 4 4 4 4 4 4 4 4 4 5 5 4 4 4 3 4 4 4 4 4 4 3 4 "  u`Mڼhh Q c!0"Y#g%&==============5 5 5 5 5 5 5 5 5  # 4 4 4 4 4 4 4 4 4 4 4 5 5 4 4 4 3 4 4 4 4 4 4 3 4 4 4 &$'+/ 1 358<=576576576576576576576576576YB<;97876 676:6 46 46 46 46 46 46 46 46 46 46 46 56 56 46 46 46 36 46 46 46 46 46 46 36 46 "64/6 6987676126 6;:9864(+*)0UB;67642265oC;6b76146/[@76T76'\@6T6 nB6T62 G76T6, `; 6T6"K"6T65|?#6T60g;$6T6(Z7%6T>6T>6T>6T>6T>6T>6T>6T>6T>6T>6T>6T>6T>6T>6T>6T65}5}5}5}5}5}5}5}5} /AS_ekqvxD Hxyn H H0H H0H H0H H0H H0H H0H H0H H0H A0H V0H '1H V1H V1s V1# '1V V1 11H 2 V1A #1s s1# A1V 2 H1A 1s V* #񊝯  h s ǂ :"DnH}ϠFAmXǍ}$W}Ă r  `ǂ X [ǂ 7 H}ǂ z$p ǂ hI!ǂ Jg#ǂ ~($v$ǂ t3}% q&5555566778ց89::         5 6677777X>>>>>>>>>>>>>>>>>>>>>>>>>+        5 5 66788888888888;<<<88888886%66%66% 66% 66% 6 6% 6 6% 6 6%6 6%6 65!6 56 66667676%76%76%76%76%76%6=6%6N6%6S6%6T6,6T6.466T6.5666T6.566T6.566T6.566T6.5666T6,.6T6%6T6%6T6%6T6%6T6%765Y6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.5=6.>6.>6.+6|kl|IQ|&uw:|_q|Q}k |7wH |"w ) |i k | P I  xyu& !_ }Q w6 w ^6oߍ|ډ|ه|̅|DŽ|B}>w.wp7Y ~ړ|Æ|誂||ˇx) . / 0 1 23456* *ǁ * * * 543322 1 1 0 / / * -,,+*)('&%$#"!  !!!  !#$%&( -,,+*)('&%$#"!  !!!  !#$%&( 6-6,6,6+6*6)6(6'6&6%6$6#6"6!6 6 6!6!6!6 66 6!6#6$6%6&6( xq.-x.,p),r!+r*g)h(m'` &[ %}o $ #ˏ "! }|\+n6 L!&#xC $i*%i&K(*7+q+,(-J._/i 0f 1\ 2D 35l685:0=g}<;;:98765 4 3 2 1 001 2 3 4 5 678:;;<>**+,-./01 2 3 5 68:=}<;;:98765 4 3 2 1 001 2 3 4 5 678:;;<>*u76*N6+@6,:6-k76.\76/W7 60[7 61b9 62r= 63G 65\;66I968E96:F=66=[?}6<6;6;6:6968676665 64 63 62 61 606061 62 63 64 65 6667686:6;6;6<6>6*#~*L+j,x-(}.5~/9~ 06~ 1*y 2m 3Q5'p6Ay8 Fy:5e=?}& =        2  K   <  W S7q$(>>>>>>>>>>>>>P=<:9752/3   !"ʃ>P8 3 2  2 1 1 0 00./V=<9874   !" 6K>6E632356B60369?6.56@;6136E96- 6G8640 6G7612 6G7612 6G8640 6C96- 6>;61369?6.56B6/46F63236;86K36B=;:76R726 G678::96 \6;B6 769@6 E6I6 c6Q6 <6O6 Y6I6 ;6A6\6O76?6J<6u76F<6N69;<7$6Q;>6J8>6G?6D?6B?6C?6D7>6D9>6G=?6A8>6A=7>6A<7>6@;9?6=987>6;98T6/=6-<6( :62 96+76/ 56/$264-# /653236654356 6 66 6!6"6 2 Ɲ       ߅ߌa| !7J]lrx~2 ^ : } b 1 s ? u;l#~Lx+*)('&%#" "%?|=w$l"3Hʃ .,(+(    84 0 //.-,630 686+ 6+ 96+ ]6# :6- >65 <60 m6, >64 G6 C65 860 I6 Z 6 P6!B 61 W6'7 6 b61\ 6) ~65G 64 76#9 6 =61 x 6% J6Z 60 Y6,J 65.6554432/)x6<6-64Y525 686- 69>64226}76. 6D63156d6.5 6Q620566V6.5 6><616P6/5 6E64$I6/5 6S76*C6.5 69? 6/?6.5 6H 61 6<6. 6C: 62 6T96634 6- 6F 62 6G7630 6- 6R9 6/ 6G76.6E 6+ 6G76316R9 6$5 6E86/56F 65. 6E96-6C< 61 6C:6.6J 6. 6>< 60369? 630 69? 6-6E 6. 6B 64/"640 6C 6/5#6- 6F 65.$603 6L 6/5$65. 6V 65.&6. 6C9 6.5&6/569?621'631 6D65.)6. 6J6.5)6- 6V76)UIFA<6A6- 69>6. W76B6. 6D60N6D76.5 6Q61u6F:6.5 6><6676E=6/5 6E66769A76/5 6S76s6B;.5 69?6Q6;79 6H696 H7 6C:D6lB76FB65eB76R QG965 mC967 60H>716% pB=8.6-65-6*-6,62 ,6$ x@{} w y} 8g w~ mD t~ )| m+ \_ e: |' ]O >d Qfg ~# Bz } W 3%< { !J^ 5 }qy Zr%# xZY? ~ ?`H 64 Z xvqkeXG6"# `$ q ~qĥ H | Т` ~ 촄2 񴂀VA P Q\ րke y*l ~7 t ;1 69 ~6  }(/ wV ߂lx   ߁  ݁             t!Al { 6~ },O }1# .} } % Jx xh K} `[U~ aJT} Aw4Hw x~ ~0_~1x 6\{.\ -8-|-^,,,o" 4 321 0 R/ v/ / . :. - . O- . A. . Y. / / g0 S1 N1Z2m34`<#W',& &%%$""!5432 1 0 . -,*!!# WQ Ƿ +ͱ&n1 1 1 2%76653 4 2 2 0 2 0 1 0 0 0 0 0 1 1 1 3 3 346 7'd$<;;:977653 2 1 1 .,++$&')/> %1 1 1 2<"60"8565&8461836.726+716- m16/ Z063 M06 >/6* 7.64 b/6$ I.63 8.6$ [.65 =.6+ c/6 >.65 Y/6. 9/6$ D06 T065 d7065 m8163c8260W736-H56+`= 6126+wF#65126+kE8'632465:=-645689989930&6465/&6%6.%616-%66.$6*6/4"62640"66-!6%6.56- 640616.6363464565465365 263 161 06, .65' -62,6+*62 !63464("630665+9#6/&E!64223*kER9632465.'hF=6F653 651//)) `RC>=<6DQ?76534346577 6}:H@;:97654544&6P616 16 16 26s"z@| ]|4}< |3x%}2v~1x *0}& =0= T0] m/w }.< 3.n [.3 {.q <-@ o.| 1.` n.: =.~ x/t `0] C0H .~16 &{1(+{2~5}3}P} k }Q!ڟ}N|#뺐Jb%˳& &o%*%Y#w"1"V!m z~.@ M ߤ'Tdž)T߄)Q)G )}8 (v$ &e &~E%l #|DZ c! ~% Ӝ[ N߂躋l; 3b 㾔 mL4  2Ii$D ǩ~y~ q&9IXcmvz}˻%3 s/ 0 A0 `0        ܼ~7\u O          Rv  :   O  A  Y    g S  N  Z m  ` <    %$$#""5888 , +   #        & % & % & % % % % % & %&% % ' & ' '  ' ( ( * *+,,-! 0114888''%%$#A>603 6G76*86B:603 6G76*86@?6604 6G76*86;7/4 6G76*86&1 6G76*86# 6G76*86"6G763 6*86 6G7626*86 6|76.6*86586-6*86486/46*86.86406*86'86.6*86 86-6*8686.56*864 86036*86+J686306*86"d686.6*8657686-6*861?6M86-6*86$ N6G76-6*865 [6G76.6*86/ m6G76.56*86! 76G76/56*863 76G76/46*86& 86G76046*865 86G76036*86( 86G76036*8665 86G76036*866( 86G76046*866 76G76/46*86676G76/56*866m6G76.56*866Z6G76.6*866M6G76-6*866 >6G76-6*866 76G76.6*866 b6G765/6*866 I6G76216*866 86G76/46*866 [6G76.6*866 =6G76-6*866 c 6G765.6*866 > 6G76126*866 Y 6G76.6*866 9 6G76-6*866D 6G76406*866T 6G76.56*866d7 6G76- 6*866m8 6G7631 6*866c86G76. 6*866W76G7 65. 6*866H6G7 6/5 6*866`=6G7 6. 6*866wF6G7 603 6*86686G7 6- 6*86G7 603 6*86G7 65. 6*8%6%8$618$68#6*8"628"686 }| }| ց}| }|~/Vs }|r:Vs}|j A` }|^ 'Vs }|P  }|A| }|-| }|~| }|{| }|g| }|M| }|0| }|}| }|j,V:| }|I}sV/|}|~!ksZ}|qR }|J < }|~ )}|f ~}|4 }}|s |}|B |}|w |}|E |}|w |}|= }}| ~}|*}|=}|T}| m}| }}| 3}| [}| {}| <}| o}| 1}| n }| = }| x }|` }|C }|.~  }|&{  }|+{ }|5} }|P }| k }|Q }|| }| }| }|%o|$*|$Y|#w|"1|"V|5/- +)('&%$ $##""r V   ~I`D a h !X"":#x# $$$$$$$$$$$$$$$$$$$$$$$$$$$k2 .,,+*'&&%$#$$#""! ! !!    !!""#$$$$$$$$$$$$$$$$$$$$$$$$$$$56T161/-*)07-62, 7+62% 7)65'7(617'6/7&6.7%607$648$6!8#6. ;#6>"6,F"6P!62 X!6&l!6765<64 J60]6,76+>6*U6*76*B6*^6*;6* T6* 96*!Q6*!:6*"X6*">6*#p76*#L6*$6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%5-sT6"}-{O  }+|H })_}(}@}'{*}&{#}%},}$D |$d|#}v#Pm"}`"YR!)@!x* ]} Bs,Y~:}~}m}E}~}g}6} v}F}z} I} x}!>}!n}"&~}"Q}# }$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$>л 9a&.&   v Y   a  (=BY` !X"":#x# S8888888888888777546566! 3 1 0 1/ +'%  !!""#6.>6%76%6%76%6%76%6%76%6%76%6%76%6%76%6&76%6*76%6076%64 76%676%6"76%6-76%6576%6&76%6576%6)76%6576%6276%6- 76%6+ 76%6,v76%6Q16%6T5046%6T663156%6T63236%6T6533446%6T6%6T6%6T6%6T6%6T6%6U6%6W6&=6[76*v76P760Y6E864 H6@:6A6;=6">6B6/ <6D65)6=6H6/36=6Q6.66%@6G96036%F69?6-6% S76E63 j<6O6- G6@:6+ a< 6C6,N9 6M 6H9 6><6F:6E6H>76W86qE;6B6sE=;6P76`BJ8876 :6!X6!>6"p76"L6#=6 =|}||}||}||}||}||}||}||}|}}|~}|0}|K}|k}|~}|E}|u }|=}|w}|T}|2}|y}|t}|t-}||ы|||Ĩ| | ޕ | 豃 | ͌ |||Ў|oޣ}"}ć~=ޗ0VKdʌk mٞpphmw1 c}HTM ;}֟6 n⭆y N 縉t !l 뾏t:w Œ Dy ̚  @uԟ -\~ ڨ;g ⯇ +Kl뻊wő x!>!n"&~"Q# o j>=;972 0.-,*)'& j>=;9720.-,*)'&6 j>.=63$ ;6/":6/(7640--%2 65606.6-6,6*6)6'6&p- j>y0=Q ;v89s@7~fE(2 z>0 z_.h1-}[,8*l )'F&#$%&'())('&%$#"!  !# #$%&'())('&%$#"!  !# #6$6%6&6'6(6)6)6(6'6&6%6$6#6"6!66 6 6!6#6 -p!'x"-x#-p$'x%-x& `'!s&$k%"n$!s#$k""n!"s #n%p$u %p *p!%u#&p ++**)(('&%$#"!!#&  ,,,+*)))'&%$#"!!#& +64+6$*63*6!)6- (64(6"'6)&6. %60$61#6/"6-!6( 62 6+6/ 6/$"64-#$63/*& 6565420/$ +8+p*3*e)|(C(c't&{ %},$}1#|."w$!iKm"w;u@ !~_0#y\:& }wqjZG4 2 2 3 3 3 3 s2 n1 h0 W/;-,s+>){'&&S$`"I G#( A 2 2 3 3 3 3 3 3 2 1/.,,)'&$# #' A 26 26 36 36 36 36 Q36 T26 Y706 e9/6}>.6E-6W9+6uA*6S:(6H7&6lB7$6eB7"6mC9 6H>76 pB=86#D>;8640'P@;:97654312.  B 1 '1 V1 s1 2 /2 V2 91 4}0 &x/k.U,2y+_)3v( K}&U~$T}"Hw 0_~ 6\{#$B_x{d'&9IXcmvz}~{wneZK;* A  !"$%&()+ - . 136 "!! !#$%'(*,- 0 25 6%8 6- 861863864865865865 863!861"86,#865'$862&86+'862 )864(*8 65+,8 64*.8 62+0865/' 2864/+5866/(  m | z|~.|@|M|T|T|Q|G |}8!|v$#|e$|~E%|l '||D(|Z*| c!,| a%-| |T0|h; 2|~d>5|G)  $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$ 6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6*%6* }$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$}$} $9%a&&'(&).*&+ -.v/Y 0 2a 45(7=9<B @$%%&'()*+,-/0 2 3 579< @$v76%Y6%H6&A6'>6(<6)=6*=6+@6,F6-S76/j< 60G 62a< 63N965H967F:69H>766<qE; @$"}%=%V&d' m(p)p*m+ c,T-;}/n 0N 2!l 3:w5 Dy7 @u9-\~<;g @%$"! !"""" !#%')*Y 0` %$"! !###" !#%')*0  6%6$6"6!6 6!6"6%"6%"6%"6 666!6#6%6'6)6*sE=;60`B=;:876 %/$^" ! @ }  / b  1 @ Z  q }! %# 4% >' P )^* +Klw0.AT؋#  76 5 4 3 2 10/.-+*)('%$#"  "#% 76 5 4 3 2 10/.-+*)('%$#"  "#% 7666 56 46 36 26 1606/6.6-6+6*6)6(6'6%6$6#6"6 66 6"6#6%6 Px4Px3 Px2 Px1 Hp0 @p/ @p.@p-8h,0`x* Px)Px(Hp'@p&8`x$ Px#Hp"8h!0`x Hp@p8`x  Hp"8h#(Xx%Hp   6    6    6  <;:875  <;:875  6<6;6:6867656  ?p=h8;x`0:pH 9p@7x`86pH 5h83xX(2pH  +,-./0 1 2 3 4 5789:;=? +,-./0 1 2 3 4 5789:;=? 6+6,6-6.6/60 61 62 63 64 656768696:6;6=6? xP+xP,xP- xP. pH/ p@0 p@1 p@2h83x`04xP 5xP7pH8p@9x`8:xP ;H=?????????????????&&&6&8`x???6????6????6?4 4 46 0x`8 ????????????????9999Po(7|9"isort your imports, so you don...!? "     %$#Sgimp-text-layer7(markup "isort your imports, so you don't have to.") (font "Sans-serif") (font-size 62.000000) (font-size-unit pixels) (antialias yes) (language "en-us") (base-direction ltr) (color (color-rgb 1.000000 1.000000 1.000000)) (justify center) (box-mode fixed) (box-width 892.000000) (box-height 57.000000) (box-unit pixels) (hinting yes) 5|95@|955={JKWcox4R@@@@#; ; ; <}* \( ]' S& nT! ! e! ! \ ! 1! sN! 7K! U'! J! !! T!  ! AlE! tM& v' 6";;;;* ('!!!! ! !!!! !!!! !!&'5"o;o;o;o;piP>8641**od>62)(o_760/07;?<76/'o85"nE5!oQ6&5^!o?6:!o<6S6!o@6%<6!oX66+# 75!oF630,!e63!oU?861-#]61!oF?<6/\61!o mEA764g64!oq>6075!o75=6!o85X6!o' 75t= =R<:::d;;;;;;                               >>==<999d65 ;75;75;75;75;75;75`=962. 86^=(86O65$6+ ,a@65,85sB76(86* N76634698664!85d96(76;6626* N66/"Q<66"85o86.+&75]6) C6585.<1756)A60 867/7582 86864 75# K6,86$ 75 m6586 75v 7685 75  8685 75Q 8685 75 7585 75/ b64 85 75& C6)85 754 j6685 756.;6-85 7576.k;6385 76UZ763+$bLD76485 862(2?C2^:6185 O65E:62)85 H86650)J:52&C+>>==<999d /0;:o;:o;:o;:o;:o;:o ly9NP!TgMPxPN OO:Tk(! ʳ kR  w _ kGw5:o5~)r:o % EL:oSWd 1| :o ) :os2 Tj :oV " :o>  :o*r@  :o[ , :oFL f_ :o  :of~ T :oB)1 :rQ>~ 7 nO C GEz/Tx ~:& ܈ -TaW5&0>3>==p<999^ ` lZa k  k ( k KY k    k Ol $ k   k  + k x4  k  R  k DO  k *  k  A y n   Z F { 82f H_d mbw  j] . p +<<<==>?                               <<<==>?ً , > |.  C{.  C9.  E8|.  I"4.   M .  ay .  h C.  t$ r.  oH .  o  ~.  ov* l6  or 3J  o l  olEc  o F ouhko ɨx>o CoD Q ژo{t<<=@==hOO ly9OP -PNNP!}:TE_ kʳ E_ kF<w _ E_ kGj)E_ l% EE_ qd 1E_ |  )E_  <:9 TjE_ bV "E_ jv> E_ ft@ E_ \[ ,Dc 18/ f_?o ( 0  h|~ T[B)1'niNQ>~^6 nO C 8f:A Txc.' e & Ph -TaW5 1Y_L <4<<c==> ?; ; ; <h  M  ix `  y   e~l'   J~      u   M|   ' j    ^    \    \    \    \    \    \    \    \    \    \    \ > > > > > > > > ;;;;                                 >>>>>>>>>o;o;o;o;p iۂp o ovǷ o5ݢ7Yіo@hQo(oY+o  o{ < oo  oo  oo  oo  oo  oo  oo  oo  oo  oo  oo  oo  >>>>>>>>> uxE;;;; mp@Tg NP.NP=?J2( WDWR Wz w5 Wrq]vZ /r(`EL-%v< q [f ( QW + PW , PW , PW , PW , PW , PW , PW , PW , PW , PW , PW ,>>>>>>>>>~<<<<<<` h a M `N K@Y %   Nl $       d +  4   R   O   &*   o A     [Z F ;2 _H_ Qmmb >j x . <<<<<<<}<<<<<<                                        <<<<<<<}<<<<<<ޖً >{ |C C Ϙ9EǷf8| "4Q tM a {   o h Ct o ;$ ro o X o o U ~o o 4* lo o r 3o   o ] uc o GFo slhko qȫPɨxo o o ow Qo odo<o<o<o<o<o<o<i} /0<:<:<:<:<:<:NO QD ly9NP!TgMPxLpY :Tk(L ʳ kR Y\w _ kGw5: ,j)r:)'% EL: fd 1| :   ) : >9 Tj : NgbV " : 7zv>  : :xt@  : Wb\[ , : :/ f_ :   :s v|~ T :D8*B)1 :]FNQ>~ 7/RDnO C - Tx  K & 2W`M-TaW5<<<<<<<NP.@>>>>>>>ddbbZZvv" " ! bb  zz 77UUO O!!!"  Ds Ds{U {U~~+ " <^<p!@>>>>>>>    ! !           !!        "<<!@M>J>J>J>J>J>J>99GGmmJ  JJ! J|!| J  J͟͟ JќќJ إإJ ݉݉JJ  Jb!bJo!oL S S+ +y y[ PՁՁy]]]"<=!@>o>o>o>o>o>o>PN 3oV3oV!@$@$cdTd^cdTd^o-%-%o[ [ o! o! o  oX X ou6u6o EX EX o PlPloYYo-@ -@o6!6o!r4 "x4 "xO1ԑ O1GEz/& &~a߽  a߽ ܈  fW fW&02K^\D 2K^\D"O8<<!`& `a  Za {   (Y Y 4 l $ Ol R + 4  qx4R  RO DO* ,* A FnZ FZ2c8H_Hmb mj j. .';t: :R:899                              ';:::788ً, |    {9 ] 98|  8"4 8I"M   M  yh C vh$ r  $  QH ~  * l/v*r 3r c lEcF Fhkuhɨx ɨx  QD 't;:;@:7h89 ly9OO OO l:T  }:ʳ  ʳw _ 5~ F<wj)  j)% E SWl%d 1 qd  ) s2 9 Tj <:9bV " bVv> *rjv>t@  ft@\[ ,FL\[/ f_18/ f(|~ T h|~B)1[BNQ>~ iNQnO C6 nOTx: T&e -TaW5-Ta'3;4::pc:78 8^>S=T=T=T=T=T=T=T lT kT k T k"h ks, k $ k u  k  q+ k  T k & T k C T k @ T k  TA y  T   qF { P 2f _dCDbwj3]i  p5v+>=======                                           >=======ً , ۆ|.  d.  w.  ڣ$r|.  54.  .  L .   C.  w r.  = .   ~.   l6  N 3J   l    ~  `k>δCQ ژm{Y>7x=w=w=w=w=w=w=wy9OP -PNP~=wTE_ y@w E_ ɳvw _ E_  ltwxE_ " EE_  ,#1E_  h &)E_  TjE_  4 w"E_  Xh w E_  kP wE_  hS w,Dc  Qs wf_?o  $ w0  ! T p )1'n e >~^Hv" C 8f:Alc93xc.' U& Ph =lW5 1Y_L 0N^H=1111111 ` 0a +  ^  X$ Y     l $        +  4  | R  z O  z *  z  A z   z Z F z 2 z H_ z mb z j z x. z =1111111                                                  =1111111 ً o| l 9)ċ 8|wN "4Y M m   h Cy  $ ro   o   ~o  * lo  r 3o   o  c o  Fo  hko  ɨxo   o  Qo  v=/1I /0/1P:/1P:/1P:/1P:/1P:/1P:/ ly9NPX.7MPx/:TW /ʳ WL /w _ WWD :/j)ir :/% EY9 :/d 1 :/  ) :/9 Tj  :/bV "  :/v>   :/t@   :/\[ ,  :// f_  :/   :/|~ T  :/B)1  :2NQ>~  7 nO C  !Tx  !&   -TaW5( ( ( ( ( ( ( ( U  5S Tc UPh B 2.  7  Z  f | zR zM z  zQ  z  z , zi z# z^ z zF+)((((((((                             (o(Mo(Jo(Jo(Jo(Jo(Jo(Jo( o= za onL oLRJqvPJj7MJLz JeJ =Jy  ULJo NJo NJo ̒NJo  NJo  NJo  NJo  NLo N o R o F8[ o bCP o ۩Cy(p uxE((o(o(o(o(o(o(PN gj Ep,OP*! ]%/  CT}^YxKo<4#& L'%oW \g+FoJRNo bIo?h|o  6ro KҧkTso 8so IR Bso  6so u$ 6so  6so  Rsr +s  :vGEz/  !v_)~  ȪDk܈  vq Ek&0(6XW=u;;;;;;r^ k   | bVJ Ou l ) t ߃: 4%AT RN OP&* m|9N Z WI;g  `n s \x +t;;;;;;                        tM;J;J;J;J;J;Jߞڑ  0 n W J [J9 GJ J(  JbI V JS{ J-  J} J 0JXpJLJ& N Jv' LD G B [ qP@yt /0;:o;:o;:o;:o;:o;:o/PKjDMPxPN  >m!0 @Pkx  ) }:o ~xD:oZk& Z^:o(  :o'7G  J: :o ?p z :o9ufv ,@ ,[ ,, f_, ,~ T,B)1-NQ>~cԯ&nO Cw'Txw(&w*-TaW5 \ \ \ \o7z0 Layer #5!? "     %$#z08DP\z0^n~(8HXhx(8HXhx(8HXhx(8HXhx(''''''''''''''''''''''''3 3 3 3 3                                                        Lo&7\i!? "     9%$#gimp-text-layer(markup "i") (font "Sans-serif") (font-size 62.000000) (font-size-unit pixels) (antialias yes) (language "en-us") (base-direction ltr) (color (color-rgb 0.937255 0.513725 0.211765)) (justify center) (box-mode fixed) (box-width 275.000000) (box-height 348.000000) (box-unit pixels) (hinting yes) '\[BNZ\[k{ӂւֲ֢֒"2 D0+({$)"Y hma C! /*'$"  "# u/ъ+^'%#!j+!# L̥j#/T W*o<'W$"? ]l6aEA"#Z$%r%&\&''S''(('''&&$;##!" {  :"Q$A'+P /&^^^^$&&''(()))***)))((&%%$"! "$'+/ &            "~$$%M%r&'''t'Q(( ('V'&& %$#Q"!( #{%1'a+ Z/э'k-j-j-j-j!!" o"?$q%%%&5&x'w&J& &%y#%#% $8$# c!a  4  5 "B_$/' 2+# ߈%/ Jʤn/&N[+N[+N[,N[,N ^^^^^^^^\\\\\                                         -j-j-j-j-j-j-j-j-j-j-j-j- [,N[,N[,N[,M[,M[,M[,M[,M[,M[,M[,M[,M[,?>>\>>===\==<<<\<<;;;\;;:::\::999\99>>>>=====<<<<<;;;;;:::::9999988>>>>=====<<<<<;;;;;:::::9999988>>>M>====M=<<<<M<;;;;M;::::M:9999M988\     !!!!!"""""#####$$$$$%%%     !!!!!"""""#####$$$$$%%%j-  j -  !!j!-!!""l"-""##l#-##$$l$-$$%%l%-%%M[, [ ,   ![!,!!!"[","""#[#,###$[$,$$$%[%,%DW"+\i #3!<9X "      %$#gimp-text-layer(markup "i") (font "Sans-serif") (font-size 62.000000) (font-size-unit pixels) (antialias yes) (language "en-us") (base-direction ltr) (color (color-rgb 0.937255 0.513725 0.211765)) (justify center) (box-mode fixed) (box-width 275.000000) (box-height 348.000000) (box-unit pixels) (hinting yes) (\\CO[\\l|#3 D0+({$)"Y hma C! /*'$"  "# u/ъ+^'%#!j+!# L̥j#/T W*o<'W$"? ]l6aEA"#Z$%r%&\&''S''(('''&&$;##!" {  :"Q$A'+P /&^^^^$&&''(()))***)))((&%%$"! "$'+/ &            "~$$%M%r&'''t'Q(( ('V'&& %$#Q"!( #{%1'a+ Z/э'k-j-j-j-j!!" o"?$q%%%&5&x'w&J& &%y#%#% $8$# c!a  4  5 "B_$/' 2+# ߈%/ Jʤn/&N[+N[+N[,N[,N ^^^^^^^^\\\\\                                         -j-j-j-j-j-j-j-j-j-j-j-j- [,N[,N[,N[,M[,M[,M[,M[,M[,M[,M[,M[,M[,?>>\>>===\==<<<\<<;;;\;;:::\::999\99>>>>=====<<<<<;;;;;:::::9999988>>>>=====<<<<<;;;;;:::::9999988>>>M>====M=<<<<M<;;;;M;::::M:9999M988\     !!!!!"""""#####$$$$$%%%     !!!!!"""""#####$$$$$%%%j-  j -  !!j!-!!""l"-""##l#-##$$l$-$$%%l%-%%M[, [ ,   ![!,!!!"[","""#[#,###$[$,$$$%[%,%DW"+\i #2!=x "     (%$#gimp-text-layer(markup "i") (font "Sans-serif") (font-size 62.000000) (font-size-unit pixels) (antialias yes) (language "en-us") (base-direction ltr) (color (color-rgb 0.937255 0.513725 0.211765)) (justify center) (box-mode fixed) (box-width 275.000000) (box-height 348.000000) (box-unit pixels) (hinting yes) )\] D P \\]m}   $ 4 D0+({$)"Y hma C! /*'$"  "# u/ъ+^'%#!j+!# L̥j#/T W*o<'W$"? ]l6aEA"#Z$%r%&\&''S''(('''&&$;##!" {  :"Q$A'+P /&^^^^$&&''(()))***)))((&%%$"! "$'+/ &            "~$$%M%r&'''t'Q(( ('V'&& %$#Q"!( #{%1'a+ Z/э'k-j-j-j-j!!" o"?$q%%%&5&x'w&J& &%y#%#% $8$# c!a  4  5 "B_$/' 2+# ߈%/ Jʤn/&N[+N[+N[,N[,N ^^^^^^^^\\\\\                                         -j-j-j-j-j-j-j-j-j-j-j-j- [,N[,N[,N[,M[,M[,M[,M[,M[,M[,M[,M[,M[,?>>\>>===\==<<<\<<;;;\;;:::\::999\99>>>>=====<<<<<;;;;;:::::9999988>>>>=====<<<<<;;;;;:::::9999988>>>M>====M=<<<<M<;;;;M;::::M:9999M988\     !!!!!"""""#####$$$$$%%%     !!!!!"""""#####$$$$$%%%j-  j -  !!j!-!!""l"-""##l#-##$$l$-$$%%l%-%%M[, [ ,   ![!,!!!"[","""#[#,###$[$,$$$%[%,%DW"+\i #1_!> "     1%$#gimp-text-layer(markup "i") (font "Sans-serif") (font-size 62.000000) (font-size-unit pixels) (antialias yes) (language "en-us") (base-direction ltr) (color (color-rgb 0.937255 0.513725 0.211765)) (justify center) (box-mode fixed) (box-width 275.000000) (box-height 348.000000) (box-unit pixels) (hinting yes)  *\ ^"E"Q"]\ ^ n ~!!!!!!!!"""%"5 D0+({$)"Y hma C! /*'$"  "# u/ъ+^'%#!j+!# L̥j#/T W*o<'W$"? ]l6aEA"#Z$%r%&\&''S''(('''&&$;##!" {  :"Q$A'+P /&^^^^$&&''(()))***)))((&%%$"! "$'+/ &            "~$$%M%r&'''t'Q(( ('V'&& %$#Q"!( #{%1'a+ Z/э'k-j-j-j-j!!" o"?$q%%%&5&x'w&J& &%y#%#% $8$# c!a  4  5 "B_$/' 2+# ߈%/ Jʤn/&N[+N[+N[,N[,N ^^^^^^^^\\\\\                                         -j-j-j-j-j-j-j-j-j-j-j-j- [,N[,N[,N[,M[,M[,M[,M[,M[,M[,M[,M[,M[,?>>\>>===\==<<<\<<;;;\;;:::\::999\99>>>>=====<<<<<;;;;;:::::9999988>>>>=====<<<<<;;;;;:::::9999988>>>M>====M=<<<<M<;;;;M;::::M:9999M988\     !!!!!"""""#####$$$$$%%%     !!!!!"""""#####$$$$$%%%j-  j -  !!j!-!!""l"-""##l#-##$$l$-$$%%l%-%%M[, [ ,   ![!,!!!"[","""#[#,###$[$,$$$%[%,%DW"+}i #4!? "     %$#1gimp-text-layer(markup "i") (font "Sans-serif") (font-size 62.000000) (font-size-unit pixels) (antialias yes) (language "en-us") (base-direction ltr) (color (color-rgb 0.000000 0.000000 0.000000)) (justify left) (box-mode fixed) (box-width 125.000000) (box-height 235.000000) (box-unit pixels) (hinting yes) %"}%N6V6b}%':'J--'336F N0 ,t(%E#L!@G "F"# /,(%#! ""$$& ܹ+T G/^W! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !@@@@                                                                                                                                 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !@@@@                                           !!!!!!!!!!!!!!!!!!!! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ? ? ? ?>u:z0 Layer #7!? "     %$#C7oz07F7FCFOF[z099: ::+:;:K:[:k:{::::::::; ;;+;;;K;[;k;{;;;;;;;;< <<+<;+>?_?@A-ABaBCE7EGEWEgEwEEEEEEEEFFF'0P_rƾ/0P-@ , p*@)`(`'`&`&@% $p$@##0#P""0"P"_"o"""""o"_"P"0"#P#0#$@$p% &@&`'`(`)`*@, p-@ /0P20P`sƿûûûûûþûþû »»»»ü¿üÿüÿüþûþûþŻþùþùþùþù ÿüÿüÿƼÿĺÿĺÿĺÿĺþùþùþùþùþùþüþ ÿĺÿĺÿĺÿĺÿĺÿļÿ»þþþƾûûû ӿ»ÿ»ÿ»ÿ»ƿ»»»üûûþûþûþûþûþŻ »ü¿üÿüÿüÿüÿüÿƼþùþùþùþùþùþùþù ÿĺÿĺÿĺÿĺÿĺÿĺÿĺþùþùþüþþþþ ÿĺÿĺÿļÿ»ÿ»ÿ»ÿ»ƾûûûûûþû »ƿ»»»»ü¿üÿüþûþûþûþŻþùþùþù ÿüÿüÿüÿƼÿĺÿĺÿĺþùþo_P0)կP0&ϯ@$p #ߏ@!` ```@  p!@!"0"P"#0#P#_#o$$$$#o#_#P#0""P"0!!@ p @```` ߏ@!p #ϯ@$կP0&ÿĺÿp`P0                                                        Lo&7z< Layer #3 != "     %$#Ghz<Gwpw|wwz<IIJLO8OHOXOhOxOOOOOOOOV^^^^^_ __)_9_I_Y_i_yiFnnnnnnnooo/o?oOo_sfuvvv v0v@vPv`vpvvvvvvvvwww w0w@wPw` K/6 -] +H*(:'d&o%o$k#M")"!l $ 'T  /-+)('&%$#"!!    !! /-+)('&%$#"!!    !! &CXit{~yrcO6/ 4`~ -Mz+P~);z(e'(w&5}%<$7~#(}"w!f!D |\!~Y|=c { >;976 5 4 3 3 K 6 ]    "#%'() >;976 5 4 3 3    ()*+,,-... |>۵J;19v87 544 c3 | ۵J 1 v      c    s >n= ;{G 9v.7Q6a 5b4 Y3 =3 z &CXit{~yrcO6 W 4`~ n=  }Mz{G  \P~v. Q a bY=zW}LRt    |`%K e  J!z"#$%t&()F.x   < u !!!'%'}'n'W(>(%(}(n(W)>)%)})n)W*>*%*}*n*W+>+%+  8 s   8 s   8 s   8 q  '8's''&&8&q&&6666777778888899999::''&&&&&%%%%%%%%%%%%%%%%%%%%%%%''&&&&&%%y ~ 2 L d y ~ 2 L d y ~ 2 L d y ~ 2 L d y '2'L&d&y&~&2&L%d%y++,,,,,-----...../////00000 1 1 ++,,,,,-----...../////00000 1 1 +,,W,,,--W---..Y.../ / Y/ / / 0 0 Y0 0 0 1 1 X1 }+n+W,>,%,},n,W->-%-}-n-W.>.%.}.n.W/>/ %/ }/ n/ W0 >0 %0 }0 o0 W1 >1 %    Oo'7z0 Layer #4!? "     %$#xz0x}z0{{-{={M{]{m{}{{{{{{{{| ||-|=|M|]|m|}||||||||} }}-}=}M}]}m}}}}}}}}}}~ ~~-~=~M~]~m~}~~~~~~~~ -=M]m                                                        Lo&7z0 Layer #6!? ~ "     %$#z0jvz0*:JZjz *:JZjz *:JZjz *Jj *Jj *:JZtttttttttttttttttttttttttttttttttttttttttt t    t    t    t    t    t    t    t    t    t    t    t    t    t                                                           Lo&7z0 Background!? "     %$#z0iuz0!-9EQ]iu)5AMYeq} %1=IUamy !-9EQ]tttttttttttttttttttttttttttttttttttttttttttttttttttttttt  t   t   t   t   t   t   t   t   t   t   t   t   t   t Lo&7isort-9.0.1/art/stylesheets/extra.css0000644000000000000000000000023613615410400014601 0ustar00[data-md-color-scheme="isort"] { --md-primary-fg-color: #EF8336; --md-primary-fg-color--light: #1674B1; --md-primary-fg-color--dark: #1674b1; } isort-9.0.1/docs/changelog.rst0000644000000000000000000000013213615410400013206 0ustar00Changelog ========= .. include:: generated/CHANGELOG.md :parser: myst_parser.sphinx_ isort-9.0.1/docs/conf.py0000644000000000000000000000337013615410400012033 0ustar00import os import re import sys from pathlib import Path from sphinx.application import Sphinx sys.path.insert(0, os.path.abspath("..")) project = "isort" copyright = "2013-2026, Timothy Crosley" author = "Timothy Crosley" extensions = [ "myst_parser", "sphinx_immaterial", "sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", "sphinx.ext.viewcode", ] html_theme = "sphinx_immaterial" html_title = "isort" html_static_path = ["../art"] html_logo = "../art/logo.png" html_favicon = "../art/logo.png" myst_heading_anchors = 2 intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} # To avoid a lot of errors about problems generating autodoc for the defaults, we ignore them. autodoc_preserve_defaults = True autodoc_default_options = {"members": True, "show-inheritance": True, "undoc-members": True} html_theme_options = { "site_url": "https://isort.readthedocs.io/", "repo_url": "https://github.com/PyCQA/isort/", "repo_name": "isort", "palette": {"primary": "deep-orange", "accent": "deep-orange"}, } def _generate_includes(app: Sphinx) -> None: """Create Sphinx-friendly copies of some files at build time.""" # Use absolute paths based on this file's location root_dir = Path(__file__).parent.absolute().parent gen_dir = Path(__file__).parent.absolute() / "generated" gen_dir.mkdir(parents=True, exist_ok=True) for file in ("CHANGELOG.md", "README.md"): content = (root_dir / file).read_text(encoding="utf-8") content = re.sub(r"\]\(docs/(.*?)\.md\)", r"](../\1)", content) (gen_dir / file).write_text(content, encoding="utf-8") def setup(app: Sphinx) -> None: """Register build hooks.""" app.connect("builder-inited", _generate_includes) isort-9.0.1/docs/index.rst0000644000000000000000000000331413615410400012373 0ustar00Home ===== .. include:: generated/README.md :parser: myst_parser.sphinx_ .. toctree:: :maxdepth: 1 :hidden: self changelog .. toctree:: :maxdepth: 2 :caption: Quick start :hidden: 0. Try 1. Install 2. CLI 3. API .. toctree:: :maxdepth: 2 :caption: Configuration :hidden: configuration/action_comments configuration/add_or_remove_imports configuration/black_compatibility Config files configuration/custom_sections_and_ordering configuration/git_hook configuration/github_action configuration/multi_line_output_modes Options Pre-commit Profiles .. toctree:: :maxdepth: 2 :caption: How to guides :hidden: howto/shared_profiles .. toctree:: :maxdepth: 2 :caption: Major releases :hidden: major_releases/introducing_isort_5 Release policy .. toctree:: :maxdepth: 2 :caption: Upgrade guides :hidden: 5.0.0 .. toctree:: :maxdepth: 2 :caption: Warning and Error codes :hidden: W0500 .. toctree:: :maxdepth: 2 :caption: Contributing :hidden: 1. Contributing guide 2. Coding standard 3. Code of conduct 4. Acknowledgements .. toctree:: :maxdepth: 1 :caption: Reference :hidden: reference/isort isort-9.0.1/docs/configuration/action_comments.md0000644000000000000000000000477013615410400017114 0ustar00# Action comments 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. ## isort: skip_file Tells isort to skip the entire file. Example: ```python # !/bin/python3 # isort: skip_file import os import sys ... ``` !!! warning This should be placed as high in the file as reasonably possible. 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. ## isort: skip If placed on the same line as (or within the continuation of a) an import statement, isort will not sort this import. 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. Example: ```python import b import a # isort: skip <- this will now stay below b ``` !!! note It is recommended to where possible use `# isort: off` and `# isort: on` or `# isort: split` instead as the behavior is more explicit and predictable. ## isort: off 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. Example: ```python import e import f # isort: off import b import a ``` ## isort: on 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. Example: ```python import e import f # isort: off import b import a # isort: on import c import d ``` ## isort: split Tells isort the current sort section is finished, and all future imports belong to a new sort grouping. Example: ```python import e import f # isort: split import a import b import c import d ``` You can also use it inline to keep an import from having imports above or below it swap position: ```python import c import b # isort: split import a ``` !!! tip isort split is exactly the same as placing an `# isort: on` immediately below an `# isort: off` ## isort: dont-add-imports Tells isort to not automatically add imports to this file, even if --add-imports is set. ## isort: dont-add-import: [IMPORT_LINE] Tells isort to not automatically add a particular import, even if --add-imports says to add it. isort-9.0.1/docs/configuration/add_or_remove_imports.md0000644000000000000000000000122413615410400020303 0ustar00# Add or remove imports ## Adding an import to multiple files isort makes it easy to add an import statement across multiple files, while being assured it's correctly placed. To add an import to all files: ```bash isort -a "from __future__ import print_function" *.py ``` To add an import only to files that already have imports: ```bash isort -a "from __future__ import print_function" --append-only *.py ``` ## Removing an import from multiple files isort also makes it easy to remove an import from multiple files, without having to be concerned with how it was originally formatted. From the command line: ```bash isort --rm "os.system" *.py ``` isort-9.0.1/docs/configuration/black_compatibility.md0000644000000000000000000000361613615410400017735 0ustar00![isort loves black](https://raw.githubusercontent.com/pycqa/isort/main/art/isort_loves_black.png) # Compatibility with black Compatibility with black is very important to the isort project and comes baked in starting with version 5. All that's required to use isort alongside black is to set the isort profile to "black". !!! tip Beyond the profile, it is common to set [skip_gitignore](./options.md#skip-gitignore) (which is not enabled by default for isort as it requires git to be installed) and [line_length](./options.md#line-length) as it is common to deviate from black's default of 88. ## Using a config file (such as .isort.cfg) 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. That way it's independent of how users call isort (pre-commit, CLI, or editor integration) the black profile will automatically be applied. For instance, your _pyproject.toml_ file would look something like ```ini [tool.isort] profile = "black" ``` Read More about supported [config files](./config_files.md). ## CLI To use the profile option when calling isort directly from the commandline simply add the --profile black argument: `isort --profile black`. A demo of how this would look like in your _.travis.yml_ ```yaml language: python python: - "3.10" install: - pip install -r requirements-dev.txt - pip install isort black - pip install coveralls script: - pytest my-package - isort --profile black my-package - black --check --diff my-package after_success: - coveralls ``` See [built-in profiles](./profiles.md) for more profiles. ## Integration with pre-commit You can also set the profile directly when integrating isort within pre-commit. ```yaml - repo: https://github.com/pycqa/isort rev: 6.0.1 hooks: - id: isort args: ["--profile", "black", "--filter-files"] ``` isort-9.0.1/docs/configuration/config_files.md0000644000000000000000000001234113615410400016352 0ustar00Supported config files ======== isort supports various standard config formats to allow customizations to be integrated into any project quickly. When applying configurations, isort looks for the closest supported config file, in the order files are listed below. You can manually specify the settings file or path by setting `--settings-path` from the command-line. Otherwise, isort will traverse up to 25 parent directories until it finds a suitable config file. Note that isort will not leave a git or Mercurial repository (checking for a `.git` or `.hg` directory). As soon as it finds a file, it stops looking. The config file search is done relative to the current directory if `isort .` or a file stream is passed in, or relative to the first path passed in if multiple paths are passed in. isort **never** merges config files together due to the confusion it can cause. !!! tip You can always introspect the configuration settings isort determined, and find out which config file it picked up, by running `isort . --show-config` ## .isort.cfg **[preferred format]** The first place isort will look for settings is in dedicated .isort.cfg files. The advantage of using this kind of config file, is that it is explicitly for isort and follows a well understood format. The downside, is that it means one more config file in your project when you may already have several polluting your file hierarchy. An example a config from the isort project itself: ```ini [settings] profile=hug src_paths=isort,test ``` ## pyproject.toml **[preferred format]** The second place isort will look, and an equally excellent choice to place your configuration, is within a pyproject.toml file. The advantage of using this config file, is that it is quickly becoming a standard place to configure all Python tools. This means other developers will know to look here and you will keep your projects root nice and tidy. The only disadvantage is that other tools you use might not yet support this format, negating the cleanliness. ```toml [tool.isort] profile = "hug" src_paths = ["isort", "test"] ``` ## setup.cfg `setup.cfg` can be thought of as the precursor to `pyproject.toml`. While isort and newer tools are increasingly moving to pyproject.toml, if you rely on many tools that use this standard it can be a natural fit to put your isort config there as well. ```ini [isort] profile=hug src_paths=isort,test ``` ## tox.ini [tox](https://tox.readthedocs.io/en/latest/) is a tool commonly used in the Python community to specify multiple testing environments. Because isort verification is commonly ran as a testing step, some prefer to place the isort config inside of the tox.ini file. ```ini [isort] profile = black multi_line_output = 3 ``` ## .editorconfig Finally, isort will look for a `.editorconfig` configuration with settings for Python source files. [EditorConfig](https://editorconfig.org/) is a project to enable specifying a configuration for text editing behaviour once, allowing multiple command line tools and text editors to pick it up. Since isort cares about a lot of the same settings as a text-editor (like line-length) it makes sense for it to look within these files as well. ``` root = true [*.py] profile = hug indent_style = space indent_size = 4 skip = build,.tox,venv src_paths=isort,test ``` ## Custom config files Optionally, you can also create a config file with a custom name, or directly point isort to a config file that falls lower in the priority order, by using [--settings-file](./options.md#settings-path). This can be useful, for instance, if you want to have one configuration for `.py` files and another for `.pyx` - while keeping the config files at the root of your repository. !!! tip Custom config files should place their configuration options inside an `[isort]` section and never a generic `[settings]` section. This is because isort can't know for sure how other tools are utilizing the config file. ## Supporting multiple config files in single isort run If you have a directory structure where different sub-directories may have their separate configuration settings and you want isort to respect these configurations, not just apply the same global configuration for the entire directory then you can do so with the `--resolve-all-configs` flag. Using the `--resolve-all-configs` along with providing the directory root as `--config-root` argument(if the config-root is not explicitly defined, then isort will consider the current directory `.` where the shell is running), isort will traverse and parse all the config files defined under the `--config-root` and dynamically decide what configurations should be applied to a specific file by choosing the nearest config file in the file's path. For instance, if your directory structure is ``` directory_root subdir1 .isort.cfg file1.py subdir2 pyproject.toml file2.py subdir3 file3.py setup.cfg ``` isort will sort `subdir1/file1` according to the configurations defined in `subdir1/.isort.cfg`, `subdir2/file2` with configurations from `subdir2/pyproject.toml` and `subdir3/file3.py` based on the `setup.cfg` settings. !!! tip You can always confirm exactly what config file was used for a file by running isort with the `--verbose` flag. isort-9.0.1/docs/configuration/custom_sections_and_ordering.md0000644000000000000000000000705213615410400021662 0ustar00# Custom sections and ordering isort provides lots of features to enable configuring how it sections imports and how it sorts imports within those sections. You can change the section order with `sections` option from the default of: ```ini FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER ``` to your preference (if defined, omitting a default section may cause errors): ```ini sections=FUTURE,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER ``` You also can define your own sections and their order. Example: ```ini known_django=django known_pandas=pandas,numpy sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,PANDAS,FIRSTPARTY,LOCALFOLDER ``` would create two new sections with the specified known modules. The `no_lines_before` option will prevent the listed sections from being split from the previous section by an empty line. Example: ```ini sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER no_lines_before=LOCALFOLDER ``` would produce a section with both FIRSTPARTY and LOCALFOLDER modules combined. **IMPORTANT NOTE**: It is very important to know when setting `known` sections that the naming does not directly map for historical reasons. For custom settings, the only difference is capitalization (`known_custom=custom` VS `sections=CUSTOM,...`) for all others reference the following mapping: - `known_standard_library` : `STANDARD_LIBRARY` - `extra_standard_library` : `STANDARD_LIBRARY` # Like known standard library but appends instead of replacing - `known_future_library` : `FUTURE` - `known_first_party`: `FIRSTPARTY` - `known_third_party`: `THIRDPARTY` - `known_local_folder`: `LOCALFOLDER` This will likely be changed in isort 6.0.0+ in a backwards compatible way. ## Auto-comment import sections Some projects prefer to have import sections uniquely titled to aid in identifying the sections quickly when visually scanning. isort can automate this as well. To do this simply set the `import_heading_{section_name}` setting for each section you wish to have auto commented - to the desired comment. For Example: ```ini import_heading_stdlib=Standard Library import_heading_firstparty=My Stuff ``` Would lead to output looking like the following: ```python # Standard Library import os import sys import django.settings # My Stuff import myproject.test ``` ## Ordering by import length isort also makes it easy to sort your imports by length, simply by setting the `length_sort` option to `True`. This will result in the following output style: ```python from evn.util import ( Pool, Dict, Options, Constant, DecayDict, UnexpectedCodePath, ) ``` It is also possible to opt-in to sorting imports by length for only specific sections by using `length_sort_` followed by the section name as a configuration item, e.g.: length_sort_stdlib=1 ## Controlling how isort sections `from` imports By default isort places straight (`import y`) imports above from imports (`from x import y`): ```python import b from a import a # This will always appear below because it is a from import. ``` However, if you prefer to keep strict alphabetical sorting you can set [force sort within sections](./options.md#force-sort-within-sections) to true. Resulting in: ```python from a import a # This will now appear at top because a appears in the alphabet before b import b ``` You can even tell isort to always place from imports on top, instead of the default of placing them on bottom, using [from first](./options.md#from-first). ```python from b import b # If from first is set to True, all from imports will be placed before non-from imports. import a ``` isort-9.0.1/docs/configuration/git_hook.md0000644000000000000000000000303213615410400015523 0ustar00# Git hook isort provides a hook function that can be integrated into your Git pre-commit script to check Python code before committing. To cause the commit to fail if there are isort errors (strict mode), include the following in `.git/hooks/pre-commit`: ```python #!/usr/bin/env python import sys from isort.hooks import git_hook sys.exit(git_hook(strict=True, modify=True, lazy=True, settings_file="")) ``` If you just want to display warnings, but allow the commit to happen anyway, call `git_hook` without the strict parameter. If you want to display warnings, but not also fix the code, call `git_hook` without the modify parameter. The `lazy` argument is to support users who are "lazy" to add files individually to the index and tend to use `git commit -a` instead. Set it to `True` to ensure all tracked files are properly isorted, leave it out or set it to `False` to check only files added to your index. If you want to use a specific configuration file for the hook, you can pass its path to settings_file. If no path is specifically requested, `git_hook` will search for the configuration file starting at the directory containing the first staged file, as per `git diff-index` ordering, and going upward in the directory structure until a valid configuration file is found or `MAX_CONFIG_SEARCH_DEPTH` directories are checked. Check the source code for the current value of this variable. The settings_file parameter is used to support users who keep their configuration file in a directory that might not be a parent of all the other files. isort-9.0.1/docs/configuration/github_action.md0000644000000000000000000000374413615410400016551 0ustar00# Github action 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. The action can be found on the [Github Actions Marketplace][python-isort]. ## Usage The `python-isort` plugin is designed to be run in combination with the [`checkout`][checkout-action] and [`setup-python`][setup-python] actions. 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. ### Inputs #### `isort-version` Optional. Version of `isort` to use. Defaults to latest version of `isort`. #### `sort-paths` Optional. List of paths to sort, relative to your project root. Defaults to `.` #### `configuration` Optional. `isort` configuration options to pass to the `isort` CLI. Defaults to `--check-only --diff`. #### `requirements-files` Optional. Paths to python requirements files to install before running isort. If multiple requirements files are provided, they should be separated by a space. If custom package installation is required, dependencies should be installed in a separate step before using this action. !!! tip It is important that the project's dependencies be installed before running isort so that third-party libraries are properly sorted. ### Outputs #### `isort-result` Output of the `isort` CLI. ### Example usage ```yaml name: Run isort on: - push jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: 3.13 - uses: isort/isort-action@v1 with: requirementsFiles: "requirements.txt requirements-test.txt" ``` [github-action-docs]: https://docs.github.com/en/free-pro-team@latest/actions [python-isort]: https://github.com/marketplace/actions/python-isort [checkout-action]: https://github.com/actions/checkout [setup-python]: https://github.com/actions/setup-python isort-9.0.1/docs/configuration/multi_line_output_modes.md0000644000000000000000000000430713615410400020676 0ustar00# Multi line output modes This [config option](./options.md#multi-line-output) defines how from imports wrap when they extend past the line\_length limit and has 12 possible settings: ## 0 - Grid ```python from third_party import (lib1, lib2, lib3, lib4, lib5, ...) ``` ## 1 - Vertical ```python from third_party import (lib1, lib2, lib3 lib4, lib5, ...) ``` ## 2 - Hanging Indent ```python from third_party import \ lib1, lib2, lib3, \ lib4, lib5, lib6 ``` ## 3 - Vertical Hanging Indent ```python from third_party import ( lib1, lib2, lib3, lib4 ) ``` ## 4 - Hanging Grid ```python from third_party import ( lib1, lib2, lib3, lib4, lib5, ...) ``` ## 5 - Hanging Grid Grouped ```python from third_party import ( lib1, lib2, lib3, lib4, lib5, ... ) ``` ## 6 - Hanging Grid Grouped Same as Mode 5. Deprecated. ## 7 - NOQA ```python from third_party import lib1, lib2, lib3, ... # NOQA ``` Alternatively, you can set `force_single_line` to `True` (`-sl` on the command line) and every import will appear on its own line: ```python from third_party import lib1 from third_party import lib2 from third_party import lib3 ... ``` ## 8 - Vertical Hanging Indent Bracket Same as Mode 3 - _Vertical Hanging Indent_ but the closing parentheses on the last line is indented. ```python from third_party import ( lib1, lib2, lib3, lib4, ) ``` ## 9 - Vertical Prefix From Module Import Starts a new line with the same `from MODULE import` prefix when lines are longer than the line length limit. ```python from third_party import lib1, lib2, lib3 from third_party import lib4, lib5, lib6 ``` ## 10 - Hanging Indent With Parentheses Same as Mode 2 - _Hanging Indent_ but uses parentheses instead of backslash for wrapping long lines. ```python from third_party import ( lib1, lib2, lib3, lib4, lib5, lib6) ``` ## 11 - Backslash Grid Same as Mode 0 - _Grid_ but uses backslashes instead of parentheses to group imports. ```python from third_party import lib1, lib2, lib3, \ lib4, lib5 ``` isort-9.0.1/docs/configuration/options.md0000644000000000000000000013167513615410400015432 0ustar00# Configuration options for isort As a code formatter isort has opinions. However, it also allows you to have your own. If your opinions disagree with those of isort, isort will disagree but commit to your way of formatting. To enable this, isort exposes a plethora of options to specify how you want your imports sorted, organized, and formatted. Too busy to build your perfect isort configuration? For curated common configurations, see isort's [built-in profiles](./profiles.md). ## Python Version Tells isort to set the known standard library based on the specified Python version. Default is to assume any Python 3 version could be the target, and use a union of all stdlib modules across versions. If auto is specified, the version of the interpreter used to run isort (currently: 39) will be used. **Type:** String **Default:** `py3` **Config default:** `3` **Python & Config File Name:** py_version **CLI Flags:** - --py - --python-version **Examples:** ### Example `.isort.cfg` ``` [settings] py_version=39 ``` ### Example `pyproject.toml` ``` [tool.isort] py_version=39 ``` ### Example cli usage `isort --py 39` ## Force To Top Force specific imports to the top of their appropriate section. **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** force_to_top **CLI Flags:** - -t - --top ## Skip Files that isort should skip over. If you want to skip multiple files you should specify twice: `--skip file1 --skip file2`. Values can be file names, directory names or file paths. To skip all files in a nested path, use [`--skip-glob`](#skip-glob). To even skip matching files that have been specified on the command line, use [`--filter-files`](#filter-files). **Type:** List of Strings **Default:** `('.bzr', '.direnv', '.eggs', '.git', '.hg', '.mypy_cache', '.nox', '.pants.d', '.pytype' '.svn', '.tox', '.venv', '__pypackages__', '_build', 'buck-out', 'build', 'dist', 'node_modules', 'venv')` **Config default:** `['.bzr', '.direnv', '.eggs', '.git', '.hg', '.mypy_cache', '.nox', '.pants.d', '.svn', '.tox', '.venv', '__pypackages__', '_build', 'buck-out', 'build', 'dist', 'node_modules', 'venv']` **Python & Config File Name:** skip **CLI Flags:** - -s - --skip **Examples:** ### Example `.isort.cfg` ``` [settings] skip=.gitignore,.dockerignore ``` ### Example `pyproject.toml` ``` [tool.isort] skip = [".gitignore", ".dockerignore"] ``` ## Extend Skip Extends --skip to add additional files that isort should skip over. If you want to skip multiple files you should specify twice: --skip file1 --skip file2. Values can be file names, directory names or file paths. To skip all files in a nested path, use [`--skip-glob`](#skip-glob). To even skip matching files that have been specified on the command line, use [`--filter-files`](#filter-files). **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** extend_skip **CLI Flags:** - --extend-skip **Examples:** ### Example `.isort.cfg` ``` [settings] extend_skip=.md,.json ``` ### Example `pyproject.toml` ``` [tool.isort] extend_skip = [".md", ".json"] ``` ## Skip Glob Files that isort should skip over. To even skip matching files that have been specified on the command line, use [`--filter-files`](#filter-files). **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** skip_glob **CLI Flags:** - --sg - --skip-glob **Examples:** ### Example `.isort.cfg` ``` [settings] skip_glob=docs/* ``` ### Example `pyproject.toml` ``` [tool.isort] skip_glob = ["docs/*"] ``` ## Extend Skip Glob Additional files that isort should skip over (extending --skip-glob). To even skip matching files that have been specified on the command line, use [`--filter-files`](#filter-files). **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** extend_skip_glob **CLI Flags:** - --extend-skip-glob **Examples:** ### Example `.isort.cfg` ``` [settings] extend_skip_glob=my_*_module.py,test/* ``` ### Example `pyproject.toml` ``` [tool.isort] extend_skip_glob = ["my_*_module.py", "test/*"] ``` ## Skip Gitignore Treat project as a git repository and ignore files listed in .gitignore. To even skip matching files that have been specified on the command line, use [`--filter-files`](#filter-files). NOTE: This requires git to be installed and accessible from the same shell as isort. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** skip_gitignore **CLI Flags:** - --gitignore - --skip-gitignore ## Line Length The max length of an import line (used for wrapping long imports). **Type:** Int **Default:** `79` **Config default:** `79` **Python & Config File Name:** line_length **CLI Flags:** - -l - -w - --line-length - --line-width ## Wrap Length Specifies how long lines that are wrapped should be, if not set line_length is used. NOTE: wrap_length must be LOWER than or equal to line_length. **Type:** Int **Default:** `0` **Config default:** `0` **Python & Config File Name:** wrap_length **CLI Flags:** - --wl - --wrap-length ## Line Ending Forces line endings to the specified value. If not set, values will be guessed per-file. **Type:** String **Default:** ` ` **Config default:** ` ` **Python & Config File Name:** line_ending **CLI Flags:** - --le - --line-ending ## Sort Re-exports Specifies whether to sort re-exports (`__all__` collections) automatically. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** sort_reexports **CLI Flags:** - --srx - --sort-reexports ## Sections What sections isort should display imports for and in what order **Type:** List of Strings **Default:** `('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER')` **Config default:** `['FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER']` **Python & Config File Name:** sections **CLI Flags:** **Not Supported** ## No Sections Put all imports into the same section bucket **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** no_sections **CLI Flags:** - --ds - --no-sections ## Known Future Library Force isort to recognize a module as part of Python's internal future compatibility libraries. WARNING: this overrides the behavior of __future__ handling and therefore can result in code that can't execute. If you're looking to add dependencies such as six, a better option is to create another section below --future using custom sections. See: https://github.com/PyCQA/isort#custom-sections-and-ordering and the discussion here: https://github.com/PyCQA/isort/issues/1463. **Type:** List of Strings **Default:** `('__future__',)` **Config default:** `['__future__']` **Python & Config File Name:** known_future_library **CLI Flags:** - -f - --future ## Known Third Party Force isort to recognize a module as being part of a third party library. **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** known_third_party **CLI Flags:** - -o - --thirdparty **Examples:** ### Example `.isort.cfg` ``` [settings] known_third_party=my_module1,my_module2 ``` ### Example `pyproject.toml` ``` [tool.isort] known_third_party = ["my_module1", "my_module2"] ``` ## Known First Party Force isort to recognize a module as being part of the current python project. **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** known_first_party **CLI Flags:** - -p - --project **Examples:** ### Example `.isort.cfg` ``` [settings] known_first_party=my_module1,my_module2 ``` ### Example `pyproject.toml` ``` [tool.isort] known_first_party = ["my_module1", "my_module2"] ``` ## Known Local Folder Force isort to recognize a module as being a local folder. Generally, this is reserved for relative imports (from . import module). **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** known_local_folder **CLI Flags:** - --known-local-folder **Examples:** ### Example `.isort.cfg` ``` [settings] known_local_folder=my_module1,my_module2 ``` ### Example `pyproject.toml` ``` [tool.isort] known_local_folder = ["my_module1", "my_module2"] ``` ## Known Standard Library Force isort to recognize a module as part of Python's standard library. **Type:** List of Strings **Default:** `('_ast', '_dummy_thread', '_thread', 'abc', 'aifc', 'argparse', 'array', 'ast', 'asynchat', 'asyncio', 'asyncore', 'atexit', 'audioop', 'base64', 'bdb', 'binascii', 'binhex', 'bisect', 'builtins', 'bz2', 'cProfile', 'calendar', 'cgi', 'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs', 'codeop', 'collections', 'colorsys', 'compileall', 'concurrent', 'configparser', 'contextlib', 'contextvars', 'copy', 'copyreg', 'crypt', 'csv', 'ctypes', 'curses', 'dataclasses', 'datetime', 'dbm', 'decimal', 'difflib', 'dis', 'distutils', 'doctest', 'dummy_threading', 'email', 'encodings', 'ensurepip', 'enum', 'errno', 'faulthandler', 'fcntl', 'filecmp', 'fileinput', 'fnmatch', 'formatter', 'fpectl', 'fractions', 'ftplib', 'functools', 'gc', 'getopt', 'getpass', 'gettext', 'glob', 'graphlib', 'grp', 'gzip', 'hashlib', 'heapq', 'hmac', 'html', 'http', 'imaplib', 'imghdr', 'imp', 'importlib', 'inspect', 'io', 'ipaddress', 'itertools', 'json', 'keyword', 'lib2to3', 'linecache', 'locale', 'logging', 'lzma', 'macpath', 'mailbox', 'mailcap', 'marshal', 'math', 'mimetypes', 'mmap', 'modulefinder', 'msilib', 'msvcrt', 'multiprocessing', 'netrc', 'nis', 'nntplib', 'ntpath', 'numbers', 'operator', 'optparse', 'os', 'ossaudiodev', 'parser', 'pathlib', 'pdb', 'pickle', 'pickletools', 'pipes', 'pkgutil', 'platform', 'plistlib', 'poplib', 'posix', 'posixpath', 'pprint', 'profile', 'pstats', 'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc', 'queue', 'quopri', 'random', 're', 'readline', 'reprlib', 'resource', 'rlcompleter', 'runpy', 'sched', 'secrets', 'select', 'selectors', 'shelve', 'shlex', 'shutil', 'signal', 'site', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver', 'spwd', 'sqlite3', 'sre', 'sre_compile', 'sre_constants', 'sre_parse', 'ssl', 'stat', 'statistics', 'string', 'stringprep', 'struct', 'subprocess', 'sunau', 'symbol', 'symtable', 'sys', 'sysconfig', 'syslog', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios', 'test', 'textwrap', 'threading', 'time', 'timeit', 'tkinter', 'token', 'tokenize', 'trace', 'traceback', 'tracemalloc', 'tty', 'turtle', 'turtledemo', 'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'uu', 'uuid', 'venv', 'warnings', 'wave', 'weakref', 'webbrowser', 'winreg', 'winsound', 'wsgiref', 'xdrlib', 'xml', 'xmlrpc', 'zipapp', 'zipfile', 'zipimport', 'zlib', 'zoneinfo')` **Config default:** `['_ast', '_dummy_thread', '_thread', 'abc', 'aifc', 'argparse', 'array', 'ast', 'asynchat', 'asyncio', 'asyncore', 'atexit', 'audioop', 'base64', 'bdb', 'binascii', 'binhex', 'bisect', 'builtins', 'bz2', 'cProfile', 'calendar', 'cgi', 'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs', 'codeop', 'collections', 'colorsys', 'compileall', 'concurrent', 'configparser', 'contextlib', 'contextvars', 'copy', 'copyreg', 'crypt', 'csv', 'ctypes', 'curses', 'dataclasses', 'datetime', 'dbm', 'decimal', 'difflib', 'dis', 'distutils', 'doctest', 'dummy_threading', 'email', 'encodings', 'ensurepip', 'enum', 'errno', 'faulthandler', 'fcntl', 'filecmp', 'fileinput', 'fnmatch', 'formatter', 'fpectl', 'fractions', 'ftplib', 'functools', 'gc', 'getopt', 'getpass', 'gettext', 'glob', 'graphlib', 'grp', 'gzip', 'hashlib', 'heapq', 'hmac', 'html', 'http', 'imaplib', 'imghdr', 'imp', 'importlib', 'inspect', 'io', 'ipaddress', 'itertools', 'json', 'keyword', 'lib2to3', 'linecache', 'locale', 'logging', 'lzma', 'macpath', 'mailbox', 'mailcap', 'marshal', 'math', 'mimetypes', 'mmap', 'modulefinder', 'msilib', 'msvcrt', 'multiprocessing', 'netrc', 'nis', 'nntplib', 'ntpath', 'numbers', 'operator', 'optparse', 'os', 'ossaudiodev', 'parser', 'pathlib', 'pdb', 'pickle', 'pickletools', 'pipes', 'pkgutil', 'platform', 'plistlib', 'poplib', 'posix', 'posixpath', 'pprint', 'profile', 'pstats', 'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc', 'queue', 'quopri', 'random', 're', 'readline', 'reprlib', 'resource', 'rlcompleter', 'runpy', 'sched', 'secrets', 'select', 'selectors', 'shelve', 'shlex', 'shutil', 'signal', 'site', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver', 'spwd', 'sqlite3', 'sre', 'sre_compile', 'sre_constants', 'sre_parse', 'ssl', 'stat', 'statistics', 'string', 'stringprep', 'struct', 'subprocess', 'sunau', 'symbol', 'symtable', 'sys', 'sysconfig', 'syslog', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios', 'test', 'textwrap', 'threading', 'time', 'timeit', 'tkinter', 'token', 'tokenize', 'trace', 'traceback', 'tracemalloc', 'tty', 'turtle', 'turtledemo', 'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'uu', 'uuid', 'venv', 'warnings', 'wave', 'weakref', 'webbrowser', 'winreg', 'winsound', 'wsgiref', 'xdrlib', 'xml', 'xmlrpc', 'zipapp', 'zipfile', 'zipimport', 'zlib', 'zoneinfo']` **Python & Config File Name:** known_standard_library **CLI Flags:** - -b - --builtin **Examples:** ### Example `.isort.cfg` ``` [settings] known_standard_library=my_module1,my_module2 ``` ### Example `pyproject.toml` ``` [tool.isort] known_standard_library = ["my_module1", "my_module2"] ``` ## Extra Standard Library Extra modules to be included in the list of ones in Python's standard library. **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** extra_standard_library **CLI Flags:** - --extra-builtin **Examples:** ### Example `.isort.cfg` ``` [settings] extra_standard_library=my_module1,my_module2 ``` ### Example `pyproject.toml` ``` [tool.isort] extra_standard_library = ["my_module1", "my_module2"] ``` ## Known Other known_OTHER is how imports of custom sections are defined. OTHER is a placeholder for the custom section name. **Type:** Dict **Default:** `{}` **Config default:** `{}` **Python & Config File Name:** known_other **CLI Flags:** **Not Supported** **Examples:** ### Example `.isort.cfg` ``` [settings] sections=FUTURE,STDLIB,THIRDPARTY,AIRFLOW,FIRSTPARTY,LOCALFOLDER known_airflow=airflow ``` ### Example `pyproject.toml` ``` [tool.isort] sections = ['FUTURE', 'STDLIB', 'THIRDPARTY', 'AIRFLOW', 'FIRSTPARTY', 'LOCALFOLDER'] known_airflow = ['airflow'] ``` ## Multi Line Output Multi line output (0-grid, 1-vertical, 2-hanging, 3-vert-hanging, 4-vert-grid, 5-vert-grid-grouped, 6-deprecated-alias-for-5, 7-noqa, 8-vertical-hanging-indent-bracket, 9-vertical-prefix-from-module-import, 10-hanging-indent-with-parentheses). **Type:** Wrapmodes **Default:** `WrapModes.GRID` **Config default:** `WrapModes.GRID` **Python & Config File Name:** multi_line_output **CLI Flags:** - -m - --multi-line **Examples:** ### Example `.isort.cfg` ``` [settings] multi_line_output=3 ``` ### Example `pyproject.toml` ``` [tool.isort] multi_line_output = 3 ``` ## Forced Separate Force certain sub modules to show separately **Type:** List of Strings **Default:** `()` **Config default:** `[]` **Python & Config File Name:** forced_separate **CLI Flags:** - --fs - --forced-separate **Examples:** ### Example `.isort.cfg` ``` [settings] forced_separate=glob_exp1,glob_exp2 ``` ### Example `pyproject.toml` ``` [tool.isort] forced_separate = ["glob_exp1", "glob_exp2"] ``` ## Indent String to place for indents defaults to " " (4 spaces). **Type:** String **Default:** ` ` **Config default:** ` ` **Python & Config File Name:** indent **CLI Flags:** - -i - --indent ## Comment Prefix Allows customizing how isort prefixes comments that it adds or modifies on import linesGenerally ` #` (two spaces before a pound symbol) is use, though one space is also common. **Type:** String **Default:** ` #` **Config default:** ` #` **Python & Config File Name:** comment_prefix **CLI Flags:** **Not Supported** ## Length Sort Sort imports by their string length. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** length_sort **CLI Flags:** - --ls - --length-sort ## Length Sort Straight Sort straight imports by their string length. Similar to `length_sort` but applies only to straight imports and doesn't affect from imports. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** length_sort_straight **CLI Flags:** - --lss - --length-sort-straight ## Length Sort Sections Sort the given sections by length **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** length_sort_sections **CLI Flags:** **Not Supported** **Examples:** ### Example `.isort.cfg` ``` [settings] length_sort_sections=future,stdlib ``` ### Example `pyproject.toml` ``` [tool.isort] length_sort_sections = ["future", "stdlib"] ``` ## Add Imports Adds the specified import line to all files, automatically determining correct placement. **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** add_imports **CLI Flags:** - -a - --add-import **Examples:** ### Example `.isort.cfg` ``` [settings] add_imports=import os,import json ``` ### Example `pyproject.toml` ``` [tool.isort] add_imports = ["import os", "import json"] ``` ## Remove Imports Removes the specified import from all files. **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** remove_imports **CLI Flags:** - --rm - --remove-import **Examples:** ### Example `.isort.cfg` ``` [settings] remove_imports=os,json ``` ### Example `pyproject.toml` ``` [tool.isort] remove_imports = ["os", "json"] ``` ## Append Only Only adds the imports specified in --add-import if the file contains existing imports. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** append_only **CLI Flags:** - --append - --append-only ## Reverse Relative Reverse order of relative imports. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** reverse_relative **CLI Flags:** - --rr - --reverse-relative ## Force Single Line Forces all from imports to appear on their own line **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** force_single_line **CLI Flags:** - --sl - --force-single-line-imports ## Single Line Exclusions One or more modules to exclude from the single line rule. **Type:** List of Strings **Default:** `()` **Config default:** `[]` **Python & Config File Name:** single_line_exclusions **CLI Flags:** - --nsl - --single-line-exclusions **Examples:** ### Example `.isort.cfg` ``` [settings] single_line_exclusions=os,json ``` ### Example `pyproject.toml` ``` [tool.isort] single_line_exclusions = ["os", "json"] ``` ## Default Section Sets the default section for import options: ('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER') **Type:** String **Default:** `THIRDPARTY` **Config default:** `THIRDPARTY` **Python & Config File Name:** default_section **CLI Flags:** - --sd - --section-default ## Import Headings A mapping of import sections to import heading comments that should show above them. **Type:** Dict **Default:** `{}` **Config default:** `{}` **Python & Config File Name:** import_headings **CLI Flags:** **Not Supported** ## Import Footers A mapping of import sections to import footer comments that should show below them. **Type:** Dict **Default:** `{}` **Config default:** `{}` **Python & Config File Name:** import_footers **CLI Flags:** **Not Supported** ## Balanced Wrapping Balances wrapping to produce the most consistent line length possible **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** balanced_wrapping **CLI Flags:** - -e - --balanced ## Use Parentheses Use parentheses for line continuation on length limit instead of backslashes. **NOTE**: This is separate from wrap modes, and only affects how individual lines that are too long get continued, not sections of multiple imports. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** use_parentheses **CLI Flags:** - --up - --use-parentheses ## Order By Type Order imports by type, which is determined by case, in addition to alphabetically. **NOTE**: type here refers to the implied type from the import name capitalization. isort does not do type introspection for the imports. These "types" are simply: CONSTANT_VARIABLE, CamelCaseClass, variable_or_function. If your project follows PEP8 or a related coding standard and has many imports this is a good default, otherwise you likely will want to turn it off. From the CLI the `--dont-order-by-type` option will turn this off. **Type:** Bool **Default:** `True` **Config default:** `true` **Python & Config File Name:** order_by_type **CLI Flags:** - --ot - --order-by-type ## Atomic Ensures the output doesn't save if the resulting file contains syntax errors. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** atomic **CLI Flags:** - --ac - --atomic ## Lines Before Imports The number of blank lines to place before imports. -1 for automatic determination **Type:** Int **Default:** `-1` **Config default:** `-1` **Python & Config File Name:** lines_before_imports **CLI Flags:** - --lbi - --lines-before-imports ## Lines After Imports The number of blank lines to place after imports. -1 for automatic determination **Type:** Int **Default:** `-1` **Config default:** `-1` **Python & Config File Name:** lines_after_imports **CLI Flags:** - --lai - --lines-after-imports ## Lines Between Sections The number of lines to place between sections **Type:** Int **Default:** `1` **Config default:** `1` **Python & Config File Name:** lines_between_sections **CLI Flags:** **Not Supported** ## Lines Between Types The number of lines to place between direct and from imports **Type:** Int **Default:** `0` **Config default:** `0` **Python & Config File Name:** lines_between_types **CLI Flags:** - --lbt - --lines-between-types ## Combine As Imports Combines as imports on the same line. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** combine_as_imports **CLI Flags:** - --ca - --combine-as ## Combine Star Ensures that if a star import is present, nothing else is imported from that namespace. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** combine_star **CLI Flags:** - --cs - --combine-star ## Include Trailing Comma Includes a trailing comma on multi line imports that include parentheses. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** include_trailing_comma **CLI Flags:** - --tc - --trailing-comma ## Split on Trailing Comma Split imports list followed by a trailing comma into VERTICAL_HANGING_INDENT mode. This follows Black style magic comma. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** split_on_trailing_comma **CLI Flags:** - --split-on-trailing-comma ## From First Switches the typical ordering preference, showing from imports first then straight ones. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** from_first **CLI Flags:** - --ff - --from-first ## Verbose Shows verbose output, such as when files are skipped or when a check is successful. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** verbose **CLI Flags:** - -v - --verbose ## Quiet Shows extra quiet output, only errors are outputted. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** quiet **CLI Flags:** - -q - --quiet ## Force Adds Forces import adds even if the original file is empty. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** force_adds **CLI Flags:** - --af - --force-adds ## Force Alphabetical Sort Within Sections Force all imports to be sorted alphabetically within a section **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** force_alphabetical_sort_within_sections **CLI Flags:** - --fass - --force-alphabetical-sort-within-sections ## Force Alphabetical Sort Force all imports to be sorted as a single section **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** force_alphabetical_sort **CLI Flags:** - --fas - --force-alphabetical-sort ## Force Grid Wrap Force number of from imports (defaults to 2 when passed as CLI flag without value) to be grid wrapped regardless of line length. If 0 is passed in (the global default) only line length is considered. **Type:** Int **Default:** `0` **Config default:** `0` **Python & Config File Name:** force_grid_wrap **CLI Flags:** - --fgw - --force-grid-wrap ## Force Sort Within Sections Don't sort straight-style imports (like import sys) before from-style imports (like from itertools import groupby). Instead, sort the imports by module, independent of import style. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** force_sort_within_sections **CLI Flags:** - --fss - --force-sort-within-sections ## Lexicographical Lexicographical order is strictly alphabetical order. For example by default isort will sort `1, 10, 2` into `1, 2, 10` - but with lexicographical sorting enabled it will remain `1, 10, 2`. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** lexicographical **CLI Flags:** **Not Supported** ## Group By Package If `True` isort will automatically create section groups by the top-level package they come from. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** group_by_package **CLI Flags:** **Not Supported** ## Separate Packages Separate packages within the listed sections with newlines. **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** separate_packages **CLI Flags:** **Not Supported** **Examples:** ### Example `.isort.cfg` ``` [settings] separate_packages=THIRDPARTY ``` ### Example `pyproject.toml` ``` [tool.isort] separate_packages = ["THIRDPARTY"] ``` ### Example before: ```python import os import sys from django.db.models.signals import m2m_changed from django.utils import functional from django_filters import BooleanFilter from junitparser import JUnitXml from loguru import logger ``` ### Example after: ```python import os import sys from django.db.models.signals import m2m_changed from django.utils import functional from django_filters import BooleanFilter from junitparser import JUnitXml from loguru import logger ``` ## Ignore Whitespace Tells isort to ignore whitespace differences when --check-only is being used. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** ignore_whitespace **CLI Flags:** - --ws - --ignore-whitespace ## No Lines Before Sections which should not be split with previous by empty lines **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** no_lines_before **CLI Flags:** - --nlb - --no-lines-before **Examples:** ### Example `.isort.cfg` ``` [settings] no_lines_before=future,stdlib ``` ### Example `pyproject.toml` ``` [tool.isort] no_lines_before = ["future", "stdlib"] ``` ## No Inline Sort Leaves `from` imports with multiple imports 'as-is' (e.g. `from foo import a, c ,b`). **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** no_inline_sort **CLI Flags:** - --nis - --no-inline-sort ## Ignore Comments If enabled, isort will strip comments that exist within import lines. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** ignore_comments **CLI Flags:** **Not Supported** ## Case Sensitive Tells isort to include casing when sorting module names **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** case_sensitive **CLI Flags:** - --case-sensitive ## Virtual Env Virtual environment to use for determining whether a package is third-party **Type:** String **Default:** ` ` **Config default:** ` ` **Python & Config File Name:** virtual_env **CLI Flags:** - --virtual-env ## Conda Env Conda environment to use for determining whether a package is third-party **Type:** String **Default:** ` ` **Config default:** ` ` **Python & Config File Name:** conda_env **CLI Flags:** - --conda-env ## Ensure Newline Before Comments Inserts a blank line before a comment following an import. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** ensure_newline_before_comments **CLI Flags:** - -n - --ensure-newline-before-comments ## Profile Base profile type to use for configuration. Profiles include: black, django, pycharm, google, open\_stack, plone, attrs, hug, wemake, appnexus. As well as any [shared profiles](../howto/shared_profiles.md). **Type:** String **Default:** ` ` **Config default:** ` ` **Python & Config File Name:** profile **CLI Flags:** - --profile ## Honor Noqa Tells isort to honor noqa comments to enforce skipping those comments. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** honor_noqa **CLI Flags:** - --honor-noqa ## Src Paths Add an explicitly defined source path (modules within src paths have their imports automatically categorized as first_party). Glob expansion (`*` and `**`) is supported for this option. **Type:** List of Strings **Default:** `()` **Config default:** `[]` **Python & Config File Name:** src_paths **CLI Flags:** - --src - --src-path **Examples:** ### Example `.isort.cfg` ``` [settings] src_paths = src,tests ``` ### Example `pyproject.toml` ``` [tool.isort] src_paths = ["src", "tests"] ``` ## Remove Redundant Aliases Tells isort to remove redundant aliases from imports, such as `import os as os`. This defaults to `False` simply because some projects use these seemingly useless aliases to signify intent and change behaviour. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** remove_redundant_aliases **CLI Flags:** - --remove-redundant-aliases ## Float To Top Causes all non-indented imports to float to the top of the file having its imports sorted (immediately below the top of file comment). This can be an excellent shortcut for collecting imports every once in a while when you place them in the middle of a file to avoid context switching. *NOTE*: It currently doesn't work with cimports and introduces some extra over-head and a performance penalty. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** float_to_top **CLI Flags:** - --float-to-top ## Filter Files Tells isort to filter files even when they are explicitly passed in as part of the CLI command. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** filter_files **CLI Flags:** - --filter-files ## Formatter Specifies the name of a formatting plugin to use when producing output. **Type:** String **Default:** ` ` **Config default:** ` ` **Python & Config File Name:** formatter **CLI Flags:** - --formatter ## Formatting Function The fully qualified Python path of a function to apply to format code sorted by isort. **Type:** Nonetype **Default:** `None` **Config default:** ` ` **Python & Config File Name:** formatting_function **CLI Flags:** **Not Supported** ## Color Output Tells isort to use color in terminal output. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** color_output **CLI Flags:** - --color ## Treat Comments As Code Tells isort to treat the specified single line comment(s) as if they are code. **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** treat_comments_as_code **CLI Flags:** - --treat-comment-as-code **Examples:** ### Example `.isort.cfg` ``` [settings] treat_comments_as_code = # my comment 1, # my other comment ``` ### Example `pyproject.toml` ``` [tool.isort] treat_comments_as_code = ["# my comment 1", "# my other comment"] ``` ## Treat All Comments As Code Tells isort to treat all single line comments as if they are code. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** treat_all_comments_as_code **CLI Flags:** - --treat-all-comment-as-code ## Supported Extensions Specifies what extensions isort can be run against. **Type:** List of Strings **Default:** `('pxd', 'py', 'pyi', 'pyx')` **Config default:** `['pxd', 'py', 'pyi', 'pyx']` **Python & Config File Name:** supported_extensions **CLI Flags:** - --ext - --extension - --supported-extension **Examples:** ### Example `.isort.cfg` ``` [settings] supported_extensions=pyw,ext ``` ### Example `pyproject.toml` ``` [tool.isort] supported_extensions = ["pyw", "ext"] ``` ## Blocked Extensions Specifies what extensions isort can never be run against. **Type:** List of Strings **Default:** `('pex',)` **Config default:** `['pex']` **Python & Config File Name:** blocked_extensions **CLI Flags:** - --blocked-extension **Examples:** ### Example `.isort.cfg` ``` [settings] blocked_extensions=pyw,pyc ``` ### Example `pyproject.toml` ``` [tool.isort] blocked_extensions = ["pyw", "pyc"] ``` ## Constants An override list of tokens to always recognize as a CONSTANT for order_by_type regardless of casing. **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** constants **CLI Flags:** **Not Supported** ## Classes An override list of tokens to always recognize as a Class for order_by_type regardless of casing. **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** classes **CLI Flags:** **Not Supported** ## Variables An override list of tokens to always recognize as a var for order_by_type regardless of casing. **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** variables **CLI Flags:** **Not Supported** ## Dedup Headings Tells isort to only show an identical custom import heading comment once, even if there are multiple sections with the comment set. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** dedup_headings **CLI Flags:** - --dedup-headings ## Only Sections Causes imports to be sorted based on their sections like STDLIB, THIRDPARTY, etc. Within sections, the imports are ordered by their import style and the imports with the same style maintain their relative positions. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** only_sections **CLI Flags:** - --only-sections - --os ## Only Modified Suppresses verbose output for non-modified files. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** only_modified **CLI Flags:** - --only-modified - --om ## Combine Straight Imports Combines all the bare straight imports of the same section in a single line. Won't work with sections which have 'as' imports **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** combine_straight_imports **CLI Flags:** - --combine-straight-imports - --csi ## Auto Identify Namespace Packages Automatically determine local namespace packages, generally by lack of any src files before a src containing directory. **Type:** Bool **Default:** `True` **Config default:** `true` **Python & Config File Name:** auto_identify_namespace_packages **CLI Flags:** **Not Supported** ## Namespace Packages Manually specify one or more namespace packages. **Type:** List of Strings **Default:** `frozenset()` **Config default:** `[]` **Python & Config File Name:** namespace_packages **CLI Flags:** **Not Supported** ## Follow Links If `True` isort will follow symbolic links when doing recursive sorting. **Type:** Bool **Default:** `True` **Config default:** `true` **Python & Config File Name:** follow_links **CLI Flags:** **Not Supported** ## Indented Import Headings If `True` isort will apply import headings to indented imports the same way it does unindented ones. **Type:** Bool **Default:** `True` **Config default:** `true` **Python & Config File Name:** indented_import_headings **CLI Flags:** **Not Supported** ## Honor Case In Force Sorted Sections Honor `--case-sensitive` when `--force-sort-within-sections` is being used. Without this option set, `--order-by-type` decides module name ordering too. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** honor_case_in_force_sorted_sections **CLI Flags:** - --hcss - --honor-case-in-force-sorted-sections ## Sort Relative In Force Sorted Sections When using `--force-sort-within-sections`, sort relative imports the same way as they are sorted when not using that setting. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** sort_relative_in_force_sorted_sections **CLI Flags:** - --srss - --sort-relative-in-force-sorted-sections ## Overwrite In Place Tells isort to overwrite in place using the same file handle. Comes at a performance and memory usage penalty over its standard approach but ensures all file flags and modes stay unchanged. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** overwrite_in_place **CLI Flags:** - --overwrite-in-place ## Reverse Sort Reverses the ordering of imports. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** reverse_sort **CLI Flags:** - --reverse-sort ## Star First Forces star imports above others to avoid overriding directly imported variables. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** star_first **CLI Flags:** - --star-first ## Format Error Override the format used to print errors. **Type:** String **Default:** `{error}: {message}` **Config default:** `{error}: {message}` **Python & Config File Name:** format_error **CLI Flags:** - --format-error ## Format Success Override the format used to print success. **Type:** String **Default:** `{success}: {message}` **Config default:** `{success}: {message}` **Python & Config File Name:** format_success **CLI Flags:** - --format-success ## Sort Order Specify sorting function. Can be built in (natural[default] = force numbers to be sequential, native = Python's built-in sorted function) or an installable plugin. **Type:** String **Default:** `natural` **Config default:** `natural` **Python & Config File Name:** sort_order **CLI Flags:** - --sort-order ## Show Version Displays the currently installed version of isort. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** **Not Supported** **CLI Flags:** - -V - --version **Examples:** ### Example cli usage `isort --version` ## Version Number Returns just the current version number without the logo **Type:** String **Default:** `==SUPPRESS==` **Config default:** `==SUPPRESS==` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --vn - --version-number ## Write To Stdout Force resulting output to stdout, instead of in-place. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** **Not Supported** **CLI Flags:** - -d - --stdout ## Show Config See isort's determined config, as well as sources of config options. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --show-config ## Show Files See the files isort will be run against with the current config options. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --show-files ## Show Diff Prints a diff of all the changes isort would make to a file, instead of changing it in place **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --df - --diff ## Check Checks the file for unsorted / unformatted imports and prints them to the command line without modifying the file. Returns 0 when nothing would change and returns 1 when the file would be reformatted. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** **Not Supported** **CLI Flags:** - -c - --check-only - --check ## Settings Path Explicitly set the settings path or file instead of auto determining based on file location. **Type:** String **Default:** `None` **Config default:** ` ` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --sp - --settings-path - --settings-file - --settings ## Config Root Explicitly set the config root for resolving all configs. When used with the --resolve-all-configs flag, isort will look at all sub-folders in this config root to resolve config files and sort files based on the closest available config(if any) **Type:** String **Default:** `None` **Config default:** ` ` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --cr - --config-root ## Resolve All Configs Tells isort to resolve the configs for all sub-directories and sort files in terms of its closest config files. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --resolve-all-configs ## Jobs Number of files to process in parallel. Negative value means use number of CPUs. **Type:** Int **Default:** `None` **Config default:** ` ` **Python & Config File Name:** **Not Supported** **CLI Flags:** - -j - --jobs ## Ask To Apply Tells isort to apply changes interactively. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --interactive ## Files One or more Python source files that need their imports sorted. **Type:** String **Default:** `None` **Config default:** ` ` **Python & Config File Name:** **Not Supported** **CLI Flags:** - ## Dont Follow Links Tells isort not to follow symlinks that are encountered when running recursively. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --dont-follow-links ## Filename Provide the filename associated with a stream. **Type:** String **Default:** `None` **Config default:** ` ` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --filename ## Allow Root Tells isort not to treat / specially, allowing it to be run against the root dir. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --allow-root ## Dont Float To Top Forces --float-to-top setting off. See --float-to-top for more information. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --dont-float-to-top ## Dont Order By Type Don't order imports by type, which is determined by case, in addition to alphabetically. **NOTE**: type here refers to the implied type from the import name capitalization. isort does not do type introspection for the imports. These "types" are simply: CONSTANT_VARIABLE, CamelCaseClass, variable_or_function. If your project follows PEP8 or a related coding standard and has many imports this is a good default. You can turn this on from the CLI using `--order-by-type`. **Type:** Bool **Default:** `False` **Config default:** `false` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --dt - --dont-order-by-type ## Ext Format Tells isort to format the given files according to an extensions formatting rules. **Type:** String **Default:** `None` **Config default:** ` ` **Python & Config File Name:** **Not Supported** **CLI Flags:** - --ext-format ## Deprecated Flags ==SUPPRESS== **Type:** String **Default:** `None` **Config default:** ` ` **Python & Config File Name:** **Not Supported** **CLI Flags:** - -k - --keep-direct-and-as isort-9.0.1/docs/configuration/pre-commit.md0000644000000000000000000000261113615410400015776 0ustar00Using isort with pre-commit =========================== isort provides official support for [pre-commit](https://pre-commit.com/). ## isort pre-commit step To use isort's official pre-commit integration add the following config: ```yaml - repo: https://github.com/pycqa/isort rev: 6.1.0 hooks: - id: isort name: isort (python) ``` under the `repos` section of your projects `.pre-commit-config.yaml` file. Optionally if you want to have different hooks over different file types (ex: python vs cython vs pyi) you can do so with the following config: ```yaml - repo: https://github.com/pycqa/isort rev: 6.1.0 hooks: - id: isort name: isort (python) - id: isort name: isort (cython) types: [cython] - id: isort name: isort (pyi) types: [pyi] ``` ## seed-isort-config Older versions of isort used a lot of magic to determine import placement, that could easily break when running on CI/CD. To fix this, a utility called `seed-isort-config` was created. Since isort 5 however, the project has drastically improved its placement logic and ensured a good level of consistency across environments. If you have a step in your pre-commit config called `seed-isort-config` or similar, it is highly recommend that you remove this. It is guaranteed to slow things down, and can conflict with isort's own module placement logic. isort-9.0.1/docs/configuration/profiles.md0000644000000000000000000000456713615410400015561 0ustar00Built-in profiles for isort ======== The following profiles are built into isort to allow easy interoperability with common projects and code styles. To use any of the listed profiles, use `isort --profile PROFILE_NAME` from the command line, or `profile=PROFILE_NAME` in your configuration file. #black - **multi_line_output**: `3` - **include_trailing_comma**: `True` - **split_on_trailing_comma**: `True` - **force_grid_wrap**: `0` - **use_parentheses**: `True` - **ensure_newline_before_comments**: `True` - **line_length**: `88` #django - **combine_as_imports**: `True` - **include_trailing_comma**: `True` - **multi_line_output**: `5` - **line_length**: `79` #pycharm - **multi_line_output**: `3` - **force_grid_wrap**: `2` - **lines_after_imports**: `2` #google - **force_single_line**: `True` - **force_sort_within_sections**: `True` - **lexicographical**: `True` - **line_length**: `1000` - **single_line_exclusions**: `('collections.abc', 'six.moves', 'typing', 'typing_extensions')` - **order_by_type**: `False` - **group_by_package**: `True` #open_stack - **force_single_line**: `True` - **force_sort_within_sections**: `True` - **lexicographical**: `True` #plone Based on black with these tweaks: - **force_alphabetical_sort**: `True` - **force_single_line**: `True` #attrs - **atomic**: `True` - **force_grid_wrap**: `0` - **include_trailing_comma**: `True` - **lines_after_imports**: `2` - **lines_between_types**: `1` - **multi_line_output**: `3` - **use_parentheses**: `True` #hug - **multi_line_output**: `3` - **include_trailing_comma**: `True` - **force_grid_wrap**: `0` - **use_parentheses**: `True` - **line_length**: `100` #wemake - **multi_line_output**: `3` - **include_trailing_comma**: `True` - **use_parentheses**: `True` - **line_length**: `80` #appnexus - **multi_line_output**: `3` - **include_trailing_comma**: `True` - **split_on_trailing_comma**: `True` - **force_grid_wrap**: `0` - **use_parentheses**: `True` - **ensure_newline_before_comments**: `True` - **line_length**: `88` - **force_sort_within_sections**: `True` - **order_by_type**: `False` - **case_sensitive**: `False` - **reverse_relative**: `True` - **sort_relative_in_force_sorted_sections**: `True` - **sections**: `['FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'APPLICATION', 'LOCALFOLDER']` - **no_lines_before**: `'LOCALFOLDER'` isort-9.0.1/docs/contributing/1.-contributing-guide.md0000644000000000000000000000760313615410400017606 0ustar00Contributing to isort ======== Looking for a useful open source project to contribute to? Want your contributions to be warmly welcomed and acknowledged? Welcome! You have found the right place. ## Getting isort set up for local development 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. Account Requirements: - [A valid GitHub account](https://github.com/join) Base System Requirements: - Python3.10+ - uv - bash or a bash compatible shell (should be auto-installed on Linux / Mac) - WSL users running Ubuntu may need to install Python's venv module even after installing Python. Once you have verified that your system matches the base requirements you can start to get the project working by following these steps: 1. [Fork the project on GitHub](https://github.com/pycqa/isort/fork). 2. Clone your fork to your local file system: `git clone https://github.com/$GITHUB_ACCOUNT/isort.git` 3. `cd isort` 4. `uv sync --all-extras --frozen` * Optionally, isolate uv's installation from the rest of your system using the instructions on the uv site here: https://docs.astral.sh/uv/ 5. `./scripts/test.sh` should yield Success: no issues found 6. `./scripts/clean.sh` should yield a report checking packages **TIP**: `./scripts/done.sh` will run both clean and test in one step. ### Docker development If you would instead like to develop using Docker, the only local requirement is docker. See the [docker docs](https://docs.docker.com/get-started/) if you have not used docker before. Once you have the docker daemon running and have cloned the repository, you can get started by following these steps: 1. `cd isort` 2. `./scripts/docker.sh` A local test cycle might look like the following: 1. `docker build ./ -t isort:latest` 2. `docker run isort` 3. if #2 fails, debug, save, and goto #1 * `docker run -it isort bash` will get you into the failed environment * `docker run -v $(git rev-parse --show-toplevel):/isort` will make changes made in the docker environment persist on your local checkout. **TIP**: combine both to get an interactive docker shell that loads changes made locally, even after build, to quickly rerun that pesky failing test 4. `./scripts/docker.sh` 5. if #4 fails, debug, save and goto #1; you may need to specify a different `--build-arg VERSION=$VER` 6. congrats! you are probably ready to push a contribution ## Making a contribution Congrats! You're now ready to make a contribution! Use the following as a guide to help you reach a successful pull-request: 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. - If it's listed there, write a comment letting others know you are working on it. - 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. - 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://github.com/PyCQA/isort/discussions). 2. Create an issue branch for your local work `git checkout -b issue/$ISSUE-NUMBER`. 3. Do your magic here. 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. 5. Run tests locally to make sure everything is still working `./scripts/done.sh` _Or if you are using Docker_ `docker run isort:latest` 6. Submit a pull request to the main project repository via GitHub. Thanks for the contribution! It will quickly get reviewed, and, once accepted, will result in your name being added to the acknowledgments list :). ## Thank you! I can not tell you how thankful I am for the hard work done by isort contributors like *you*. Thank you! ~Timothy Crosley isort-9.0.1/docs/contributing/2.-coding-standard.md0000644000000000000000000000577313615410400017054 0ustar00# HOPE 8 -- Style Guide for Hug Code | | | |------------|---------------------------------------------| | HOPE: | 8 | | Title: | Style Guide for Hug Code | | Author(s): | Timothy Crosley | | Status: | Active | | Type: | Process | | Created: | 19-May-2019 | | Updated: | 17-August-2019 | ## Introduction 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. Optionally, projects that use Hug are encouraged to follow this HOPE and link to it as a reference. ## PEP 8 Foundation 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. ## Line Length Too short of lines discourage descriptive variable names where they otherwise make sense. Too long of lines reduce overall readability and make it hard to compare 2 files side by side. There is no perfect number: but for Hug, we've decided to cap the lines at 100 characters. ## Descriptive Variable names Naming things is hard. Hug has a few strict guidelines on the usage of variable names, which hopefully will reduce some of the guesswork: - No one character variable names. - Except for x, y, and z as coordinates. - It's not okay to override built-in functions. - 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. - Avoid Acronyms, Abbreviations, or any other short forms - unless they are almost universally understand. ## Adding new modules 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. 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. ## Automated Code Cleaners All code submitted should be formatted using Ruff and isort. Ruff should be run with the line length set to 100, and isort with Black compatible settings in place. ## Automated Code Linting All code submitted should run through the following tools: - Ruff and isort verification. - Flake8 - flake8-bugbear - Bandit - ruff - pep8-naming - vulture isort-9.0.1/docs/contributing/3.-code-of-conduct.md0000644000000000000000000000755013615410400016760 0ustar00# HOPE 11 -- Code of Conduct | | | |------------|---------------------------------------------| | HOPE: | 11 | | Title: | Code of Conduct | | Author(s): | Timothy Crosley | | Status: | Active | | Type: | Process | | Created: | 17-August-2019 | | Updated: | 17-August-2019 | ## Abstract Defines the Code of Conduct for Hug and all related projects. ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting [timothy.crosley@gmail.com](mailto:timothy.crosley@gmail.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Confidentiality will be maintained with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq isort-9.0.1/docs/contributing/4.-acknowledgements.md0000644000000000000000000001654413615410400017345 0ustar00# Acknowledgements ## Core Developers - Aniruddha Bhattacharjee (@anirudnits) - Daniël van Noord (@DanielNoord) - Jon Dufresne (@jdufresne) - Tamas Szabo (@sztamas) - Thiago A. (@staticdev) - Timothy Edmund Crosley (@timothycrosley) ## Plugin Writers - *VIM* - Juan Pedro Fisanotti (@fisadev) - *Emacs* - Friedrich Paetzke (@paetzke) - *Sublime* - Thijs de Zoute (@thijsdezoete) ## Notable Bug Reporters - Bengt Lüers (@Bengt) - Chris Adams (@acdha) - @OddBloke - Martin Geisler (@mgeisleinr) - Tim Heap (@timheap) - Matěj Nikl (@MatejNikl) ## Code Contributors - Aaron Gallagher (@habnabit) - Thomas Grainger (@graingert) - Thijs de Zoute (@thijsdezoete) - Marc Abramowitz (@msabramo) - Daniel Cowgill (@dcowgill) - Francois Lebel (@flebel) - Antoni Segura Puimedon (@celebdor) - Pablo (@oubiga) - Oskar Hahn (@ostcar) - Wim Glenn (@wimglenn) - Matt Caldwell (@mattcaldwell) - Dwayne Bailey (@dwaynebailey) - Ionel Cristian Mărieș (@ionelmc) - Chris Adams (@acdha) - GuoJing (@GuoJing) - George Hickman (@ghickman) - Dan Davison (@dandavison) - Maciej Wolff (@maciejwo) - Elliott Sales de Andrade (@qulogic) - Kasper Jacobsen (@dinoshauer) - Sebastian Pipping (@hartwork) - Helen Sherwood-Taylor (@helenst) - Mocker (@Zuckonit) - Tim Graham (@timgraham) - Adam (@NorthIsUp) - Norman Jäckel (@normanjaeckel) - Derrick Petzold (@dpetzold) - Michael van Tellingen (@mvantellingen) - Patrick Yevsukov (@patrickyevsukov) - Christer van der Meeren (@cmeeren) - Timon Wong/NHNCN (@timonwong) - Jeremy Dunck (@jdunck) - Benjamin ABEL (@benjaminabel) - Dan Baragan (@danbaragan) - Rob Cowie (@robcowie) - Amit Shah (@Amwam) - Patrick Gerken (@do3cc) - @dein0s - David Stensland (@terite) - Ankur Dedania (@AbsoluteMSTR) - Lee Packham (@leepa) - Jesse Mullan (@jmullan) - Kwok-kuen Cheung (@cheungpat) - Johan Bloemberg (@aequitas) - Dan Watson (@dcwatson) - Éric Araujo (@merwok) - Dan Palmer (@danpalmer) - Andy Boot (@bootandy) - @m7v8 - John Vandenberg (@jayvdb) - Adam Chainz (@adamchainz) - @Brightcells - Jonas Trappenberg (@teeberg) - Andrew Konstantaras (@akonsta) - Jason Brackman (@jasonbrackman) - Kathryn Lingel (@katlings) - Andrew Gaul (@gaul) - John Chadwick (@jchv) - Jon Dufresne (@jdufresne) - Brian F. Baron (@briabar) - Madison Caldwell (@madirey) - Matt Yule-Bennett (@mattbennett) - Jaswanth Kumar (@jaswanth098) - Dario Navin (@Zarathustra2) - Danny Weinberg (@FuegoFro) - Gram (@orsinium) - Hugo van Kemenade (@hugovk) - Géry Ogam (@maggyero) - Cody Scott (@Siecje) - Pedro Algarvio (@s0undt3ch) - Chris St. Pierre (@stpierre) - Sebastian Rittau (@srittau) - João M.C. Teixeira (@joaomcteixeira) - Honnix (@honnix) - Anders Kaseorg (@andersk) - @r-richmond - Sebastian (@sebix) - Kosei Kitahara (@Surgo) - Seung Hyeon, Kim (@hyeonjames) - Gerard Dalmau (@gdalmau) - Robert Tasarz (@rtasarz) - Ryo Miyajima (@sergeant-wizard) - @mdagaro - Maksim Kurnikov (@mkurnikov) - Daniel Hahler (@blueyed) - @ucodery - Aarni Koskela (@akx) - Alex Chan (@alexwlchan) - Rick Thomas (@richardlthomas) - Jeppe Fihl-Pearson (@Tenzer) - Jonas Lundberg (@lundberg) - Neil (@NeilGirdhar) - @dmanikowski-reef - Stephen Brown II (@StephenBrown2) - Ankur Dedania (@AnkurDedania) - Anthony Sottile (@asottile) - Bendik Samseth (@bsamseth) - Dan W Anderson (@anderson-dan-w) - DeepSource Bot (@deepsourcebot) - Mitar (@mitar) - Omer Katz (@thedrow) - Santiago Castro (@bryant1410) - Sergey Fursov (@GeyseR) - Thomas Robitaille (@astrofrog) - Ville Skyttä (@scop) - Hakan Çelik (@hakancelik96) - Dylan Katz (@Plazmaz) - Linus Lewandowski (@LEW21) - Bastien Gérard (@bagerard) - Brian Dombrowski (@bdombro) - Ed Morley (@edmorley) - Graeme Coupar (@obmarg) - Jerome Leclanche (@jleclanche) - Joshu Coats (@rhwlo) - Mansour Behabadi (@oxplot) - Sam Lai (@slai) - Tamas Szabo (@sztamas) - Yedidyah Bar David (@didib) - Hidetoshi Hirokawa (@h-hirokawa) - Aaron Chong (@acjh) - Harai Akihiro (@harai) - Andy Freeland (@rouge8) - @ethifus - Joachim Brandon LeBlanc (@demosdemon) - Brian May (@brianmay) - Bruno Oliveira (@nicoddemus) - Bruno Renié (@brutasse) - Bryce Guinta (@brycepg) - David Chan (@dchanm) - David Smith (@smithdc1) - Irv Lustig (@Dr-Irv) - Dylan Richardson (@dylrich) - Emil Melnikov (@emilmelnikov) - Eric Johnson (@metrizable) - @ryabtsev - Felix Yan (@felixonmars) - Gil Forcada Codinachs (@gforcada) - Ilya Konstantinov (@ikonst) - Jace Browning (@jacebrowning) - Jin Suk Park (@jinmel) - Jürgen Gmach (@jugmac00) - Maciej Gawinecki (@dzieciou) - Minn Soe (@MinnSoe) - Nikolaus Wittenstein (@adzenith) - Norman J. Harman Jr. (@njharman) - P R Gurunath (@gurunath-p) - Patrick Hayes (@pfhayes) - Pete Grayson (@jpgrayson) - Philip Jenvey (@pjenvey) - Rajiv Bakulesh Shah (@brainix) - Reid D McKenzie (@arrdem) - Robert DeRose (@RobertDeRose) - Roey Darwish Dror (@r-darwish) - Rudinei Goi Roecker (@rudineirk) - Wagner (@wagner-certat) - Nikita Sobolev (@sobolevn) - Terence Honles (@terencehonles) - The Gitter Badger (@gitter-badger) - Tim Gates (@timgates42) - Tim Staley (@timstaley) - Vincent Hatakeyama (@vincent-hatakeyama) - Yaron de Leeuw (@jarondl) - @jwg4 - @nicolelodeon - Łukasz Langa (@ambv) - Grzegorz Pstrucha (@Gricha) - Zac Hatfield-Dodds (@Zac-HD) - Jiří Škorpil (@JiriSko) - James Winegar (@jameswinegar) - Abdullah Dursun (@adursun) - Guillaume Lostis (@glostis) - Krzysztof Jagiełło (@kjagiello) - Nicholas Devenish (@ndevenish) - Aniruddha Bhattacharjee (@anirudnits) - Alexandre Yang (@AlexandreYang) - Andrew Howe (@howeaj) - Sang-Heon Jeon (@lntuition) - Denis Veselov (@saippuakauppias) - James Curtin (@jamescurtin) - Marco Gorelli (@MarcoGorelli) - Louis Sautier (@sbraz) - Timur Kushukov (@timqsh) - Bhupesh Varshney (@Bhupesh-V) - Rohan Khanna (@rohankhanna) - Vasilis Gerakaris (@vgerak) - @tonci-bw - @jaydesl - Tamara (@infinityxxx) - Akihiro Nitta (@akihironitta) - Samuel Gaist (@sgaist) - @dwanderson-intel - Quentin Santos (@qsantos) - @gofr - Pavel Savchenko (@asfaltboy) - @dongfangtianyu - Christian Clauss (@cclauss) - Jon Banafato (@jonafato) - ruro (@RuRo) - Léni (@legau) - keno (Ken Okada) (@kenoss) - Shota Terashita (@shotat) - Luca Di sera (@diseraluca) - Tonye Jack (@jackton1) - Yusuke Hayashi (@yhay81) - Arthur Rio (@arthurio) - Bob (@bobwalker99) - Martijn Pieters (@mjpieters) - Asiel Díaz Benítez (@adbenitez) - Almaz (@monosans) - Mathieu Kniewallner (@mkniewallner) - Christian Decker (@chrisdecker1201) - Adam Parkin (@pzelnip) - @MapleCCC - @Parnassius - @SaucyGames05 - Tim Heap (@mx-moth) ## Documenters - Reinout van Rees (@reinout) - Helen Sherwood-Taylor (@helenst) - Elliott Sales de Andrade (@QuLogic) - Brian Peiris (@brianpeiris) - Tim Graham (@timgraham) - Josh Soref (@jsoref) - Teg Khanna (@tegkhanna) - Sarah Beth Tracy (@sbtries) - Aaron Brown (@aaronvbrown) - Harutaka Kawamura (@harupy) - Brad Solomon (@bsolomon1124) - Martynas Mickevičius (@2m) - Taneli Hukkinen (@hukkinj1) - @r-richmond - John Villalovos (@JohnVillalovos) - Kosei Kitahara (@Surgo) - Marat Sharafutdinov (@decaz) - Abtin (@abtinmo) - @scottwedge - Hasan Ramezani (@hramezani) - @hirosassa - David Poznik (@dpoznik) - Mike Frysinger (@vapier) - @DanielFEvans - Giuseppe Lumia (@glumia) - John Brock (@JohnHBrock) - Sergey Fedoseev (@sir-sigurd) -------------------------------------------- A sincere thanks to everyone who has helped isort be the great utility it is today! It would not be one-hundredth as useful and consistent as it is now without the help of your bug reports, commits, and suggestions. You guys rock! ~Timothy Crosley isort-9.0.1/docs/howto/shared_profiles.md0000644000000000000000000000077213615410400015372 0ustar00# Shared profiles As well as the [built in profiles](../configuration/profiles.md), you can define and share your own profiles. All that's required is to create a Python package that exposes an entry point to a dictionary exposing profile settings under `isort.profiles`. An example is available [within the `isort` repo](https://github.com/PyCQA/isort/tree/main/example_shared_isort_profile) ## Example `.isort.cfg` ``` [options.entry_points] isort.profiles = shared_profile=my_module:PROFILE ``` isort-9.0.1/docs/major_releases/introducing_isort_5.md0000644000000000000000000001150713615410400020043 0ustar00# Introducing isort 5 [![isort 5 - the best version of isort yet](https://raw.githubusercontent.com/pycqa/isort/main/art/logo_5.png)](https://isort.readthedocs.io/) isort 5.0.0 is the first major release of isort in over five years and the first significant refactoring of isort since it was conceived more than ten years ago. It's also the first version to require Python 3 (Python 3.6+ at that!) to run - though it can still be run on source files from any version of Python. This does mean that there may be some pain with the upgrade process, but we believe the improvements will be well worth it. [Click here for an attempt at full changelog with a list of breaking changes.](../changelog.rst) [Using isort 4.x.x? Click here for the isort 5.0.0 upgrade guide.](../upgrade_guides/5.0.0.md) [Try isort 5 right now from your browser!](../quick_start/0.-try.md) So why the massive change? ## Profile support ``` isort --profile black . isort --profile django . isort --profile pycharm . isort --profile google . isort --profile open_stack . isort --profile plone . isort --profile attrs . isort --profile hug . ``` isort is very configurable. That's great, but it can be overwhelming, both for users and for the isort project. isort now comes with profiles for the most common isort configurations, so you likely will not need to configure anything at all. This also means that as a project, isort can run extensive tests against these specific profiles to ensure nothing breaks over time. ## Sort imports **anywhere** ```python3 import a # <- These are sorted import b b.install(a) import os # <- And these are sorted import sys def my_function(): import x # <- Even these are sorted! import z ``` isort 5 will find and sort contiguous section of imports no matter where they are. It also allows you to place code in-between imports without any hacks required. ## Streaming architecture ```python import a import b ... # more code! ``` isort has been refactored to use a streaming architecture. This means it can sort files of *any* size (even larger than the Python interpreter supports!) without breaking a sweat. It also means that even when sorting imports in smaller files, it is faster and more resource-efficient. ## Consistent behavior across **all** environments Sorting the same file with the same configuration should give you the same output no matter what computer or OS you are running. Extensive effort has been placed around refactoring how modules are placed and how configuration files are loaded to ensure this is the case. ## Cython support ```python3 cimport ctime from cpython cimport PyLong_FromVoidPtr from cpython cimport bool as py_bool from cython.operator cimport dereference as deref from cython.operator cimport preincrement as preinc from libc.stdint cimport uint64_t, uintptr_t from libc.stdlib cimport atoi, calloc, free, malloc from libc.string cimport memcpy, strlen from libcpp cimport bool as cpp_bool from libcpp.map cimport map as cpp_map from libcpp.pair cimport pair as cpp_pair from libcpp.string cimport string as cpp_string from libcpp.vector cimport vector as cpp_vector from multimap cimport multimap as cpp_multimap from wstring cimport wstring as cpp_wstring ``` isort 5 adds seamless support for Cython (`.pyx`) files. ## Action Comments ```python3 import e import f # isort: off <- Turns isort parsing off import b import a # isort: on <- Turns isort parsing back on import c import d ``` isort 5 adds support for [Action Comments](../configuration/action_comments.md) which provide a quick and convenient way to control the flow of parsing within single source files. ## First class Python API ```python3 import isort isort.code(""" import b import a """) == """ import a import b """ ``` isort now exposes its programmatic API as a first-class citizen. This API makes it easy to extend or use isort in your own Python project. You can see the full documentation for this new API [here](../reference/isort.rst). ## Solid base for the future A major focus for the release was to give isort a solid foundation for the next 5-10 years of the project's life. isort has been refactored into functional components that are easily testable. The project now has 100% code coverage. It utilizes tools like [Hypothesis](https://hypothesis.readthedocs.io/en/latest/) to reduce the number of unexpected errors. It went from fully dynamic to fully static typing using mypy. Finally, it utilizes the latest linters both on (like [DeepSource](https://deepsource.io/gh/pycqa/isort/)) and offline (like [Flake8](https://flake8.pycqa.org/en/latest/)) to help ensure a higher bar for all code contributions into the future. ## Give 5.0.0 a try! [Try isort 5 right now from your browser!](../quick_start/0.-try.md) OR Install isort locally using `pip3 install isort`. [Click here for full installation instructions.](../quick_start/1.-install.md) isort-9.0.1/docs/major_releases/release_policy.md0000644000000000000000000000465013615410400017052 0ustar00# Isort project official release policy Isort has moved from being a simple hobby project for individuals to sort imports in their Python files to an essential part of the CI/CD pipeline for large companies and significant Open Source projects. Due to this evolution, it is now of increased importance that isort maintains a level of quality, predictability, and consistency that gives projects big and small confidence to depend on it. ## Formatting guarantees 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 without worrying about major formatting changes disrupting their Project. ## Packaging guarantees Starting with the 5.0.0 release isort includes the following project guarantees to help guide development: - isort will never have dependencies, optional, required, or otherwise. - isort will always act the same independent to the Python environment it is installed in. ## Versioning 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 `MAJOR.MINOR.PATCH`. ### Patch Releases x.x.1 Within the isort Project, patch releases are really meant solely to fix bugs and minor oversights. Patch releases should *never* drastically change formatting, even if it's for the better. ### Minor Releases x.1.x Minor changes can contain new backward-incompatible features, and of particular note can include bug fixes that result in intentional formatting changes - but they should still never be too large in scope. API backward compatibility should strictly be maintained. ### Major Releases 1.x.x Major releases are the only place where backward-incompatible changes or substantial formatting changes can occur. Because these kind of changes are likely to break projects that utilize isort, either as a formatter or library, isort must do the following: - Release a release candidate with at least 2 weeks for bugs to be reported and fixed. - Keep releasing follow up release candidates until there are no or few bugs reported. - Provide an upgrade guide that helps users work around any backward-incompatible changes. - Provide a detailed changelog of all changes. - Where possible, warn and point to the upgrade guide instead of breaking when options are removed. isort-9.0.1/docs/quick_start/0.-try.md0000644000000000000000000000756213615410400014447 0ustar00# Try isort from your browser! Use our live isort editor to see how isort can help improve the formatting of your Python imports. !!! important - "Safe to use. No code is transmitted." 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. Try isort
from future import braces import b import b import os import a from future import braces import b import a import b, a
Installing isort with Pyodide...
 Configuration (Note: the below must follow JSON format). Full configuration guide is here:
{"line_length": 80, "profile": "black", "atomic": true }
Like what you saw? Installing isort to use locally is as simple as `pip install isort`. [Click here for full installation instructions.](./1.-install.md) isort-9.0.1/docs/quick_start/1.-install.md0000644000000000000000000000144513615410400015272 0ustar00# Install Install `isort` using your preferred Python package manager: `pip3 install isort` OR `uv add isort` OR `poetry add isort` OR `pipenv install isort` OR For a fully isolated user installation you can use [pipx](https://github.com/pipxproject/pipx) `pipx install isort` !!!tip 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. isort-9.0.1/docs/quick_start/2.-cli.md0000644000000000000000000000575213615410400014401 0ustar00# Command line usage Once installed, `isort` exposes a command line utility for sorting, organizing, and formatting imports within Python and Cython source files. 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. For a list of all CLI options type `isort --help` or view [the online configuration reference](../configuration/options.md): ## Formatting a Project 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`). ## Verifying a Project 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 .`. ## Single Source Files 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`). ## Temporary Files By default, when isort processes a source file, it writes the result to a sibling temporary file by appending `.isorted` to the source filename. If the content changes, this temporary file replaces the original. isort removes the temporary file when processing finishes and also attempts cleanup if sorting raises an exception. If the process terminates before cleanup runs, the temporary file may remain. It can be removed after isort has stopped. Projects where abrupt termination is possible can ignore `*.isorted` files, or use the [`--overwrite-in-place`](../configuration/options.md#overwrite-in-place) option to avoid creating them, with a performance and memory usage penalty. ## Multiple Projects 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. ``` # YES isort project1 isort project2 # Also YES isort project1/src project1/test isort project2/src project2/test # NO isort project1 project2 ``` isort-9.0.1/docs/quick_start/3.-api.md0000644000000000000000000000306613615410400014400 0ustar00# Programmatic Python API Usage In addition to the powerful command line interface, isort exposes a complete Python API. To use the Python API, `import isort` and then call the desired function call: Every function is fully type hinted and requires and returns only builtin Python objects. Highlights include: - `isort.code` - Takes a string containing code, and returns it with imports sorted. - `isort.check_code` - Takes a string containing code, and returns `True` if all imports are sorted correctly, otherwise, `False`. - `isort.stream` - Takes an input stream containing Python code and an output stream. Outputs code to output stream with all imports sorted. - `isort.check_stream` - Takes an input stream containing Python code and returns `True` if all imports in the stream are sorted correctly, otherwise, `False`. - `isort.file` - Takes the path of a Python source file and sorts the imports in-place. - `isort.check_file` - Takes the path of a Python source file and returns `True` if all imports contained within are sorted correctly, otherwise, `False`. - `isort.place_module` - Takes the name of a module as a string and returns the categorization determined for it. - `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. For a full definition of the API see the [API reference documentation](../reference/isort.rst) or try `help(isort)` from an interactive interpreter. isort-9.0.1/docs/reference/isort.rst0000644000000000000000000000113513615410400014361 0ustar00API Reference ============= isort.api --------- .. automodule:: isort.api isort.identify -------------- .. automodule:: isort.identify isort.main ---------- .. automodule:: isort.main isort.place ----------- .. automodule:: isort.place isort.wrap_modes ---------------- .. automodule:: isort.wrap_modes :no-index-entry: isort.settings -------------- .. automodule:: isort.settings :no-index-entry: :private-members: isort.utils ---------------- .. automodule:: isort.utils :no-index-entry: isort.exceptions ---------------- .. automodule:: isort.exceptions :no-index-entry: isort-9.0.1/docs/upgrade_guides/5.0.0.md0000644000000000000000000001657513615410400014520 0ustar00# Upgrading to 5.0.0 isort 5.0.0 is the first major release of isort in 5 years, and as such it does introduce some breaking changes. This guide is meant to help migrate projects from using isort 4.x.x unto the 5.0.0 release. Related documentation: * [isort 5.0.0 changelog](../changelog.rst) * [isort 5 release document](../major_releases/introducing_isort_5.md) !!! important - "If you use pre-commit remove seed-isort-config." If you currently use pre-commit, make sure to see the pre-commit section of this document. In particular, make sure to remove any `seed-isort-config` pre-step. ## Imports no Longer Moved to Top One of the most immediately evident changes when upgrading to isort 5, is it now avoids moving imports around code by default. The great thing about this is that it means that isort can safely run against complex code bases that need to place side effects between import sections without needing any comments, flags, or configs. It's also part of the rearchitecting that allows it to sort within type checking conditionals and functions. However, it can be a jarring change for those of us who have gotten used to placing imports right above their usage in code to avoid context switching. No need to worry! isort still supports this work mode. If you want to move all imports to the top, you can use the new`--float-to-top` flag in the CLI or `float_to_top=true` option in your config file. See: [float-to-top](../configuration/options.md#float-to-top) ## Migrating CLI options ### `--dont-skip` or `-ns` In an earlier version isort had a default skip of `__init__.py`. To get around that many projects wanted a way to not skip `__init__.py` or any other files that were automatically skipped in the future by isort. isort no longer has any default skips, so if the value here is `__init__.py` you can simply remove the command line option. If it is something else, just make sure you aren't specifying to skip that file somewhere else in your config. ### `--recursive` or `-rc` Prior to version 5.0.0, isort wouldn't automatically traverse directories. The --recursive option was necessary to tell it to do so. In 5.0.0 directories are automatically traversed for all Python files, and as such this option is no longer necessary and should simply be removed. ### `--apply` or `-y` Prior to version 5.0.0, depending on how isort was executed, it would ask you before making every file change. In isort 5.0.0 file changes happen by default inline with other formatters. `--interactive` is available to restore the previous behavior. If encountered this option can simply be removed. ### `--keep-direct-and-as` or `-k` Many versions ago, by default isort would remove imports such as `from datetime import datetime` if an alias for the same import also existed such as `from datetime import datetime as dt` - never allowing both to exist. The option was originally added to allow working around this, and was then turned on as the default. Now the option for the old behaviour has been removed. Simply remove the option from your config file. ### `-ac`, `-wl`, `-ws`, `-tc`, `-sp`, `-sp`, `-sl`, `-sg`, `-sd`, `-rr`, `-ot`, `-nlb`, `-nis`, `-ls`, `-le`, `-lbt`, `-lai`, `-fss`, `-fgw`, `-ff`, `-fass`, `-fas`, `-dt`, `-ds`, `-df`, `-cs`, `-ca`, `-af`, `-ac` Two-letter shortened setting names (like `ac` for `atomic`) now require two dashes to avoid ambiguity. Simply add another dash before the option, or switch to the long form option to fix (example: `--ac` or `--atomic`). ### `-v` and `-V` The `-v` (previously for version now for verbose) and `-V` (previously for verbose and now for version) options have been swapped to be more consistent with tools across the CLI and in particular Python ecosystem. ## Migrating Config options The first thing to keep in mind is how isort loads config options has changed in isort 5. It will no longer merge multiple config files, instead you must have 1 isort config per a project. If you have multiple configs, they will need to be merged into 1 single one. You can see the priority order of configuration files and the manner in which they are loaded on the [config files documentation page](../configuration/config_files.md). !!! tip - "Config options are loaded relative to the file, not the isort instance." isort looks for a config file based on the path of the file you request to sort. If you have your config placed outside of the project, you can use `--settings-path` to manually specify the config location instead. Full information about how config files are loaded is in the linked config files documentation page. ### `not_skip` This is the same as the `--dont-skip` CLI option above. In an earlier version isort had a default skip of `__init__.py`. To get around that many projects wanted a way to not skip `__init__.py` or any other files that were automatically skipped in the future by isort. isort no longer has any default skips, so if the value here is `__init__.py` you can simply remove the setting. If it is something else, just make sure you aren't specifying to skip that file somewhere else in your config. ### `keep_direct_and_as_imports` This is the same as `keep-direct-and-as` from CLI. Many versions ago, by default isort would remove imports such as `from datetime import datetime` if an alias for the same import also existed such as `from datetime import datetime as dt` - never allowing both to exist. The option was originally added to allow working around this, and was then turned on as the default. Now the option for the old behaviour has been removed. Simply remove the option from your config file. ### `known_standard_library` isort settings no longer merge together, instead they override. The old behavior of merging together caused many hard to track down errors, but the one place it was very convenient was for adding a few additional standard library modules. In isort 5, you can still get this behavior by moving your extra modules from the `known_standard_library` setting to [`extra_standard_library`](../configuration/options.md#extra-standard-library). ### module placement changes: `known_third_party`, `known_first_party`, `default_section`, etc... isort has completely rewritten its logic for placing modules in 5.0.0 to ensure the same behavior across environments. You can see the details of this change [here](https://github.com/pycqa/isort/issues/1147). The TL;DR of which is that isort has now changed from `default_section=FIRSTPARTY` to `default_section=THIRDPARTY`. If you all already setting the default section to third party, your config is probably in good shape. If not, you should be able to just specify your projects modules with `known_first_party` and be done with it. ## Migrating pre-commit ### seed-isort-config If you have a step in your precommit called `seed-isort-config` or similar, it is highly recommend that you remove this. It is unnecessary in 5.x.x, is guaranteed to slow things down, and worse can conflict with isort's own module placement logic. ### isort pre-commit step isort now includes an optimized precommit configuration in the repo itself. To use it you can replace any existing isort precommit step with: ``` - repo: https://github.com/pycqa/isort rev: 6.1.0 hooks: - id: isort name: isort (python) - id: isort name: isort (cython) types: [cython] - id: isort name: isort (pyi) types: [pyi] ``` under the `repos` section of your projects `.pre-commit-config.yaml` config. isort-9.0.1/docs/warning_and_error_codes/W0500.md0000644000000000000000000000252113615410400016443 0ustar00# W0500 warning codes The W0500 error codes are reserved for warnings related to a major release of the isort project. Generally, the existence of any of these will trigger one additional warning listing the upgrade guide. For the most recent upgrade guide, see: [The 5.0.0 Upgrade Guide.](../upgrade_guides/5.0.0.md). ## W0501: Deprecated CLI flags were included that will be ignored. 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. Often, this happens because an argument used to be required to turn on a feature that then became the default. An example of this is `--recursive` which became the default behavior for all folders passed-in starting with 5.0.0. ## W0502: Deprecated CLI flags were included that will safely be remapped. 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 before the next major release. ## W0503: Deprecated config options were ignored. This warning will be shown if a deprecated config option is defined in the Project's isort config file, but can safely be ignored. This is similar to `W0500` but dealing with config files rather than CLI flags. isort-9.0.1/example_isort_formatting_plugin/example_isort_formatting_plugin.py0000644000000000000000000000110013615410400025311 0ustar00import black import isort def black_format_import_section( contents: str, extension: str, config: isort.settings.Config ) -> str: """Formats the given import section using black.""" if extension.lower() not in ("pyi", "py"): return contents try: return black.format_file_contents( contents, fast=True, mode=black.FileMode( is_pyi=extension.lower() == "pyi", line_length=config.line_length, ), ) except black.NothingChanged: return contents isort-9.0.1/example_isort_formatting_plugin/pyproject.toml0000644000000000000000000000110413615410400021174 0ustar00[project] name = "example_isort_formatting_plugin" version = "0.1.1" description = "An example plugin that modifies isort formatting using black." authors = [{name = "Timothy Crosley", email = "timothy.crosley@gmail.com"}, {name = "staticdev", email = "staticdev-support@proton.me"}] license = "MIT" requires-python = ">=3.10.0" dependencies = [ "isort>=5.13.2", "black>=24.3.0", ] [project.entry-points."isort.formatters"] example = "example_isort_formatting_plugin:black_format_import_section" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" isort-9.0.1/example_isort_formatting_plugin/uv.lock0000644000000000000000000010600713615410400017574 0ustar00version = 1 revision = 3 requires-python = ">=3.10.0" [[package]] name = "black" version = "26.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "mypy-extensions" }, { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, { name = "pytokens" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e1/c5/61175d618685d42b005847464b8fb4743a67b1b8fdb75e50e5a96c31a27a/black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07", size = 666155, upload-time = "2026-03-12T03:36:03.593Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/32/a8/11170031095655d36ebc6664fe0897866f6023892396900eec0e8fdc4299/black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2", size = 1866562, upload-time = "2026-03-12T03:39:58.639Z" }, { url = "https://files.pythonhosted.org/packages/69/ce/9e7548d719c3248c6c2abfd555d11169457cbd584d98d179111338423790/black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b", size = 1703623, upload-time = "2026-03-12T03:40:00.347Z" }, { url = "https://files.pythonhosted.org/packages/7f/0a/8d17d1a9c06f88d3d030d0b1d4373c1551146e252afe4547ed601c0e697f/black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac", size = 1768388, upload-time = "2026-03-12T03:40:01.765Z" }, { url = "https://files.pythonhosted.org/packages/52/79/c1ee726e221c863cde5164f925bacf183dfdf0397d4e3f94889439b947b4/black-26.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a", size = 1412969, upload-time = "2026-03-12T03:40:03.252Z" }, { url = "https://files.pythonhosted.org/packages/73/a5/15c01d613f5756f68ed8f6d4ec0a1e24b82b18889fa71affd3d1f7fad058/black-26.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a", size = 1220345, upload-time = "2026-03-12T03:40:04.892Z" }, { url = "https://files.pythonhosted.org/packages/17/57/5f11c92861f9c92eb9dddf515530bc2d06db843e44bdcf1c83c1427824bc/black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff", size = 1851987, upload-time = "2026-03-12T03:40:06.248Z" }, { url = "https://files.pythonhosted.org/packages/54/aa/340a1463660bf6831f9e39646bf774086dbd8ca7fc3cded9d59bbdf4ad0a/black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c", size = 1689499, upload-time = "2026-03-12T03:40:07.642Z" }, { url = "https://files.pythonhosted.org/packages/f3/01/b726c93d717d72733da031d2de10b92c9fa4c8d0c67e8a8a372076579279/black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5", size = 1754369, upload-time = "2026-03-12T03:40:09.279Z" }, { url = "https://files.pythonhosted.org/packages/e3/09/61e91881ca291f150cfc9eb7ba19473c2e59df28859a11a88248b5cbbc4d/black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e", size = 1413613, upload-time = "2026-03-12T03:40:10.943Z" }, { url = "https://files.pythonhosted.org/packages/16/73/544f23891b22e7efe4d8f812371ab85b57f6a01b2fc45e3ba2e52ba985b8/black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5", size = 1219719, upload-time = "2026-03-12T03:40:12.597Z" }, { url = "https://files.pythonhosted.org/packages/dc/f8/da5eae4fc75e78e6dceb60624e1b9662ab00d6b452996046dfa9b8a6025b/black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1", size = 1895920, upload-time = "2026-03-12T03:40:13.921Z" }, { url = "https://files.pythonhosted.org/packages/2c/9f/04e6f26534da2e1629b2b48255c264cabf5eedc5141d04516d9d68a24111/black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f", size = 1718499, upload-time = "2026-03-12T03:40:15.239Z" }, { url = "https://files.pythonhosted.org/packages/04/91/a5935b2a63e31b331060c4a9fdb5a6c725840858c599032a6f3aac94055f/black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7", size = 1794994, upload-time = "2026-03-12T03:40:17.124Z" }, { url = "https://files.pythonhosted.org/packages/e7/0a/86e462cdd311a3c2a8ece708d22aba17d0b2a0d5348ca34b40cdcbea512e/black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983", size = 1420867, upload-time = "2026-03-12T03:40:18.83Z" }, { url = "https://files.pythonhosted.org/packages/5b/e5/22515a19cb7eaee3440325a6b0d95d2c0e88dd180cb011b12ae488e031d1/black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb", size = 1230124, upload-time = "2026-03-12T03:40:20.425Z" }, { url = "https://files.pythonhosted.org/packages/f5/77/5728052a3c0450c53d9bb3945c4c46b91baa62b2cafab6801411b6271e45/black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54", size = 1895034, upload-time = "2026-03-12T03:40:21.813Z" }, { url = "https://files.pythonhosted.org/packages/52/73/7cae55fdfdfbe9d19e9a8d25d145018965fe2079fa908101c3733b0c55a0/black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f", size = 1718503, upload-time = "2026-03-12T03:40:23.666Z" }, { url = "https://files.pythonhosted.org/packages/e1/87/af89ad449e8254fdbc74654e6467e3c9381b61472cc532ee350d28cfdafb/black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56", size = 1793557, upload-time = "2026-03-12T03:40:25.497Z" }, { url = "https://files.pythonhosted.org/packages/43/10/d6c06a791d8124b843bf325ab4ac7d2f5b98731dff84d6064eafd687ded1/black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839", size = 1422766, upload-time = "2026-03-12T03:40:27.14Z" }, { url = "https://files.pythonhosted.org/packages/59/4f/40a582c015f2d841ac24fed6390bd68f0fc896069ff3a886317959c9daf8/black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2", size = 1232140, upload-time = "2026-03-12T03:40:28.882Z" }, { url = "https://files.pythonhosted.org/packages/d5/da/e36e27c9cebc1311b7579210df6f1c86e50f2d7143ae4fcf8a5017dc8809/black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78", size = 1889234, upload-time = "2026-03-12T03:40:30.964Z" }, { url = "https://files.pythonhosted.org/packages/0e/7b/9871acf393f64a5fa33668c19350ca87177b181f44bb3d0c33b2d534f22c/black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568", size = 1720522, upload-time = "2026-03-12T03:40:32.346Z" }, { url = "https://files.pythonhosted.org/packages/03/87/e766c7f2e90c07fb7586cc787c9ae6462b1eedab390191f2b7fc7f6170a9/black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f", size = 1787824, upload-time = "2026-03-12T03:40:33.636Z" }, { url = "https://files.pythonhosted.org/packages/ac/94/2424338fb2d1875e9e83eed4c8e9c67f6905ec25afd826a911aea2b02535/black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c", size = 1445855, upload-time = "2026-03-12T03:40:35.442Z" }, { url = "https://files.pythonhosted.org/packages/86/43/0c3338bd928afb8ee7471f1a4eec3bdbe2245ccb4a646092a222e8669840/black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1", size = 1258109, upload-time = "2026-03-12T03:40:36.832Z" }, { url = "https://files.pythonhosted.org/packages/8e/0d/52d98722666d6fc6c3dd4c76df339501d6efd40e0ff95e6186a7b7f0befd/black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b", size = 207542, upload-time = "2026-03-12T03:36:01.668Z" }, ] [[package]] name = "click" version = "8.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] name = "example-isort-formatting-plugin" version = "0.1.1" source = { editable = "." } dependencies = [ { name = "black" }, { name = "isort" }, ] [package.metadata] requires-dist = [ { name = "black", specifier = ">=24.3.0" }, { name = "isort", specifier = ">=5.13.2" }, ] [[package]] name = "isort" version = "5.13.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303, upload-time = "2023-12-13T20:37:26.124Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310, upload-time = "2023-12-13T20:37:23.244Z" }, ] [[package]] name = "mypy-extensions" version = "1.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433, upload-time = "2023-02-04T12:11:27.157Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695, upload-time = "2023-02-04T12:11:25.002Z" }, ] [[package]] name = "packaging" version = "24.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950, upload-time = "2024-11-08T09:47:47.202Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" }, ] [[package]] name = "pathspec" version = "1.0.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, ] [[package]] name = "platformdirs" version = "4.3.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302, upload-time = "2024-09-17T19:06:50.688Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439, upload-time = "2024-09-17T19:06:49.212Z" }, ] [[package]] name = "pytokens" version = "0.4.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5", size = 161522, upload-time = "2026-01-30T01:02:50.393Z" }, { url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe", size = 246945, upload-time = "2026-01-30T01:02:52.399Z" }, { url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c", size = 259525, upload-time = "2026-01-30T01:02:53.737Z" }, { url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7", size = 262693, upload-time = "2026-01-30T01:02:54.871Z" }, { url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2", size = 103567, upload-time = "2026-01-30T01:02:56.414Z" }, { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" }, { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" }, { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" }, { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" }, { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" }, { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, ] [[package]] name = "tomli" version = "2.2.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, ] [[package]] name = "typing-extensions" version = "4.12.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321, upload-time = "2024-06-07T18:52:15.995Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438, upload-time = "2024-06-07T18:52:13.582Z" }, ] isort-9.0.1/example_isort_sorting_plugin/example_isort_sorting_plugin.py0000644000000000000000000000026613615410400024153 0ustar00from natsort import natsorted def natural_plus(*args, **kwargs) -> str: """An even more natural sorting order for isort using natsort.""" return natsorted(*args, **kwargs) isort-9.0.1/example_isort_sorting_plugin/pyproject.toml0000644000000000000000000000112413615410400020511 0ustar00[project] name = "example_isort_sorting_plugin" version = "0.1.0" description = "An example plugin that modifies isorts sorting order to provide an even more natural sort by utilizing natsort." authors = [{name = "Timothy Crosley", email = "timothy.crosley@gmail.com"}, {name = "staticdev", email = "staticdev-support@proton.me"}] license = "MIT" requires-python = ">=3.10.0" dependencies = [ "natsort>=7.1.1" ] [project.entry-points."isort.sort_function"] natural_plus = "example_isort_sorting_plugin:natural_plus" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" isort-9.0.1/example_isort_sorting_plugin/uv.lock0000644000000000000000000000171013615410400017102 0ustar00version = 1 revision = 3 requires-python = ">=3.10.0" [[package]] name = "example-isort-sorting-plugin" version = "0.1.0" source = { editable = "." } dependencies = [ { name = "natsort" }, ] [package.metadata] requires-dist = [{ name = "natsort", specifier = ">=7.1.1" }] [[package]] name = "natsort" version = "8.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575, upload-time = "2023-06-20T04:17:19.925Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268, upload-time = "2023-06-20T04:17:17.522Z" }, ] isort-9.0.1/example_shared_isort_profile/example_shared_isort_profile.py0000644000000000000000000000023513615410400024015 0ustar00PROFILE = { "multi_line_output": 3, "include_trailing_comma": True, "force_grid_wrap": 0, "use_parentheses": True, "line_length": 100, } isort-9.0.1/example_shared_isort_profile/pyproject.toml0000644000000000000000000000071513615410400020441 0ustar00[project] name = "example_shared_isort_profile" version = "0.1.0" description = "An example shared isort profile" authors = [{name = "Timothy Crosley", email = "timothy.crosley@gmail.com"}, {name = "staticdev", email = "staticdev-support@proton.me"}] license = "MIT" requires-python = ">=3.10.0" [project.entry-points."isort.profiles"] example = "example_shared_isort_profile:PROFILE" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" isort-9.0.1/example_shared_isort_profile/uv.lock0000644000000000000000000000022713615410400017027 0ustar00version = 1 revision = 3 requires-python = ">=3.10.0" [[package]] name = "example-shared-isort-profile" version = "0.1.0" source = { editable = "." } isort-9.0.1/isort/__init__.py0000644000000000000000000000155013615410400013053 0ustar00"""Defines the public isort interface""" __all__ = ( "Config", "ImportKey", "__version__", "check_code", "check_file", "check_stream", "code", "file", "find_imports_in_code", "find_imports_in_file", "find_imports_in_paths", "find_imports_in_stream", "place_module", "place_module_with_reason", "settings", "stream", ) from . import settings from ._version import __version__ from .api import ImportKey from .api import check_code_string as check_code from .api import ( check_file, check_stream, find_imports_in_code, find_imports_in_file, find_imports_in_paths, find_imports_in_stream, place_module, place_module_with_reason, ) from .api import sort_code_string as code from .api import sort_file as file from .api import sort_stream as stream from .settings import Config isort-9.0.1/isort/__main__.py0000644000000000000000000000004413615410400013031 0ustar00from isort.main import main main() isort-9.0.1/isort/_parse_utils.py0000644000000000000000000001533413615410400014012 0ustar00"""Shared low-level parsing utilities.""" import re from collections.abc import Callable from typing import Literal, NamedTuple from .settings import Config class NormalizeLineResult(NamedTuple): normalized_line: str raw_line: str def normalize_line(raw_line: str) -> NormalizeLineResult: """Normalizes import related statements in the provided line.""" line = re.sub(r"from(\.+)cimport ", r"from \g<1> cimport ", raw_line) line = re.sub(r"from(\.+)import ", r"from \g<1> import ", line) line = line.replace("import*", "import *") line = re.sub(r" (\.+)import ", r" \g<1> import ", line) line = re.sub(r" (\.+)cimport ", r" \g<1> cimport ", line) line = line.replace("\t", " ") return NormalizeLineResult(normalized_line=line, raw_line=raw_line) def strip_syntax(import_string: str) -> str: import_string = import_string.replace("_import", "[[i]]") import_string = import_string.replace("_cimport", "[[ci]]") for remove_syntax in ["\\", "(", ")", ","]: import_string = import_string.replace(remove_syntax, " ") import_list = import_string.split() for key in ("from", "import", "cimport"): if key in import_list: import_list.remove(key) import_string = " ".join(import_list) import_string = import_string.replace("[[i]]", "_import") import_string = import_string.replace("[[ci]]", "_cimport") return import_string.replace("{ ", "{|").replace(" }", "|}") class SkipLineResult(NamedTuple): should_skip: bool in_quote: str # Ignore DeepSource cyclomatic complexity check for this function. # skipcq: PY-R1000 def skip_line(line: str, in_quote: str, needs_import: bool = True) -> SkipLineResult: """Determine if a given line should be skipped.""" should_skip = bool(in_quote) if '"' in line or "'" in line: char_index = 0 while char_index < len(line): if line[char_index] == "\\": char_index += 1 elif in_quote: if line[char_index : char_index + len(in_quote)] == in_quote: in_quote = "" elif line[char_index] in ("'", '"'): long_quote = line[char_index : char_index + 3] if long_quote in ('"""', "'''"): in_quote = long_quote char_index += 2 else: in_quote = line[char_index] elif line[char_index] == "#": break char_index += 1 if ";" in line.split("#")[0] and needs_import: for part in (part.strip() for part in line.split(";")): if ( part and not part.startswith("from ") and not part.startswith(("import ", "cimport ")) ): should_skip = True return SkipLineResult(should_skip=bool(should_skip or in_quote), in_quote=in_quote) class ExtraLine(NamedTuple): line: str comment: str | None class ImportContinuationResult(NamedTuple): final_line: str complete_import_string: str extra_lines: list[ExtraLine] def collect_import_continuation( line: str, import_string: str, get_next_line: Callable[[], tuple[str, str | None]], line_separator: str = "\n", ) -> ImportContinuationResult: r"""Collect continuation lines for a multi-line import statement. Handles both parenthesised imports ``from X import (`` + newline + `` Y, Z)`` and backslash-continued imports ``import Y, \\`` + newline + `` Z``. """ extra_lines: list[ExtraLine] = [] if "(" in line.split("#", 1)[0]: while not line.split("#")[0].strip().endswith(")"): try: line, comment = get_next_line() except StopIteration: break extra_lines.append(ExtraLine(line=line, comment=comment)) import_string += line_separator + line else: while line.strip().endswith("\\"): try: line, comment = get_next_line() except StopIteration: break line = line.lstrip() # Still need to check for parentheses after an escaped line if "(" in line.split("#")[0] and ")" not in line.split("#")[0]: extra_lines.append(ExtraLine(line=line, comment=comment)) import_string += line_separator + line while not line.split("#")[0].strip().endswith(")"): try: line, comment = get_next_line() except StopIteration: break extra_lines.append(ExtraLine(line=line, comment=comment)) import_string += line_separator + line else: if import_string.strip().endswith( (" import", " cimport") ) or line.strip().startswith(("import ", "cimport ")): extra_lines.append(ExtraLine(line=line, comment=comment)) import_string += line_separator + line else: extra_lines.append(ExtraLine(line=line, comment=comment)) import_string = import_string.rstrip().rstrip("\\") + " " + line.lstrip() return ImportContinuationResult( final_line=line, complete_import_string=import_string, extra_lines=extra_lines, ) def normalize_from_import_string(import_string: str) -> str: """Normalize a ``from … import …`` string, handling line-continuation characters. Removes ``import(``, backslash continuations and embedded newlines, then reconstructs the canonical ``from X import Y, Z`` form. """ import_string = ( import_string.replace("import(", "import (").replace("\\", " ").replace("\n", " ") ) cimports = " cimport " in import_string parts = import_string.split(" cimport " if cimports else " import ") from_import = parts[0].split(" ") return (" cimport " if cimports else " import ").join( [from_import[0] + " " + "".join(from_import[1:]), *parts[1:]] ) # TODO: Return a `StrEnum` once we no longer support Python 3.10. def import_type( line: str, config: Config ) -> Literal["from", "straight", "lazy_from", "lazy_straight"] | None: """If the current line is an import line it will return its type.""" if config.honor_noqa and line.lower().rstrip().endswith("noqa"): return None if "isort:skip" in line or "isort: skip" in line or "isort: split" in line: return None if line.startswith(("import ", "cimport ")): return "straight" if line.startswith("from "): return "from" if line.startswith("lazy import "): return "lazy_straight" if line.startswith("lazy from "): return "lazy_from" return None isort-9.0.1/isort/_version.py0000644000000000000000000000031213615410400013133 0ustar00from importlib import metadata __version__ = metadata.version("isort") _IS_COMPILED = __file__.endswith((".so", ".pyd")) _VERSION_STRING = f"{__version__} (compiled {'yes' if _IS_COMPILED else 'no'})" isort-9.0.1/isort/api.py0000644000000000000000000006017213615410400012072 0ustar00__all__ = ( "ImportKey", "check_code_string", "check_file", "check_stream", "find_imports_in_code", "find_imports_in_file", "find_imports_in_paths", "find_imports_in_stream", "place_module", "place_module_with_reason", "sort_code_string", "sort_file", "sort_stream", ) import contextlib import os import shutil import sys from collections.abc import Iterator from contextlib import AbstractContextManager, nullcontext from enum import Enum from io import StringIO from itertools import chain from pathlib import Path from threading import Event from typing import Any, TextIO, cast from warnings import warn from isort import core from . import files, identify, io from .exceptions import ( ExistingSyntaxErrors, FileSkipComment, FileSkipSetting, IntroducedSyntaxErrors, ) from .format import ask_whether_to_apply_changes_to_file, create_terminal_printer, show_unified_diff from .io import File from .place import module as place_module # noqa: F401 from .place import module_with_reason as place_module_with_reason # noqa: F401 from .settings import CYTHON_EXTENSIONS, DEFAULT_CONFIG, Config class ImportKey(Enum): """Defines how to key an individual import, generally for deduping. Import keys are defined from less to more specific: from x.y import z as a PACKAGE: x MODULE: y ATTRIBUTE: z ALIAS: a """ PACKAGE = 1 MODULE = 2 ATTRIBUTE = 3 ALIAS = 4 def sort_code_string( code: str, extension: str | None = None, config: Config = DEFAULT_CONFIG, file_path: Path | None = None, disregard_skip: bool = False, show_diff: bool | TextIO = False, **config_kwargs: Any, ) -> str: """Sorts any imports within the provided code string, returning a new string with them sorted. - **code**: The string of code with imports that need to be sorted. - **extension**: The file extension that contains imports. Defaults to filename extension or py. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a TextIO stream is provided results will be written to it, otherwise no diff will be computed. - ****config_kwargs**: Any config modifications. """ input_stream = StringIO(code) output_stream = StringIO() config = _config(path=file_path, config=config, **config_kwargs) sort_stream( input_stream, output_stream, extension=extension, config=config, file_path=file_path, disregard_skip=disregard_skip, show_diff=show_diff, ) output_stream.seek(0) return output_stream.read() def check_code_string( code: str, show_diff: bool | TextIO = False, extension: str | None = None, config: Config = DEFAULT_CONFIG, file_path: Path | None = None, disregard_skip: bool = False, **config_kwargs: Any, ) -> bool: """Checks the order, format, and categorization of imports within the provided code string. Returns `True` if everything is correct, otherwise `False`. - **code**: The string of code with imports that need to be sorted. - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a TextIO stream is provided results will be written to it, otherwise no diff will be computed. - **extension**: The file extension that contains imports. Defaults to filename extension or py. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. - ****config_kwargs**: Any config modifications. """ config = _config(path=file_path, config=config, **config_kwargs) return check_stream( StringIO(code), show_diff=show_diff, extension=extension, config=config, file_path=file_path, disregard_skip=disregard_skip, ) def sort_stream( input_stream: TextIO, output_stream: TextIO, extension: str | None = None, config: Config = DEFAULT_CONFIG, file_path: Path | None = None, disregard_skip: bool = False, show_diff: bool | TextIO = False, raise_on_skip: bool = True, **config_kwargs: Any, ) -> bool: """Sorts any imports within the provided code stream, outputs to the provided output stream. Returns `True` if anything is modified from the original input stream, otherwise `False`. - **input_stream**: The stream of code with imports that need to be sorted. - **output_stream**: The stream where sorted imports should be written to. - **extension**: The file extension that contains imports. Defaults to filename extension or py. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a TextIO stream is provided results will be written to it, otherwise no diff will be computed. - ****config_kwargs**: Any config modifications. """ extension = extension or (file_path and file_path.suffix.lstrip(".")) or "py" if show_diff: _output_stream = StringIO() _input_stream = StringIO(input_stream.read()) changed = sort_stream( input_stream=_input_stream, output_stream=_output_stream, extension=extension, config=config, file_path=file_path, disregard_skip=disregard_skip, raise_on_skip=raise_on_skip, **config_kwargs, ) _output_stream.seek(0) _input_stream.seek(0) show_unified_diff( file_input=_input_stream.read(), file_output=_output_stream.read(), file_path=file_path, output=output_stream if show_diff is True else show_diff, color_output=config.color_output, ) return changed config = _config(path=file_path, config=config, **config_kwargs) content_source = str(file_path or "Passed in content") if not disregard_skip and file_path and config.is_skipped(file_path): raise FileSkipSetting(content_source) _internal_output = output_stream if config.atomic: try: file_content = input_stream.read() compile(file_content, content_source, "exec", flags=0, dont_inherit=True) except SyntaxError: if extension not in CYTHON_EXTENSIONS: raise ExistingSyntaxErrors(content_source) if config.verbose: warn( f"{content_source} Python AST errors found but ignored due to Cython extension", stacklevel=2, ) input_stream = StringIO(file_content) if not output_stream.readable(): _internal_output = StringIO() try: changed = core.process( input_stream, _internal_output, extension=extension, config=config, raise_on_skip=raise_on_skip, ) except FileSkipComment: raise FileSkipComment(content_source) if config.atomic: _internal_output.seek(0) try: compile(_internal_output.read(), content_source, "exec", flags=0, dont_inherit=True) _internal_output.seek(0) except SyntaxError: # pragma: no cover if extension not in CYTHON_EXTENSIONS: raise IntroducedSyntaxErrors(content_source) if config.verbose: warn( f"{content_source} Python AST errors found but ignored due to Cython extension", stacklevel=2, ) if _internal_output != output_stream: output_stream.write(_internal_output.read()) return changed def check_stream( input_stream: TextIO, show_diff: bool | TextIO = False, extension: str | None = None, config: Config = DEFAULT_CONFIG, file_path: Path | None = None, disregard_skip: bool = False, **config_kwargs: Any, ) -> bool: """Checks any imports within the provided code stream, returning `False` if any unsorted or incorrectly imports are found or `True` if no problems are identified. - **input_stream**: The stream of code with imports that need to be sorted. - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a TextIO stream is provided results will be written to it, otherwise no diff will be computed. - **extension**: The file extension that contains imports. Defaults to filename extension or py. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. - ****config_kwargs**: Any config modifications. """ config = _config(path=file_path, config=config, **config_kwargs) if show_diff: input_stream = StringIO(input_stream.read()) with open(os.devnull, "w") as devnull: changed: bool = sort_stream( input_stream=input_stream, output_stream=devnull, extension=extension, config=config, file_path=file_path, disregard_skip=disregard_skip, ) printer = create_terminal_printer( color=config.color_output, error=config.format_error, success=config.format_success ) if not changed: if config.verbose and not config.only_modified: printer.success(f"{file_path or ''} Everything Looks Good!") return True printer.error(f"{file_path or ''} Imports are incorrectly sorted and/or formatted.") if show_diff: output_stream = StringIO() input_stream.seek(0) file_contents = input_stream.read() sort_stream( input_stream=StringIO(file_contents), output_stream=output_stream, extension=extension, config=config, file_path=file_path, disregard_skip=disregard_skip, ) output_stream.seek(0) show_unified_diff( file_input=file_contents, file_output=output_stream.read(), file_path=file_path, output=None if show_diff is True else show_diff, color_output=config.color_output, ) return False def check_file( filename: str | Path, show_diff: bool | TextIO = False, config: Config = DEFAULT_CONFIG, file_path: Path | None = None, disregard_skip: bool = True, extension: str | None = None, **config_kwargs: Any, ) -> bool: """Checks any imports within the provided file, returning `False` if any unsorted or incorrectly imports are found or `True` if no problems are identified. - **filename**: The name or Path of the file to check. - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a TextIO stream is provided results will be written to it, otherwise no diff will be computed. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. - **extension**: The file extension that contains imports. Defaults to filename extension or py. - ****config_kwargs**: Any config modifications. """ file_config: Config = config if "config_trie" in config_kwargs: config_trie = config_kwargs.pop("config_trie", None) if config_trie: config_info = config_trie.search(filename) if config.verbose: print(f"{config_info[0]} used for file {filename}") file_config = Config(**config_info[1]) with io.File.read(filename) as source_file: return check_stream( source_file.stream, show_diff=show_diff, extension=extension, config=file_config, file_path=file_path or source_file.path, disregard_skip=disregard_skip, **config_kwargs, ) @contextlib.contextmanager def _in_memory_output_stream_context(source_file: File, changed: Event) -> Iterator[TextIO]: """ Output stream that is kept in memory. If `changed` is set at exit it will copy the stream to the source file. """ stream = StringIO(newline=None) yield stream if changed.is_set(): stream.seek(0) source_file.stream.close() with source_file.path.open("w") as fs: shutil.copyfileobj(stream, fs) @contextlib.contextmanager def _file_output_stream_context( filename: str | Path, source_file: File, changed: Event ) -> Iterator[TextIO]: """ Output stream that is written to disk, using a "temporary" `.isorted` file. If `changed` is set at exit it will replace the source file with the temporary file. """ tmp_file = source_file.path.with_suffix(source_file.path.suffix + ".isorted") try: with tmp_file.open("w+", encoding=source_file.encoding, newline="") as output_stream: shutil.copymode(filename, tmp_file) yield output_stream if changed.is_set(): source_file.stream.close() tmp_file.replace(source_file.path) finally: # Make sure to remove the temporary file, whatever is the outcoming of sorting. tmp_file.unlink(missing_ok=True) # Ignore DeepSource cyclomatic complexity check for this function. It is one # the main entrypoints so sort of expected to be complex. # skipcq: PY-R1000 def sort_file( filename: str | Path, extension: str | None = None, config: Config = DEFAULT_CONFIG, file_path: Path | None = None, disregard_skip: bool = True, ask_to_apply: bool = False, show_diff: bool | TextIO = False, write_to_stdout: bool = False, output: TextIO | None = None, **config_kwargs: Any, ) -> bool: """Sorts and formats any groups of imports within the provided file or Path. Returns `True` if the file has been changed, otherwise `False`. - **filename**: The name or Path of the file to format. - **extension**: The file extension that contains imports. Defaults to filename extension or py. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. - **ask_to_apply**: If `True`, prompt before applying any changes. - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a TextIO stream is provided results will be written to it, otherwise no diff will be computed. - **write_to_stdout**: If `True`, write to stdout instead of the input file. - **output**: If a TextIO is provided, results will be written there rather than replacing the original file content. - ****config_kwargs**: Any config modifications. """ file_config: Config = config if "config_trie" in config_kwargs: config_trie = config_kwargs.pop("config_trie", None) if config_trie: config_info = config_trie.search(filename) if config.verbose: print(f"{config_info[0]} used for file {filename}") file_config = Config(**config_info[1]) with io.File.read(filename) as source_file: actual_file_path = file_path or source_file.path config = _config(path=actual_file_path, config=file_config, **config_kwargs) if write_to_stdout: output = sys.stdout # Prepare the output stream. Using the `is_changed_event` we propagate whether the file # should flush the output to the source file. is_changed_event = Event() if output: output_stream_context: AbstractContextManager[TextIO] = nullcontext(output) elif config.overwrite_in_place: output_stream_context = _in_memory_output_stream_context(source_file, is_changed_event) else: output_stream_context = _file_output_stream_context( filename, source_file, is_changed_event ) with output_stream_context as output_stream: try: changed = sort_stream( input_stream=source_file.stream, output_stream=output_stream, config=config, file_path=actual_file_path, disregard_skip=disregard_skip, extension=extension, ) except ExistingSyntaxErrors: warn( f"{actual_file_path} unable to sort due to existing syntax errors", stacklevel=2, ) return False except IntroducedSyntaxErrors: # pragma: no cover warn( f"{actual_file_path} unable to sort as isort introduces new syntax errors", stacklevel=2, ) return False if not changed or write_to_stdout: return changed if show_diff or ask_to_apply: output_stream.seek(0) source_file.stream.seek(0) show_unified_diff( file_input=source_file.stream.read(), file_output=output_stream.read(), file_path=actual_file_path, output=(None if show_diff is True else cast(TextIO, show_diff)), color_output=config.color_output, ) if show_diff or ( ask_to_apply and not ask_whether_to_apply_changes_to_file(str(source_file.path)) ): return False if not config.quiet and not output: print(f"Fixing {source_file.path}") is_changed_event.set() return True def find_imports_in_code( code: str, config: Config = DEFAULT_CONFIG, file_path: Path | None = None, unique: bool | ImportKey = False, top_only: bool = False, **config_kwargs: Any, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided code string. - **code**: The string of code with imports that need to be sorted. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **unique**: If True, only the first instance of an import is returned. - **top_only**: If True, only return imports that occur before the first function or class. - ****config_kwargs**: Any config modifications. """ yield from find_imports_in_stream( input_stream=StringIO(code), config=config, file_path=file_path, unique=unique, top_only=top_only, **config_kwargs, ) def find_imports_in_stream( input_stream: TextIO, config: Config = DEFAULT_CONFIG, file_path: Path | None = None, unique: bool | ImportKey = False, top_only: bool = False, _seen: set[str] | None = None, **config_kwargs: Any, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided code stream. - **input_stream**: The stream of code with imports that need to be sorted. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **unique**: If True, only the first instance of an import is returned. - **top_only**: If True, only return imports that occur before the first function or class. - **_seen**: An optional set of imports already seen. Generally meant only for internal use. - ****config_kwargs**: Any config modifications. """ config = _config(config=config, **config_kwargs) identified_imports = identify.imports( input_stream, config=config, file_path=file_path, top_only=top_only ) if not unique: yield from identified_imports seen: set[str] = set() if _seen is None else _seen for identified_import in identified_imports: if unique in (True, ImportKey.ALIAS): key = identified_import.statement() elif unique == ImportKey.ATTRIBUTE: key = f"{identified_import.module}.{identified_import.attribute}" elif unique == ImportKey.MODULE: key = identified_import.module elif unique == ImportKey.PACKAGE: # pragma: no branch # type checking ensures this key = identified_import.module.split(".")[0] if key and key not in seen: seen.add(key) yield identified_import def find_imports_in_file( filename: str | Path, config: Config = DEFAULT_CONFIG, file_path: Path | None = None, unique: bool | ImportKey = False, top_only: bool = False, **config_kwargs: Any, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided source file. - **filename**: The name or Path of the file to look for imports in. - **extension**: The file extension that contains imports. Defaults to filename extension or py. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **unique**: If True, only the first instance of an import is returned. - **top_only**: If True, only return imports that occur before the first function or class. - ****config_kwargs**: Any config modifications. """ try: with io.File.read(filename) as source_file: yield from find_imports_in_stream( input_stream=source_file.stream, config=config, file_path=file_path or source_file.path, unique=unique, top_only=top_only, **config_kwargs, ) except OSError as error: warn(f"Unable to parse file {filename} due to {error}", stacklevel=2) def find_imports_in_paths( paths: Iterator[str | Path], config: Config = DEFAULT_CONFIG, file_path: Path | None = None, unique: bool | ImportKey = False, top_only: bool = False, **config_kwargs: Any, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided source paths. - **paths**: A collection of paths to recursively look for imports within. - **extension**: The file extension that contains imports. Defaults to filename extension or py. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **unique**: If True, only the first instance of an import is returned. - **top_only**: If True, only return imports that occur before the first function or class. - ****config_kwargs**: Any config modifications. """ config = _config(config=config, **config_kwargs) seen: set[str] | None = set() if unique else None yield from chain( *( find_imports_in_file( file_name, unique=unique, config=config, top_only=top_only, _seen=seen ) for file_name in files.find(map(str, paths), config, [], []) ) ) def _config( path: Path | None = None, config: Config = DEFAULT_CONFIG, **config_kwargs: Any ) -> Config: if path and ( config is DEFAULT_CONFIG and "settings_path" not in config_kwargs and "settings_file" not in config_kwargs ): config_kwargs["settings_path"] = path if config_kwargs: if config is not DEFAULT_CONFIG: raise ValueError( "You can either specify custom configuration options using kwargs or " "passing in a Config object. Not Both!" ) config = Config(**config_kwargs) return config isort-9.0.1/isort/comments.py0000644000000000000000000000314313615410400013141 0ustar00def parse(line: str) -> tuple[str, str | None]: """Parses import lines for comments and returns back the import statement and the associated comment. Returns ``None`` as the comment when no ``#`` is present in the line, and an empty string when a bare ``#`` with no text is present. """ comment_start = line.find("#") if comment_start != -1: return (line[:comment_start], line[comment_start + 1 :].strip()) return (line, None) def add_to_line( comments: list[str] | None, original_string: str = "", removed: bool = False, comment_prefix: str = "", ) -> str: """Returns a string with comments added if removed is not set.""" if removed: return parse(original_string)[0] if not comments: return original_string unique_comments: list[str] = [] for comment in comments: if comment not in unique_comments: unique_comments.append(comment) comment_text = "; ".join(unique_comments) base, existing_comment = parse(original_string) if existing_comment is not None: # An existing comment was stripped off, leaving the whitespace that used to # precede it (the previous ``comment_prefix`` spacing). Drop it so re-adding a # comment does not accumulate leading spaces on every run. Bases without a # comment (e.g. the indentation-only strings used by the multi-line wrap modes) # are left untouched so their indentation is preserved. base = base.rstrip() if comment_text: return f"{base}{comment_prefix} {comment_text}" return f"{base}{comment_prefix}" isort-9.0.1/isort/core.py0000644000000000000000000006371513615410400012257 0ustar00import textwrap from io import StringIO from itertools import chain from typing import TextIO import isort.literal from isort.settings import DEFAULT_CONFIG, Config from . import output, parse from .exceptions import ExistingSyntaxErrors, FileSkipComment from .format import format_natural, remove_whitespace from .settings import FILE_SKIP_COMMENTS CIMPORT_IDENTIFIERS = ("cimport ", "cimport*", "from.cimport") _BASE_IMPORT_IDENTIFIERS = ("from ", "from.import", "import ", "import*") IMPORT_START_IDENTIFIERS = ( *_BASE_IMPORT_IDENTIFIERS, *(f"lazy {identifier}" for identifier in _BASE_IMPORT_IDENTIFIERS), *CIMPORT_IDENTIFIERS, ) DOCSTRING_INDICATORS = ('"""', "'''") COMMENT_INDICATORS = (*DOCSTRING_INDICATORS, "'", '"', "#") # Every legal Python string prefix, lower-cased; `u` cannot be combined with another. STRING_PREFIXES = frozenset(("r", "u", "f", "b", "fr", "rf", "br", "rb")) CODE_SORT_COMMENTS = ( "# isort: list", "# isort: dict", "# isort: set", "# isort: unique-list", "# isort: tuple", "# isort: unique-tuple", "# isort: assignments", ) LITERAL_TYPE_MAPPING = {"(": "tuple", "[": "list", "{": "set"} PYLINT_DISABLE_NEXT_COMMENT = "# pylint: disable-next" SKIP_IMPORT_COMMENTS = ("isort:skip", "isort: skip") def _strip_string_prefix(line: str) -> str: """Return `line` without a leading Python string prefix (`r`, `b`, `f`, `u` or a legal combination of them), so quote-based heuristics see the quote itself. """ for prefix_length in (2, 1): if line[:prefix_length].lower() in STRING_PREFIXES and line[ prefix_length : prefix_length + 1 ] in ("'", '"'): return line[prefix_length:] return line def _is_comment_or_string_start(line: str) -> bool: """Return whether `line` opens a comment, a docstring or any other string literal. A string prefix (`r`, `b`, `f`, `u` or a legal combination of them) is skipped first, so a prefixed docstring is recognised exactly as an unprefixed one is. """ return _strip_string_prefix(line.lstrip()).startswith(COMMENT_INDICATORS) def _has_skip_comment(import_statement: str) -> bool: """Return whether an import statement carries a per-line ``isort: skip`` directive.""" return any(comment in import_statement for comment in SKIP_IMPORT_COMMENTS) # Ignore DeepSource cyclomatic complexity check for this function. # skipcq: PY-R1000 def process( input_stream: TextIO, output_stream: TextIO, extension: str = "py", raise_on_skip: bool = True, config: Config = DEFAULT_CONFIG, ) -> bool: """Parses stream identifying sections of contiguous imports and sorting them Code with unsorted imports is read from the provided `input_stream`, sorted and then outputted to the specified `output_stream`. - `input_stream`: Text stream with unsorted import sections. - `output_stream`: Text stream to output sorted inputs into. - `config`: Config settings to use when sorting imports. Defaults settings. - *Default*: `isort.settings.DEFAULT_CONFIG`. - `extension`: The file extension or file extension rules that should be used. - *Default*: `"py"`. - *Choices*: `["py", "pyi", "pyx"]`. Returns `True` if there were changes that needed to be made (errors present) from what was provided in the input_stream, otherwise `False`. """ line_separator: str = config.line_ending add_imports: list[str] = [format_natural(addition) for addition in config.add_imports] import_section: str = "" next_import_section: str = "" next_cimports: bool = False in_quote: str = "" was_in_quote: bool = False first_comment_index_start: int = -1 first_comment_index_end: int = -1 contains_imports: bool = False in_top_comment: bool = False first_import_section: bool = True indent: str = "" isort_off: bool = False skip_file: bool = False code_sorting: bool | str = False code_sorting_section: str = "" code_sorting_indent: str = "" cimports: bool = False made_changes: bool = False stripped_line: str = "" end_of_file: bool = False verbose_output: list[str] = [] lines_before: list[str] = [] is_reexport: bool = False reexport_rollback: int = 0 if config.float_to_top: new_input = "" current = "" isort_off = False for line in chain(input_stream, (None,)): stripped_line = line.strip() if line is not None else "" if isort_off and line is not None: if stripped_line == "# isort: on": isort_off = False new_input += line elif ( line is None or stripped_line in ("# isort: split", "# isort: off") or str(line).rstrip().endswith("# isort: split") ): if stripped_line == "# isort: off": isort_off = True if current: before = current if add_imports: add_line_separator = line_separator or "\n" current += add_line_separator + add_line_separator.join(add_imports) add_imports = [] parsed = parse.file_contents(current, config=config) verbose_output += parsed.verbose_output extra_space = "" while before and before[-1] == "\n": extra_space += "\n" before = before[:-1] extra_space = extra_space.replace("\n", "", 1) sorted_output = output.sorted_imports( parsed, config, extension, import_type="import" ) made_changes = made_changes or _has_changed( before=before, after=sorted_output, line_separator=parsed.line_separator, ignore_whitespace=config.ignore_whitespace, ) new_input += sorted_output new_input += extra_space current = "" new_input += line or "" else: current += line or "" input_stream = StringIO(new_input) for index, line in enumerate(chain(input_stream, (None,))): if line is None: if index == 0 and not config.force_adds: return False not_imports = True end_of_file = True line = "" if not line_separator: line_separator = "\n" if code_sorting and code_sorting_section: if is_reexport: # Clamp to 0: in check mode the output is a no-op stream whose tell() # stays 0, so an unclamped rollback would seek to a negative position # and raise ValueError. See PR #2576. output_stream.seek(max(0, output_stream.tell() - reexport_rollback)) reexport_rollback = 0 sorted_code = textwrap.indent( isort.literal.assignment( code_sorting_section, str(code_sorting), extension, config=_indented_config(config, indent), ), code_sorting_indent, ) made_changes = made_changes or _has_changed( before=code_sorting_section, after=sorted_code, line_separator=line_separator, ignore_whitespace=config.ignore_whitespace, ) output_stream.write(sorted_code) if ( is_reexport # Check if we need to truncate. If we're redirecting to `devnull` we don't need # to and don't want to call `truncate()` on it, as it will raise an exception. and output_stream.tell() > 0 ): output_stream.truncate() else: stripped_line = line.strip() if stripped_line and not line_separator: line_separator = ( line[len(line.rstrip()) :].replace(" ", "").replace("\t", "").replace("\f", "") ) for file_skip_comment in FILE_SKIP_COMMENTS: if file_skip_comment in line: if raise_on_skip: raise FileSkipComment("Passed in content") isort_off = True skip_file = True if not in_quote: if stripped_line == "# isort: off": isort_off = True elif stripped_line.startswith("# isort: dont-add-imports"): add_imports = [] elif stripped_line.startswith("# isort: dont-add-import:"): import_not_to_add = stripped_line.split("# isort: dont-add-import:", 1)[ 1 ].strip() add_imports = [ import_to_add for import_to_add in add_imports if import_to_add != import_not_to_add ] if ( (index == 0 or (index in {1, 2} and not contains_imports)) and stripped_line.startswith("#") and not stripped_line.startswith(PYLINT_DISABLE_NEXT_COMMENT) and stripped_line not in config.section_comments and stripped_line not in CODE_SORT_COMMENTS ): in_top_comment = True elif in_top_comment and ( not line.startswith("#") or stripped_line in config.section_comments or stripped_line in CODE_SORT_COMMENTS ): in_top_comment = False first_comment_index_end = index - 1 was_in_quote = bool(in_quote) if ((not stripped_line.startswith("#") or in_quote) and '"' in line) or "'" in line: char_index = 0 if first_comment_index_start == -1 and line.startswith(('"', "'")): first_comment_index_start = index while char_index < len(line): if line[char_index] == "\\": char_index += 1 elif in_quote: if line[char_index : char_index + len(in_quote)] == in_quote: in_quote = "" if first_comment_index_end < first_comment_index_start: first_comment_index_end = index elif line[char_index] in ("'", '"'): long_quote = line[char_index : char_index + 3] if long_quote in ('"""', "'''"): in_quote = long_quote char_index += 2 else: in_quote = line[char_index] elif line[char_index] == "#": break char_index += 1 not_imports = bool(in_quote) or was_in_quote or in_top_comment or isort_off if not (in_quote or was_in_quote or in_top_comment): if isort_off: if not skip_file and stripped_line == "# isort: on": isort_off = False elif stripped_line.endswith("# isort: split"): not_imports = True elif stripped_line in CODE_SORT_COMMENTS: code_sorting = stripped_line.split("isort: ")[1].strip() code_sorting_indent = line[: -len(line.lstrip())] not_imports = True elif ( config.sort_reexports and stripped_line.startswith("__all__") and not _has_skip_comment(stripped_line) ): _, rhs = stripped_line.split("=") code_sorting = LITERAL_TYPE_MAPPING.get(rhs.lstrip()[0], "tuple") code_sorting_indent = line[: -len(line.lstrip())] not_imports = True code_sorting_section += line reexport_rollback = len(line) is_reexport = True elif code_sorting: if not stripped_line: sorted_code = textwrap.indent( isort.literal.assignment( code_sorting_section, str(code_sorting), extension, config=_indented_config(config, indent), ), code_sorting_indent, ) made_changes = made_changes or _has_changed( before=code_sorting_section, after=sorted_code, line_separator=line_separator, ignore_whitespace=config.ignore_whitespace, ) if is_reexport: # Clamp to 0 so check mode's no-op output stream (tell() == 0) # cannot produce a negative seek position. See PR #2576. output_stream.seek(max(0, output_stream.tell() - reexport_rollback)) reexport_rollback = 0 output_stream.write(sorted_code) if ( is_reexport # Check if we need to truncate. If we're redirecting to `devnull` we # don't need to and don't want to call `truncate()` on it, as it will # raise an exception. and output_stream.tell() > 0 ): output_stream.truncate() not_imports = True code_sorting = False code_sorting_section = "" code_sorting_indent = "" is_reexport = False else: code_sorting_section += line line = "" elif ( stripped_line in config.section_comments or stripped_line in config.section_comments_end ): if import_section and not contains_imports: output_stream.write(import_section) import_section = line not_imports = False else: import_section += line indent = line[: -len(line.lstrip())] elif not (stripped_line or contains_imports): not_imports = True elif not stripped_line or ( stripped_line.startswith("#") and (not indent or indent + line.lstrip() == line) and not config.treat_all_comments_as_code and stripped_line not in config.treat_comments_as_code ): import_section += line elif stripped_line.startswith(IMPORT_START_IDENTIFIERS): new_indent = line[: -len(line.lstrip())] import_statement = line stripped_line = line.strip().split("#")[0] while stripped_line.endswith("\\") or ( "(" in stripped_line and ")" not in stripped_line ): if stripped_line.endswith("\\"): while stripped_line and stripped_line.endswith("\\"): line = input_stream.readline() stripped_line = line.strip().split("#")[0] import_statement += line else: while ")" not in stripped_line: line = input_stream.readline() if not line: # end of file without closing parenthesis raise ExistingSyntaxErrors("Parenthesis is not closed") stripped_line = line.strip().split("#")[0] import_statement += line # The second clause keeps a per-line ``isort: skip`` import exactly # where it is: when earlier imports have already been collected into # the current section, the skipped statement is treated as a section # boundary so those imports can't be sorted above it. Without it, a # preceding import (most commonly a ``__future__`` import, which is # always floated to the top) makes isort splice the sorted block # ahead of the skipped line and relocate it below the block. See #2092. if ( import_statement.lstrip().startswith("from") and "import" not in import_statement ) or (contains_imports and _has_skip_comment(import_statement)): line = import_statement not_imports = True else: did_contain_imports = contains_imports contains_imports = True cimport_statement: bool = False if ( import_statement.lstrip().startswith(CIMPORT_IDENTIFIERS) or " cimport " in import_statement or " cimport*" in import_statement or " cimport(" in import_statement or ( ".cimport" in import_statement and "cython.cimports" not in import_statement ) # Allow pure python imports. See #2062 ): cimport_statement = True if cimport_statement != cimports or ( new_indent != indent and import_section and (not did_contain_imports or len(new_indent) < len(indent)) ): indent = new_indent if import_section: next_cimports = cimport_statement next_import_section = import_statement import_statement = "" not_imports = True line = "" else: cimports = cimport_statement else: if new_indent != indent: if import_section and did_contain_imports: import_statement = indent + import_statement.lstrip() else: indent = new_indent import_section += import_statement else: not_imports = True if not_imports: above_import_section: str = "" if not was_in_quote and config.lines_before_imports > -1: if line.strip() == "" and not end_of_file: lines_before += line continue if not import_section: output_stream.write("".join(lines_before)) else: above_import_section = "".join(lines_before) lines_before = [] raw_import_section: str = import_section if ( add_imports and (stripped_line or end_of_file) and not config.append_only and not in_top_comment and not was_in_quote and not import_section and not _is_comment_or_string_start(line) and not (line.rstrip().endswith(DOCSTRING_INDICATORS) and "=" not in line) ): add_line_separator = line_separator or "\n" import_section = add_line_separator.join(add_imports) + add_line_separator if end_of_file and index != 0: output_stream.write(add_line_separator) contains_imports = True add_imports = [] if next_import_section and not import_section: # pragma: no cover raw_import_section = import_section = next_import_section next_import_section = "" if import_section: if add_imports and (contains_imports or not config.append_only) and not indent: import_section = ( line_separator.join(add_imports) + line_separator + import_section ) contains_imports = True add_imports = [] if not indent: import_section += line raw_import_section += line if not contains_imports: output_stream.write(import_section) else: leading_whitespace = import_section[: -len(import_section.lstrip())] trailing_whitespace = import_section[len(import_section.rstrip()) :] if first_import_section and not import_section.lstrip( line_separator ).startswith(COMMENT_INDICATORS): import_section = import_section.lstrip(line_separator) raw_import_section = raw_import_section.lstrip(line_separator) first_import_section = False if indent: import_section = "".join( line[len(indent) :] if line.startswith(indent) else line for line in import_section.splitlines(keepends=True) ) parsed_content = parse.file_contents(import_section, config=config) verbose_output += parsed_content.verbose_output sorted_import_section = output.sorted_imports( parsed_content, _indented_config(config, indent), extension, import_type="cimport" if cimports else "import", ) if not (import_section.strip() and not sorted_import_section): if indent: sorted_import_section = ( leading_whitespace + textwrap.indent(sorted_import_section, indent).strip() + trailing_whitespace ) made_changes = made_changes or _has_changed( before=above_import_section + raw_import_section, after=sorted_import_section, line_separator=line_separator, ignore_whitespace=config.ignore_whitespace, ) output_stream.write(sorted_import_section) if not line and not indent and next_import_section: output_stream.write(line_separator) if indent: output_stream.write(line) if not next_import_section: indent = "" if next_import_section: cimports = next_cimports contains_imports = True else: contains_imports = False import_section = next_import_section next_import_section = "" else: output_stream.write(line) not_imports = False if stripped_line and not in_quote and not import_section and not next_import_section: if stripped_line == "yield": while not stripped_line or stripped_line == "yield": new_line = input_stream.readline() if not new_line: break output_stream.write(new_line) stripped_line = new_line.strip().split("#")[0] if stripped_line.startswith(("raise", "yield")): while stripped_line.endswith("\\"): new_line = input_stream.readline() if not new_line: break output_stream.write(new_line) stripped_line = new_line.strip().split("#")[0] if made_changes and config.only_modified: for output_str in verbose_output: print(output_str) return made_changes def _indented_config(config: Config, indent: str) -> Config: if not indent: return config return Config( config=config, line_length=max(config.line_length - len(indent), 0), wrap_length=max(config.wrap_length - len(indent), 0), lines_after_imports=1, import_headings=config.import_headings if config.indented_import_headings else {}, import_footers=config.import_footers if config.indented_import_headings else {}, ) def _has_changed(before: str, after: str, line_separator: str, ignore_whitespace: bool) -> bool: if ignore_whitespace: return ( remove_whitespace(before, line_separator=line_separator).strip() != remove_whitespace(after, line_separator=line_separator).strip() ) return before.rstrip() != after.rstrip() isort-9.0.1/isort/exceptions.py0000644000000000000000000001566213615410400013506 0ustar00"""All isort specific exception classes should be defined here""" from __future__ import annotations from functools import partial from pathlib import Path from .profiles import profiles class ISortError(Exception): """Base isort exception object from which all isort sourced exceptions should inherit""" def __reduce__(self) -> tuple[partial[ISortError], tuple[()]]: return (partial(type(self), **self.__dict__), ()) class InvalidSettingsPath(ISortError): """Raised when a settings path is provided that is neither a valid file or directory""" def __init__(self, settings_path: str | Path): super().__init__( f"isort was told to use the settings_path: {settings_path} as the base directory or " "file that represents the starting point of config file discovery, but it does not " "exist." ) self.settings_path = settings_path class ExistingSyntaxErrors(ISortError): """Raised when isort is told to sort imports within code that has existing syntax errors""" def __init__(self, file_path: str): super().__init__( f"isort was told to sort imports within code that contains syntax errors: {file_path}." ) self.file_path = file_path class IntroducedSyntaxErrors(ISortError): """Raised when isort has introduced a syntax error in the process of sorting imports""" def __init__(self, file_path: str): super().__init__( f"isort introduced syntax errors when attempting to sort the imports contained within " f"{file_path}." ) self.file_path = file_path class FileSkipped(ISortError): """Should be raised when a file is skipped for any reason""" def __init__(self, message: str, file_path: str): super().__init__(message) self.message = message self.file_path = file_path class FileSkipComment(FileSkipped): """Raised when an entire file is skipped due to a isort skip file comment""" def __init__(self, file_path: str, **kwargs: str): super().__init__( f"{file_path} contains a file skip comment and was skipped.", file_path=file_path ) class FileSkipSetting(FileSkipped): """Raised when an entire file is skipped due to provided isort settings""" def __init__(self, file_path: str, **kwargs: str): super().__init__( f"{file_path} was skipped as it's listed in 'skip' setting" " or matches a glob in 'skip_glob' setting", file_path=file_path, ) class ProfileDoesNotExist(ISortError): """Raised when a profile is set by the user that doesn't exist""" def __init__(self, profile: str): super().__init__( f"Specified profile of {profile} does not exist. " f"Available profiles: {','.join(profiles)}." ) self.profile = profile class SortingFunctionDoesNotExist(ISortError): """Raised when the specified sorting function isn't available""" def __init__(self, sort_order: str, available_sort_orders: list[str]): super().__init__( f"Specified sort_order of {sort_order} does not exist. " f"Available sort_orders: {','.join(available_sort_orders)}." ) self.sort_order = sort_order self.available_sort_orders = available_sort_orders class FormattingPluginDoesNotExist(ISortError): """Raised when a formatting plugin is set by the user that doesn't exist""" def __init__(self, formatter: str): super().__init__(f"Specified formatting plugin of {formatter} does not exist. ") self.formatter = formatter class LiteralParsingFailure(ISortError): """Raised when one of isorts literal sorting comments is used but isort can't parse the the given data structure. """ def __init__(self, code: str, original_error: Exception | type[Exception]): super().__init__( f"isort failed to parse the given literal {code}. It's important to note " "that isort literal sorting only supports simple literals parsable by " f"ast.literal_eval which gave the exception of {original_error}." ) self.code = code self.original_error = original_error class LiteralSortTypeMismatch(ISortError): """Raised when an isort literal sorting comment is used, with a type that doesn't match the supplied data structure's type. """ def __init__(self, kind: type, expected_kind: type): super().__init__( f"isort was told to sort a literal of type {expected_kind} but was given " f"a literal of type {kind}." ) self.kind = kind self.expected_kind = expected_kind class AssignmentsFormatMismatch(ISortError): """Raised when isort is told to sort assignments but the format of the assignment section doesn't match isort's expectation. """ def __init__(self, code: str): super().__init__( "isort was told to sort a section of assignments, however the given code:\n\n" f"{code}\n\n" "Does not match isort's strict single line formatting requirement for assignment " "sorting:\n\n" "{variable_name} = {value}\n" "{variable_name2} = {value2}\n" "...\n\n" ) self.code = code class UnsupportedSettings(ISortError): """Raised when settings are passed into isort (either from config, CLI, or runtime) that it doesn't support. """ @staticmethod def _format_option(name: str, value: object, source: object) -> str: return f"\t- {name} = {value} (source: '{source}')" def __init__(self, unsupported_settings: dict[str, dict[str, object]]): errors = "\n".join( self._format_option(name, **option) for name, option in unsupported_settings.items() ) super().__init__( "isort was provided settings that it doesn't support:\n\n" f"{errors}\n\n" "For a complete and up-to-date listing of supported settings see: " "https://isort.readthedocs.io/en/latest/configuration/options.html.\n" ) self.unsupported_settings = unsupported_settings class UnsupportedEncoding(ISortError): """Raised when isort encounters an encoding error while trying to read a file""" def __init__(self, filename: str | Path): super().__init__(f"Unknown or unsupported encoding in {filename}") self.filename = filename class MissingSection(ISortError): """Raised when isort encounters an import that matches a section that is not defined""" def __init__(self, import_module: str, section: str): super().__init__( f"Found {import_module} import while parsing, but {section} was not included " "in the `sections` setting of your config. Please add it before continuing\n" "See https://isort.readthedocs.io/en/latest/index.html#custom-sections-and-ordering " "for more info." ) isort-9.0.1/isort/files.py0000644000000000000000000000313613615410400012420 0ustar00import os from collections.abc import Iterable, Iterator from pathlib import Path from isort.settings import Config def find( paths: Iterable[str | Path], config: Config, skipped: list[str], broken: list[str] ) -> Iterator[str | Path]: """Finds and provides an iterator for all Python source files defined in paths.""" visited_dirs: set[Path] = set() for path in paths: if os.path.isdir(path): for dirpath, dirnames, filenames in os.walk( path, topdown=True, followlinks=config.follow_links ): base_path = Path(dirpath) for dirname in list(dirnames): full_path = base_path / dirname resolved_path = full_path.resolve() if config.is_skipped(full_path): skipped.append(str(full_path)) dirnames.remove(dirname) else: if resolved_path in visited_dirs: # pragma: no cover dirnames.remove(dirname) visited_dirs.add(resolved_path) for filename in filenames: filepath = os.path.join(dirpath, filename) if config.is_supported_filetype(filepath): if config.is_skipped(Path(os.path.abspath(filepath))): skipped.append(os.path.abspath(filepath)) else: yield filepath elif not os.path.exists(path): broken.append(str(path)) else: yield path isort-9.0.1/isort/format.py0000644000000000000000000001255713615410400012615 0ustar00import re import sys from datetime import datetime from difflib import unified_diff from pathlib import Path from typing import TextIO try: import colorama except ImportError: colorama_unavailable = True else: colorama_unavailable = False ADDED_LINE_PATTERN = re.compile(r"\+[^+]") REMOVED_LINE_PATTERN = re.compile(r"-[^-]") def format_simplified(import_line: str) -> str: import_line = import_line.strip() if import_line.startswith("from "): import_line = import_line.replace("from ", "") import_line = import_line.replace(" import ", ".") elif import_line.startswith("import "): import_line = import_line.replace("import ", "") return import_line def format_natural(import_line: str) -> str: import_line = import_line.strip() if not import_line.startswith("from ") and not import_line.startswith("import "): if "." not in import_line: return f"import {import_line}" parts = import_line.split(".") end = parts.pop(-1) return f"from {'.'.join(parts)} import {end}" return import_line def show_unified_diff( *, file_input: str, file_output: str, file_path: Path | None, output: TextIO | None = None, color_output: bool = False, ) -> None: """Shows a unified_diff for the provided input and output against the provided file path. - **file_input**: A string that represents the contents of a file before changes. - **file_output**: A string that represents the contents of a file after changes. - **file_path**: A Path object that represents the file path of the file being changed. - **output**: A stream to output the diff to. If non is provided uses sys.stdout. - **color_output**: Use color in output if True. """ printer = create_terminal_printer(color_output, output) file_name = "" if file_path is None else str(file_path) file_mtime = str( datetime.now() if file_path is None else datetime.fromtimestamp(file_path.stat().st_mtime) ) unified_diff_lines = unified_diff( file_input.splitlines(keepends=True), file_output.splitlines(keepends=True), fromfile=file_name + ":before", tofile=file_name + ":after", fromfiledate=file_mtime, tofiledate=str(datetime.now()), ) for line in unified_diff_lines: printer.diff_line(line) def ask_whether_to_apply_changes_to_file(file_path: str) -> bool: answer = None while answer not in ("yes", "y", "no", "n", "quit", "q"): answer = input(f"Apply suggested changes to '{file_path}' [y/n/q]? ") # nosec answer = answer.lower() if answer in ("no", "n"): return False if answer in ("quit", "q"): sys.exit(1) return True def remove_whitespace(content: str, line_separator: str = "\n") -> str: content = ( content.replace(line_separator, "").replace(" ", "").replace("\t", "").replace("\f", "") ) return content class BasicPrinter: ERROR = "ERROR" SUCCESS = "SUCCESS" def __init__(self, error: str, success: str, output: TextIO | None = None): self.output = output or sys.stdout self.success_message = success self.error_message = error def success(self, message: str) -> None: print(self.success_message.format(success=self.SUCCESS, message=message), file=self.output) def error(self, message: str) -> None: print(self.error_message.format(error=self.ERROR, message=message), file=sys.stderr) def diff_line(self, line: str) -> None: self.output.write(line) class ColoramaPrinter(BasicPrinter): def __init__(self, error: str, success: str, output: TextIO | None): super().__init__(error, success, output=output) # Note: this constants are instance variables instead ofs class variables # because they refer to colorama which might not be installed. self.ERROR = self.style_text("ERROR", colorama.Fore.RED) self.SUCCESS = self.style_text("SUCCESS", colorama.Fore.GREEN) self.ADDED_LINE = colorama.Fore.GREEN self.REMOVED_LINE = colorama.Fore.RED @staticmethod def style_text(text: str, style: str | None = None) -> str: if style is None: return text return style + text + str(colorama.Style.RESET_ALL) def diff_line(self, line: str) -> None: style = None if re.match(ADDED_LINE_PATTERN, line): style = self.ADDED_LINE elif re.match(REMOVED_LINE_PATTERN, line): style = self.REMOVED_LINE self.output.write(self.style_text(line, style)) def create_terminal_printer( color: bool, output: TextIO | None = None, error: str = "", success: str = "" ) -> BasicPrinter: if color and colorama_unavailable: no_colorama_message = ( "\n" "Sorry, but to use --color (color_output) the colorama python package is required.\n\n" "Reference: https://pypi.org/project/colorama/\n\n" "You can either install it separately on your system or as the colors extra " "for isort. Ex: \n\n" "$ pip install isort[colors]\n" ) print(no_colorama_message, file=sys.stderr) sys.exit(1) if not colorama_unavailable: colorama.init(strip=False) return ( ColoramaPrinter(error, success, output) if color else BasicPrinter(error, success, output) ) isort-9.0.1/isort/hooks.py0000644000000000000000000000707413615410400012446 0ustar00"""Defines a git hook to allow pre-commit warnings and errors about import order. usage: exit_code = git_hook(strict=True|False, modify=True|False) """ import os import subprocess # nosec from pathlib import Path from isort import Config, api, exceptions def get_output(command: list[str]) -> str: """Run a command and return raw output :param str command: the command to run :returns: the stdout output of the command """ result = subprocess.run(command, stdout=subprocess.PIPE, check=True) # nosec return result.stdout.decode() def get_lines(command: list[str]) -> list[str]: """Run a command and return lines of output :param str command: the command to run :returns: list of whitespace-stripped lines output by command """ stdout = get_output(command) return [line.strip() for line in stdout.splitlines()] def git_hook( strict: bool = False, modify: bool = False, lazy: bool = False, settings_file: str = "", directories: list[str] | None = None, ) -> int: """Git pre-commit hook to check staged files for isort errors :param bool strict - if True, return number of errors on exit, causing the hook to fail. If False, return zero so it will just act as a warning. :param bool modify - if True, fix the sources if they are not sorted properly. If False, only report result without modifying anything. :param bool lazy - if True, also check/fix unstaged files. This is useful if you frequently use ``git commit -a`` for example. If False, only check/fix the staged files for isort errors. :param str settings_file - A path to a file to be used as the configuration file for this run. When settings_file is the empty string, the configuration file will be searched starting at the directory containing the first staged file, if any, and going upward in the directory structure. :param list[str] directories - A list of directories to restrict the hook to. :return number of errors if in strict mode, 0 otherwise. """ # Get list of modified files which are staged for commit... diff_cmd = ["git", "diff-index", "--cached", "--name-only", "--diff-filter=ACMRTUXB", "HEAD"] if lazy: # ...or *all* modified files if lazy=True diff_cmd.remove("--cached") if directories: diff_cmd.extend(directories) files_modified = get_lines(diff_cmd) if not files_modified: return 0 errors = 0 config = Config( settings_file=settings_file, settings_path=os.path.dirname(os.path.abspath(files_modified[0])), ) for filename in files_modified: if filename.endswith(".py"): try: check_passed = False if lazy: # Check the file as-is on disk check_passed = api.check_file(filename, config=config) else: # Read and check the git *staged* contents of the file staged_cmd = ["git", "show", f":{filename}"] staged_contents = get_output(staged_cmd) check_passed = api.check_code_string( staged_contents, file_path=Path(filename), config=config ) if not check_passed: errors += 1 if modify: api.sort_file(filename, config=config) except exceptions.FileSkipped: # pragma: no cover pass return errors if strict else 0 isort-9.0.1/isort/identify.py0000644000000000000000000001421013615410400013124 0ustar00"""Fast stream based import identification. Eventually this will likely replace parse.py """ from collections.abc import Iterator from functools import partial from pathlib import Path from typing import NamedTuple, TextIO from ._parse_utils import ( collect_import_continuation, import_type, normalize_from_import_string, normalize_line, skip_line, strip_syntax, ) from .comments import parse as parse_comments from .settings import DEFAULT_CONFIG, Config STATEMENT_DECLARATIONS: tuple[str, ...] = ("def ", "cdef ", "cpdef ", "class ", "@", "async def") class Import(NamedTuple): line_number: int indented: bool module: str attribute: str | None = None alias: str | None = None cimport: bool = False file_path: Path | None = None def statement(self) -> str: import_cmd = "cimport" if self.cimport else "import" if self.attribute: import_string = f"from {self.module} {import_cmd} {self.attribute}" else: import_string = f"{import_cmd} {self.module}" if self.alias: import_string += f" as {self.alias}" return import_string def __str__(self) -> str: return ( f"{self.file_path or ''}:{self.line_number} " f"{'indented ' if self.indented else ''}{self.statement()}" ) # Ignore DeepSource cyclomatic complexity check for this function. # skipcq: PY-R1000 def imports( input_stream: TextIO, config: Config = DEFAULT_CONFIG, file_path: Path | None = None, top_only: bool = False, ) -> Iterator[Import]: """Parses a python file taking out and categorizing imports.""" in_quote = "" indexed_input = enumerate(input_stream) for index, raw_line in indexed_input: (skipping_line, in_quote) = skip_line(raw_line, in_quote=in_quote) if top_only and not in_quote and raw_line.startswith(STATEMENT_DECLARATIONS): break if skipping_line: continue stripped_line = raw_line.strip().split("#")[0] if stripped_line.startswith(("raise", "yield")): if stripped_line == "yield": while not stripped_line or stripped_line == "yield": try: index, next_line = next(indexed_input) except StopIteration: break stripped_line = next_line.strip().split("#")[0] while stripped_line.endswith("\\"): try: index, next_line = next(indexed_input) except StopIteration: break stripped_line = next_line.strip().split("#")[0] continue # pragma: no cover line, *end_of_line_comment = raw_line.split("#", 1) statements = [line.strip() for line in line.split(";")] if end_of_line_comment: statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" for statement in statements: line, _raw_line = normalize_line(statement) type_of_import = import_type(line, config) if type_of_import is None: continue # pragma: no cover import_string, _ = parse_comments(line) identified_import = partial( Import, index + 1, # line numbers use 1 based indexing raw_line.startswith((" ", "\t")), file_path=file_path, ) _, import_string, _ = collect_import_continuation( line, import_string, # We can disregard `index` here because it is no longer accessed after this line. lambda: parse_comments(next(indexed_input)[1]), ) if type_of_import == "from": import_string = normalize_from_import_string(import_string) cimports: bool = " cimport " in import_string or import_string.startswith("cimport") identified_import = partial(identified_import, cimport=cimports) just_imports = [ item.replace("{|", "{ ").replace("|}", " }") for item in strip_syntax(import_string).split() ] direct_imports = just_imports[1:] top_level_module = "" if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports): while "as" in just_imports: attribute = None as_index = just_imports.index("as") if type_of_import == "from": attribute = just_imports[as_index - 1] top_level_module = just_imports[0] module = top_level_module + "." + attribute alias = just_imports[as_index + 1] direct_imports.remove(attribute) direct_imports.remove(alias) direct_imports.remove("as") just_imports[1:] = direct_imports if attribute == alias and config.remove_redundant_aliases: yield identified_import(top_level_module, attribute) else: yield identified_import(top_level_module, attribute, alias=alias) else: module = just_imports[as_index - 1] alias = just_imports[as_index + 1] just_imports.remove(alias) just_imports.remove("as") just_imports.remove(module) if module == alias and config.remove_redundant_aliases: yield identified_import(module) else: yield identified_import(module, alias=alias) if just_imports: if type_of_import == "from": module = just_imports.pop(0) for attribute in just_imports: yield identified_import(module, attribute) else: for module in just_imports: yield identified_import(module) isort-9.0.1/isort/io.py0000644000000000000000000000366413615410400011733 0ustar00"""Defines any IO utilities used by isort""" import dataclasses import tokenize from collections.abc import Callable, Iterator from contextlib import contextmanager from io import BytesIO, StringIO, TextIOWrapper from pathlib import Path from typing import TextIO from isort.exceptions import UnsupportedEncoding @dataclasses.dataclass(frozen=True) class File: stream: TextIO path: Path encoding: str @staticmethod def detect_encoding(filename: str | Path, readline: Callable[[], bytes]) -> str: try: return tokenize.detect_encoding(readline)[0] except Exception: raise UnsupportedEncoding(filename) @staticmethod def from_contents(contents: str, filename: str) -> "File": encoding = File.detect_encoding(filename, BytesIO(contents.encode("utf-8")).readline) return File(stream=StringIO(contents), path=Path(filename).resolve(), encoding=encoding) @property def extension(self) -> str: return self.path.suffix.lstrip(".") @staticmethod def _open(filename: str | Path) -> TextIOWrapper: """Open a file in read only mode using the encoding detected by detect_encoding(). """ buffer = open(filename, "rb") try: encoding = File.detect_encoding(filename, buffer.readline) buffer.seek(0) text = TextIOWrapper(buffer, encoding, line_buffering=True, newline="") text.mode = "r" # type: ignore[misc] return text except Exception: buffer.close() raise @staticmethod @contextmanager def read(filename: str | Path) -> Iterator["File"]: file_path = Path(filename).resolve() stream = None try: stream = File._open(file_path) yield File(stream=stream, path=file_path, encoding=stream.encoding) finally: if stream is not None: stream.close() isort-9.0.1/isort/literal.py0000644000000000000000000001403013615410400012745 0ustar00import ast from collections.abc import Callable from typing import Any from isort.exceptions import ( AssignmentsFormatMismatch, LiteralParsingFailure, LiteralSortTypeMismatch, ) from isort.settings import DEFAULT_CONFIG, Config type_mapping: dict[str, tuple[type, Callable[[Any, Config, int], str]]] = {} def assignments(code: str) -> str: values = {} for line in code.splitlines(keepends=True): if not line.strip(): continue if " = " not in line: raise AssignmentsFormatMismatch(code) variable_name, value = line.split(" = ", 1) values[variable_name] = value return "".join( f"{variable_name} = {values[variable_name]}" for variable_name in sorted(values.keys()) ) def assignment(code: str, sort_type: str, extension: str, config: Config = DEFAULT_CONFIG) -> str: """Sorts the literal present within the provided code against the provided sort type, returning the sorted representation of the source code. """ if sort_type == "assignments": return assignments(code) if sort_type not in type_mapping: raise ValueError( "Trying to sort using an undefined sort_type. " f"Defined sort types are {', '.join(type_mapping.keys())}." ) variable_name, literal = code.split("=") variable_name = variable_name.strip() literal = literal.lstrip() try: value = ast.literal_eval(literal) except Exception as error: raise LiteralParsingFailure(code, error) expected_type, sort_function = type_mapping[sort_type] if type(value) is not expected_type: raise LiteralSortTypeMismatch(type(value), expected_type) prefix_length = len(f"{variable_name} = ") sorted_value_code = f"{variable_name} = {sort_function(value, config, prefix_length)}" if config.formatting_function: sorted_value_code = config.formatting_function( sorted_value_code, extension, config ).rstrip() sorted_value_code += code[len(code.rstrip()) :] return sorted_value_code def register_type( name: str, kind: type ) -> Callable[[Callable[[Any, Config, int], str]], Callable[[Any, Config, int], str]]: """Registers a new literal sort type.""" def wrap( function: Callable[[Any, Config, int], str], ) -> Callable[[Any, Config, int], str]: type_mapping[name] = (kind, function) return function return wrap def _black_quote(value: str) -> str: """Quote a string the way black does: prefer double quotes, fall back to single only when it avoids escaping. Values with backslashes or control characters defer to repr() so requoting can never produce invalid source. """ if any(char in value for char in ("\\", "\n", "\r", "\t")): # deliberate: repr() sacrifices black-quote-normalization here to guarantee valid source return repr(value) if '"' in value and "'" not in value: return f"'{value}'" if '"' not in value: return f'"{value}"' return '"' + value.replace('"', '\\"') + '"' def _repr_element(value: Any) -> str: """Render a single sorted element: strings via black's quote rule, everything else via repr() (so ints and other literals in ``# isort: list`` etc. keep working). """ if isinstance(value, str): return _black_quote(value) return repr(value) def _format_collection( elements: list[str], open_bracket: str, close_bracket: str, config: Config, prefix_length: int, single_element_comma: bool = False, ) -> str: """Render already-rendered, sorted ``elements`` as ``open ... close`` honoring the config: a single line when it fits within ``line_length`` (accounting for the ``prefix_length`` of the `` = `` prefix), otherwise a vertical-hanging-indent block. ``single_element_comma`` forces the mandatory trailing comma that a one-element tuple needs to stay a tuple. """ only_element_needs_comma = single_element_comma and len(elements) == 1 inner = ", ".join(elements) if only_element_needs_comma: inner += "," single_line = f"{open_bracket}{inner}{close_bracket}" if prefix_length + len(single_line) <= config.line_length: return single_line indent = config.indent trailing = "," if (config.include_trailing_comma or only_element_needs_comma) else "" body = (",\n" + indent).join(elements) return f"{open_bracket}\n{indent}{body}{trailing}\n{close_bracket}" @register_type("dict", dict) def _dict(value: dict[Any, Any], config: Config, prefix_length: int) -> str: items = [ f"{_repr_element(key)}: {_repr_element(item)}" for key, item in sorted(value.items(), key=lambda item: item[1]) ] return _format_collection(items, "{", "}", config, prefix_length) @register_type("list", list) def _list(value: list[Any], config: Config, prefix_length: int) -> str: elements = [_repr_element(item) for item in sorted(value)] return _format_collection(elements, "[", "]", config, prefix_length) @register_type("unique-list", list) def _unique_list(value: list[Any], config: Config, prefix_length: int) -> str: elements = [_repr_element(item) for item in sorted(set(value))] return _format_collection(elements, "[", "]", config, prefix_length) @register_type("set", set) def _set(value: set[Any], config: Config, prefix_length: int) -> str: elements = [_repr_element(item) for item in sorted(value)] return _format_collection(elements, "{", "}", config, prefix_length) @register_type("tuple", tuple) def _tuple(value: tuple[Any, ...], config: Config, prefix_length: int) -> str: elements = [_repr_element(item) for item in sorted(value)] return _format_collection(elements, "(", ")", config, prefix_length, single_element_comma=True) @register_type("unique-tuple", tuple) def _unique_tuple(value: tuple[Any, ...], config: Config, prefix_length: int) -> str: elements = [_repr_element(item) for item in sorted(set(value))] return _format_collection(elements, "(", ")", config, prefix_length, single_element_comma=True) isort-9.0.1/isort/logo.py0000644000000000000000000000061413615410400012254 0ustar00from ._version import _VERSION_STRING ASCII_ART = rf""" _ _ (_) ___ ___ _ __| |_ | |/ _/ / _ \/ '__ _/ | |\__ \/\_\/| | | |_ |_|\___/\___/\_/ \_/ isort your imports, so you don't have to. VERSION {_VERSION_STRING} """ __doc__ = f""" ```python {ASCII_ART} ``` """ isort-9.0.1/isort/main.py0000644000000000000000000013161713615410400012250 0ustar00"""Tool for sorting imports alphabetically, and automatically separated into sections.""" from __future__ import annotations import argparse import functools import json import os import sys from collections.abc import Iterator, Sequence from contextlib import AbstractContextManager, nullcontext from dataclasses import asdict from gettext import gettext as _ from io import TextIOWrapper from pathlib import Path from typing import Any from warnings import warn from . import api, files, sections from ._version import _VERSION_STRING from .exceptions import FileSkipped, ISortError, UnsupportedEncoding from .format import create_terminal_printer from .logo import ASCII_ART from .profiles import profiles from .settings import VALID_PY_TARGETS, Config, find_all_configs from .utils import Trie from .wrap_modes import WrapModes QUICK_GUIDE = f""" {ASCII_ART} Nothing to do: no files or paths have been passed in! Try one of the following: `isort .` - sort all Python files, starting from the current directory, recursively. `isort . --interactive` - Do the same, but ask before making any changes. `isort . --check --diff` - Check to see if imports are correctly sorted within this project. `isort --help` - In-depth information about isort's available command-line options. Visit https://isort.readthedocs.io/ for complete information about how to use isort. """ class SortAttempt: def __init__(self, incorrectly_sorted: bool, skipped: bool, supported_encoding: bool) -> None: self.incorrectly_sorted = incorrectly_sorted self.skipped = skipped self.supported_encoding = supported_encoding def __reduce__(self) -> tuple[type[SortAttempt], tuple[bool, bool, bool]]: # Defined explicitly as mypyc's removal of `__dict__` breaks pickling this class, which is # necessary when using multiple jobs. return (self.__class__, (self.incorrectly_sorted, self.skipped, self.supported_encoding)) def sort_imports( file_name: str | Path, config: Config, check: bool = False, ask_to_apply: bool = False, write_to_stdout: bool = False, **kwargs: Any, ) -> SortAttempt | None: incorrectly_sorted: bool = False skipped: bool = False try: if check: try: incorrectly_sorted = not api.check_file(file_name, config=config, **kwargs) except FileSkipped: skipped = True return SortAttempt(incorrectly_sorted, skipped, True) try: incorrectly_sorted = not api.sort_file( file_name, config=config, ask_to_apply=ask_to_apply, write_to_stdout=write_to_stdout, **kwargs, ) except FileSkipped: skipped = True return SortAttempt(incorrectly_sorted, skipped, True) except (OSError, ValueError) as error: warn(f"Unable to parse file {file_name} due to {error}", stacklevel=2) return None except UnsupportedEncoding: if config.verbose: warn(f"Encoding not supported for {file_name}", stacklevel=2) return SortAttempt(incorrectly_sorted, skipped, False) except ISortError as error: _print_hard_fail(config, message=str(error)) sys.exit(1) except Exception: _print_hard_fail(config, offending_file=file_name) raise def _print_hard_fail( config: Config, offending_file: str | Path | None = None, message: str | None = None ) -> None: """Fail on unrecoverable exception with custom message.""" message = message or ( f"Unrecoverable exception thrown when parsing {offending_file or ''}! " "This should NEVER happen.\n" "If encountered, please open an issue: https://github.com/PyCQA/isort/issues/new" ) printer = create_terminal_printer( color=config.color_output, error=config.format_error, success=config.format_success ) printer.error(message) def _build_arg_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( description="Sort Python import definitions alphabetically " "within logical sections. Run with no arguments to see a quick " "start guide, otherwise, one or more files/directories/stdin must be provided. " "Use `-` as the first argument to represent stdin. Use --interactive to use the pre 5.0.0 " "interactive behavior." " " "If you've used isort 4 but are new to isort 5, see the upgrading guide: " "https://isort.readthedocs.io/en/latest/upgrade_guides/5.0.0.html", add_help=False, # prevent help option from appearing in "optional arguments" group ) general_group = parser.add_argument_group("general options") target_group = parser.add_argument_group("target options") output_group = parser.add_argument_group("general output options") inline_args_group = output_group.add_mutually_exclusive_group() section_group = parser.add_argument_group("section output options") general_group.add_argument( "-h", "--help", action="help", default=argparse.SUPPRESS, help=_("show this help message and exit"), ) general_group.add_argument( "-V", "--version", action="store_true", dest="show_version", help="Displays the currently installed version of isort.", ) general_group.add_argument( "--vn", "--version-number", action="version", version=_VERSION_STRING, help="Returns just the current version number without the logo", ) general_group.add_argument( "-v", "--verbose", action="store_true", dest="verbose", help="Shows verbose output, such as when files are skipped or when a check is successful.", ) general_group.add_argument( "--only-modified", "--om", dest="only_modified", action="store_true", help="Suppresses verbose output for non-modified files.", ) general_group.add_argument( "--dedup-headings", dest="dedup_headings", action="store_true", help="Tells isort to only show an identical custom import heading comment once, even if" " there are multiple sections with the comment set.", ) general_group.add_argument( "-q", "--quiet", action="store_true", dest="quiet", help="Shows extra quiet output, only errors are outputted.", ) general_group.add_argument( "-d", "--stdout", help="Force resulting output to stdout, instead of in-place.", dest="write_to_stdout", action="store_true", ) general_group.add_argument( "--overwrite-in-place", help="Tells isort to overwrite in place using the same file handle. " "Comes at a performance and memory usage penalty over its standard " "approach but ensures all file flags and modes stay unchanged.", dest="overwrite_in_place", action="store_true", ) general_group.add_argument( "--show-config", dest="show_config", action="store_true", help="See isort's determined config, as well as sources of config options.", ) general_group.add_argument( "--show-files", dest="show_files", action="store_true", help="See the files isort will be run against with the current config options.", ) general_group.add_argument( "--df", "--diff", dest="show_diff", action="store_true", help="Prints a diff of all the changes isort would make to a file, instead of " "changing it in place", ) general_group.add_argument( "-c", "--check-only", "--check", action="store_true", dest="check", help="Checks the file for unsorted / unformatted imports and prints them to the " "command line without modifying the file. Returns 0 when nothing would change and " "returns 1 when the file would be reformatted.", ) general_group.add_argument( "--ws", "--ignore-whitespace", action="store_true", dest="ignore_whitespace", help="Tells isort to ignore whitespace differences when --check-only is being used.", ) general_group.add_argument( "--sp", "--settings-path", "--settings-file", "--settings", dest="settings_path", help="Explicitly set the settings path or file instead of auto determining " "based on file location.", ) general_group.add_argument( "--cr", "--config-root", dest="config_root", help="Explicitly set the config root for resolving all configs. When used " "with the --resolve-all-configs flag, isort will look at all sub-folders " "in this config root to resolve config files and sort files based on the " "closest available config(if any)", ) general_group.add_argument( "--resolve-all-configs", dest="resolve_all_configs", action="store_true", help="Tells isort to resolve the configs for all sub-directories " "and sort files in terms of its closest config files.", ) general_group.add_argument( "--profile", dest="profile", type=str, help="Base profile type to use for configuration. " f"Profiles include: {', '.join(profiles.keys())}. As well as any shared profiles.", ) general_group.add_argument( "-j", "--jobs", help="Number of files to process in parallel. Negative value means use number of CPUs.", dest="jobs", type=int, nargs="?", const=-1, ) general_group.add_argument( "--ac", "--atomic", dest="atomic", action="store_true", help="Ensures the output doesn't save if the resulting file contains syntax errors.", ) general_group.add_argument( "--interactive", dest="ask_to_apply", action="store_true", help="Tells isort to apply changes interactively.", ) general_group.add_argument( "--format-error", dest="format_error", help="Override the format used to print errors.", ) general_group.add_argument( "--format-success", dest="format_success", help="Override the format used to print success.", ) general_group.add_argument( "--srx", "--sort-reexports", dest="sort_reexports", action="store_true", help="Automatically sort all re-exports (module level __all__ collections)", ) target_group.add_argument( "files", nargs="*", help="One or more Python source files that need their imports sorted." ) target_group.add_argument( "--filter-files", dest="filter_files", action="store_true", help="Tells isort to filter files even when they are explicitly passed in as " "part of the CLI command.", ) target_group.add_argument( "-s", "--skip", help="Files that isort should skip over. If you want to skip multiple " "files you should specify twice: --skip file1 --skip file2. Values can be " "file names, directory names or file paths. To skip all files in a nested path " "use --skip-glob.", dest="skip", action="append", ) target_group.add_argument( "--extend-skip", help="Extends --skip to add additional files that isort should skip over. " "If you want to skip multiple " "files you should specify twice: --skip file1 --skip file2. Values can be " "file names, directory names or file paths. To skip all files in a nested path " "use --skip-glob.", dest="extend_skip", action="append", ) target_group.add_argument( "--sg", "--skip-glob", help="Files that isort should skip over.", dest="skip_glob", action="append", ) target_group.add_argument( "--extend-skip-glob", help="Additional files that isort should skip over (extending --skip-glob).", dest="extend_skip_glob", action="append", ) target_group.add_argument( "--gitignore", "--skip-gitignore", action="store_true", dest="skip_gitignore", help="Treat project as a git repository and ignore files listed in .gitignore." "\nNOTE: This requires git to be installed and accessible from the same shell as isort.", ) target_group.add_argument( "--ext", "--extension", "--supported-extension", dest="supported_extensions", action="append", help="Specifies what extensions isort can be run against.", ) target_group.add_argument( "--blocked-extension", dest="blocked_extensions", action="append", help="Specifies what extensions isort can never be run against.", ) target_group.add_argument( "--dont-follow-links", dest="dont_follow_links", action="store_true", help="Tells isort not to follow symlinks that are encountered when running recursively.", ) target_group.add_argument( "--filename", dest="filename", help="Provide the filename associated with a stream.", ) target_group.add_argument( "--allow-root", action="store_true", default=False, help="Tells isort not to treat / specially, allowing it to be run against the root dir.", ) output_group.add_argument( "-a", "--add-import", dest="add_imports", action="append", help="Adds the specified import line to all files, " "automatically determining correct placement.", ) output_group.add_argument( "--append", "--append-only", dest="append_only", action="store_true", help="Only adds the imports specified in --add-import if the file" " contains existing imports.", ) output_group.add_argument( "--af", "--force-adds", dest="force_adds", action="store_true", help="Forces import adds even if the original file is empty.", ) output_group.add_argument( "--rm", "--remove-import", dest="remove_imports", action="append", help="Removes the specified import from all files.", ) output_group.add_argument( "--float-to-top", dest="float_to_top", action="store_true", help="Causes all non-indented imports to float to the top of the file having its imports " "sorted (immediately below the top of file comment).\n" "This can be an excellent shortcut for collecting imports every once in a while " "when you place them in the middle of a file to avoid context switching.\n\n" "*NOTE*: It currently doesn't work with cimports and introduces some extra over-head " "and a performance penalty.", ) output_group.add_argument( "--dont-float-to-top", dest="dont_float_to_top", action="store_true", help="Forces --float-to-top setting off. See --float-to-top for more information.", ) output_group.add_argument( "--ca", "--combine-as", dest="combine_as_imports", action="store_true", help="Combines as imports on the same line.", ) output_group.add_argument( "--cs", "--combine-star", dest="combine_star", action="store_true", help="Ensures that if a star import is present, " "nothing else is imported from that namespace.", ) output_group.add_argument( "-e", "--balanced", dest="balanced_wrapping", action="store_true", help="Balances wrapping to produce the most consistent line length possible", ) output_group.add_argument( "--ff", "--from-first", dest="from_first", action="store_true", help="Switches the typical ordering preference, " "showing from imports first then straight ones.", ) output_group.add_argument( "--fgw", "--force-grid-wrap", nargs="?", const=2, type=int, dest="force_grid_wrap", help="Force number of from imports (defaults to 2 when passed as CLI flag without value) " "to be grid wrapped regardless of line " "length. If 0 is passed in (the global default) only line length is considered.", ) output_group.add_argument( "--fs", "--forced-separate", dest="forced_separate", action="append", help="Force specified sub modules to show separately. To specify multiple modules, " "use the argument multiple times: --forced-separate typing --forced-separate six.", ) output_group.add_argument( "-i", "--indent", help='String to place for indents defaults to " " (4 spaces).', dest="indent", type=str, ) output_group.add_argument( "--lbi", "--lines-before-imports", dest="lines_before_imports", type=int ) output_group.add_argument( "--lai", "--lines-after-imports", dest="lines_after_imports", type=int ) output_group.add_argument( "--lbt", "--lines-between-types", dest="lines_between_types", type=int ) output_group.add_argument( "--le", "--line-ending", dest="line_ending", help="Forces line endings to the specified value. " "If not set, values will be guessed per-file.", ) output_group.add_argument( "--ls", "--length-sort", help="Sort imports by their string length.", dest="length_sort", action="store_true", ) output_group.add_argument( "--lss", "--length-sort-straight", help="Sort straight imports by their string length. Similar to `length_sort` " "but applies only to straight imports and doesn't affect from imports.", dest="length_sort_straight", action="store_true", ) output_group.add_argument( "-m", "--multi-line", dest="multi_line_output", choices=list(WrapModes.__members__.keys()) + [str(mode.value) for mode in WrapModes.__members__.values()], type=str, help="Multi line output (0-grid, 1-vertical, 2-hanging, 3-vert-hanging, 4-vert-grid, " "5-vert-grid-grouped, 6-deprecated-alias-for-5, 7-noqa, " "8-vertical-hanging-indent-bracket, 9-vertical-prefix-from-module-import, " "10-hanging-indent-with-parentheses).", ) output_group.add_argument( "-n", "--ensure-newline-before-comments", dest="ensure_newline_before_comments", action="store_true", help="Inserts a blank line before a comment following an import.", ) inline_args_group.add_argument( "--nis", "--no-inline-sort", dest="no_inline_sort", action="store_true", help="Leaves `from` imports with multiple imports 'as-is' " "(e.g. `from foo import a, c ,b`).", ) output_group.add_argument( "--ot", "--order-by-type", dest="order_by_type", action="store_true", help="Order imports by type, which is determined by case, in addition to alphabetically.\n" "\n**NOTE**: type here refers to the implied type from the import name capitalization.\n" ' isort does not do type introspection for the imports. These "types" are simply: ' "CONSTANT_VARIABLE, CamelCaseClass, variable_or_function. If your project follows PEP8" " or a related coding standard and has many imports this is a good default, otherwise you " "likely will want to turn it off. From the CLI the `--dont-order-by-type` option will turn " "this off.", ) output_group.add_argument( "--dt", "--dont-order-by-type", dest="dont_order_by_type", action="store_true", help="Don't order imports by type, which is determined by case, in addition to " "alphabetically.\n\n" "**NOTE**: type here refers to the implied type from the import name capitalization.\n" ' isort does not do type introspection for the imports. These "types" are simply: ' "CONSTANT_VARIABLE, CamelCaseClass, variable_or_function. If your project follows PEP8" " or a related coding standard and has many imports this is a good default. You can turn " "this on from the CLI using `--order-by-type`.", ) output_group.add_argument( "--rr", "--reverse-relative", dest="reverse_relative", action="store_true", help="Reverse order of relative imports.", ) output_group.add_argument( "--reverse-sort", dest="reverse_sort", action="store_true", help="Reverses the ordering of imports.", ) output_group.add_argument( "--sort-order", dest="sort_order", help="Specify sorting function. Can be built in (natural[default] = force numbers " "to be sequential, native = Python's built-in sorted function) or an installable plugin.", ) inline_args_group.add_argument( "--sl", "--force-single-line-imports", dest="force_single_line", action="store_true", help="Forces all from imports to appear on their own line", ) output_group.add_argument( "--nsl", "--single-line-exclusions", help="One or more modules to exclude from the single line rule.", dest="single_line_exclusions", action="append", ) output_group.add_argument( "--tc", "--trailing-comma", dest="include_trailing_comma", action="store_true", help="Includes a trailing comma on multi line imports that include parentheses.", ) output_group.add_argument( "--up", "--use-parentheses", dest="use_parentheses", action="store_true", help="Use parentheses for line continuation on length limit instead of slashes." " **NOTE**: This is separate from wrap modes, and only affects how individual lines that " " are too long get continued, not sections of multiple imports.", ) output_group.add_argument( "-l", "-w", "--line-length", "--line-width", help="The max length of an import line (used for wrapping long imports).", dest="line_length", type=int, ) output_group.add_argument( "--wl", "--wrap-length", dest="wrap_length", type=int, help="Specifies how long lines that are wrapped should be, if not set line_length is used." "\nNOTE: wrap_length must be LOWER than or equal to line_length.", ) output_group.add_argument( "--case-sensitive", dest="case_sensitive", action="store_true", help="Tells isort to include casing when sorting module names", ) output_group.add_argument( "--remove-redundant-aliases", dest="remove_redundant_aliases", action="store_true", help=( "Tells isort to remove redundant aliases from imports, such as `import os as os`." " This defaults to `False` simply because some projects use these seemingly useless " " aliases to signify intent and change behaviour." ), ) output_group.add_argument( "--honor-noqa", dest="honor_noqa", action="store_true", help="Tells isort to honor noqa comments to enforce skipping those comments.", ) output_group.add_argument( "--treat-comment-as-code", dest="treat_comments_as_code", action="append", help="Tells isort to treat the specified single line comment(s) as if they are code.", ) output_group.add_argument( "--treat-all-comment-as-code", dest="treat_all_comments_as_code", action="store_true", help="Tells isort to treat all single line comments as if they are code.", ) output_group.add_argument( "--formatter", dest="formatter", type=str, help="Specifies the name of a formatting plugin to use when producing output.", ) output_group.add_argument( "--color", dest="color_output", action="store_true", help="Tells isort to use color in terminal output.", ) output_group.add_argument( "--ext-format", dest="ext_format", help="Tells isort to format the given files according to an extensions formatting rules.", ) output_group.add_argument( "--star-first", help="Forces star imports above others to avoid overriding directly imported variables.", dest="star_first", action="store_true", ) output_group.add_argument( "--split-on-trailing-comma", help="Split imports list followed by a trailing comma into VERTICAL_HANGING_INDENT mode", dest="split_on_trailing_comma", action="store_true", ) section_group.add_argument( "--sd", "--section-default", dest="default_section", help="Sets the default section for import options: " + str(sections.DEFAULT), ) section_group.add_argument( "--only-sections", "--os", dest="only_sections", action="store_true", help="Causes imports to be sorted based on their sections like STDLIB, THIRDPARTY, etc. " "Within sections, the imports are ordered by their import style and the imports with " "the same style maintain their relative positions.", ) section_group.add_argument( "--ds", "--no-sections", help="Put all imports into the same section bucket", dest="no_sections", action="store_true", ) section_group.add_argument( "--fas", "--force-alphabetical-sort", action="store_true", dest="force_alphabetical_sort", help="Force all imports to be sorted as a single section", ) section_group.add_argument( "--fss", "--force-sort-within-sections", action="store_true", dest="force_sort_within_sections", help="Don't sort straight-style imports (like import sys) before from-style imports " "(like from itertools import groupby). Instead, sort the imports by module, " "independent of import style.", ) section_group.add_argument( "--hcss", "--honor-case-in-force-sorted-sections", action="store_true", dest="honor_case_in_force_sorted_sections", help="Honor `--case-sensitive` when `--force-sort-within-sections` is being used. " "Without this option set, `--order-by-type` decides module name ordering too.", ) section_group.add_argument( "--srss", "--sort-relative-in-force-sorted-sections", action="store_true", dest="sort_relative_in_force_sorted_sections", help="When using `--force-sort-within-sections`, sort relative imports the same " "way as they are sorted when not using that setting.", ) section_group.add_argument( "--fass", "--force-alphabetical-sort-within-sections", action="store_true", dest="force_alphabetical_sort_within_sections", help="Force all imports to be sorted alphabetically within a section", ) section_group.add_argument( "-t", "--top", help="Force specific imports to the top of their appropriate section.", dest="force_to_top", action="append", ) section_group.add_argument( "--combine-straight-imports", "--csi", dest="combine_straight_imports", action="store_true", help="Combines all the bare straight imports of the same section in a single line. " "Won't work with sections which have 'as' imports", ) section_group.add_argument( "--nlb", "--no-lines-before", help="Sections which should not be split with previous by empty lines", dest="no_lines_before", action="append", ) section_group.add_argument( "--src", "--src-path", dest="src_paths", action="append", help="Add an explicitly defined source path " "(modules within src paths have their imports automatically categorized as first_party)." " Glob expansion (`*` and `**`) is supported for this option.", ) section_group.add_argument( "-b", "--builtin", dest="known_standard_library", action="append", help="Force isort to recognize a module as part of Python's standard library.", ) section_group.add_argument( "--extra-builtin", dest="extra_standard_library", action="append", help="Extra modules to be included in the list of ones in Python's standard library.", ) section_group.add_argument( "-f", "--future", dest="known_future_library", action="append", help="Force isort to recognize a module as part of Python's internal future compatibility " "libraries. WARNING: this overrides the behavior of __future__ handling and therefore" " can result in code that can't execute. If you're looking to add dependencies such " "as six, a better option is to create another section below --future using custom " "sections. See: https://github.com/PyCQA/isort#custom-sections-and-ordering and the " "discussion here: https://github.com/PyCQA/isort/issues/1463.", ) section_group.add_argument( "-o", "--thirdparty", dest="known_third_party", action="append", help="Force isort to recognize a module as being part of a third party library.", ) section_group.add_argument( "-p", "--project", dest="known_first_party", action="append", help="Force isort to recognize a module as being part of the current python project.", ) section_group.add_argument( "--known-local-folder", dest="known_local_folder", action="append", help="Force isort to recognize a module as being a local folder. " "Generally, this is reserved for relative imports (from . import module).", ) section_group.add_argument( "--virtual-env", dest="virtual_env", help="Virtual environment to use for determining whether a package is third-party", ) section_group.add_argument( "--conda-env", dest="conda_env", help="Conda environment to use for determining whether a package is third-party", ) section_group.add_argument( "--py", "--python-version", action="store", dest="py_version", choices=(*tuple(VALID_PY_TARGETS), "auto"), help="Tells isort to set the known standard library based on the specified Python " "version. Default is to assume any Python 3 version could be the target, and use a union " "of all stdlib modules across versions. If auto is specified, the version of the " "interpreter used to run isort " f"(currently: {sys.version_info.major}{sys.version_info.minor}) will be used.", ) return parser def parse_args(argv: Sequence[str] | None = None) -> dict[str, Any]: argv = sys.argv[1:] if argv is None else list(argv) parser = _build_arg_parser() arguments = {key: value for key, value in vars(parser.parse_args(argv)).items() if value} if "dont_order_by_type" in arguments: arguments["order_by_type"] = False del arguments["dont_order_by_type"] if "dont_follow_links" in arguments: arguments["follow_links"] = False del arguments["dont_follow_links"] if "dont_float_to_top" in arguments: del arguments["dont_float_to_top"] if arguments.get("float_to_top", False): sys.exit("Can't set both --float-to-top and --dont-float-to-top.") else: arguments["float_to_top"] = False multi_line_output = arguments.get("multi_line_output", None) if multi_line_output: if multi_line_output.isdigit(): arguments["multi_line_output"] = WrapModes(int(multi_line_output)) else: arguments["multi_line_output"] = WrapModes[multi_line_output] return arguments def _preconvert(item: Any) -> str | list[Any]: """Preconverts objects from native types into JSONifiable types""" if isinstance(item, (set, frozenset)): return list(item) if isinstance(item, WrapModes): return str(item.name) if isinstance(item, Path): return str(item) if callable(item) and hasattr(item, "__name__"): return str(item.__name__) raise TypeError(f"Unserializable object {item} of type {type(item)}") def identify_imports_main( argv: Sequence[str] | None = None, stdin: TextIOWrapper | None = None ) -> None: parser = argparse.ArgumentParser( description="Get all import definitions from a given file." "Use `-` as the first argument to represent stdin." ) parser.add_argument( "files", nargs="+", help="One or more Python source files that need their imports sorted." ) parser.add_argument( "--top-only", action="store_true", default=False, help="Only identify imports that occur in before functions or classes.", ) target_group = parser.add_argument_group("target options") target_group.add_argument( "--follow-links", action="store_true", default=False, help="Tells isort to follow symlinks that are encountered when running recursively.", ) uniqueness = parser.add_mutually_exclusive_group() uniqueness.add_argument( "--unique", action="store_true", default=False, help="If true, isort will only identify unique imports.", ) uniqueness.add_argument( "--packages", dest="unique", action="store_const", const=api.ImportKey.PACKAGE, default=False, help="If true, isort will only identify the unique top level modules imported.", ) uniqueness.add_argument( "--modules", dest="unique", action="store_const", const=api.ImportKey.MODULE, default=False, help="If true, isort will only identify the unique modules imported.", ) uniqueness.add_argument( "--attributes", dest="unique", action="store_const", const=api.ImportKey.ATTRIBUTE, default=False, help="If true, isort will only identify the unique attributes imported.", ) arguments = parser.parse_args(argv) file_names = arguments.files if file_names == ["-"]: identified_imports = api.find_imports_in_stream( sys.stdin if stdin is None else stdin, unique=arguments.unique, top_only=arguments.top_only, follow_links=arguments.follow_links, ) else: identified_imports = api.find_imports_in_paths( file_names, unique=arguments.unique, top_only=arguments.top_only, follow_links=arguments.follow_links, ) for identified_import in identified_imports: if arguments.unique == api.ImportKey.PACKAGE: print(identified_import.module.split(".")[0]) elif arguments.unique == api.ImportKey.MODULE: print(identified_import.module) elif arguments.unique == api.ImportKey.ATTRIBUTE: print(f"{identified_import.module}.{identified_import.attribute}") else: print(str(identified_import)) # Ignore DeepSource cyclomatic complexity check for this function. It is one # the main entrypoints so sort of expected to be complex. # skipcq: PY-R1000 def main(argv: Sequence[str] | None = None, stdin: TextIOWrapper | None = None) -> None: arguments = parse_args(argv) if arguments.get("show_version"): print(ASCII_ART) return show_config: bool = arguments.pop("show_config", False) show_files: bool = arguments.pop("show_files", False) if show_config and show_files: sys.exit("Error: either specify show-config or show-files not both.") if "settings_path" in arguments: if os.path.isfile(arguments["settings_path"]): arguments["settings_file"] = os.path.abspath(arguments["settings_path"]) arguments["settings_path"] = os.path.dirname(arguments["settings_file"]) else: arguments["settings_path"] = os.path.abspath(arguments["settings_path"]) if "virtual_env" in arguments: venv = arguments["virtual_env"] arguments["virtual_env"] = os.path.abspath(venv) if not os.path.isdir(arguments["virtual_env"]): warn(f"virtual_env dir does not exist: {arguments['virtual_env']}", stacklevel=2) file_names = arguments.pop("files", []) if not file_names and not show_config: print(QUICK_GUIDE) if arguments: sys.exit("Error: arguments passed in without any paths or content.") return if "settings_path" not in arguments: arguments["settings_path"] = ( arguments.get("filename", None) or os.getcwd() if file_names == ["-"] else os.path.abspath(file_names[0] if file_names else ".") ) if not os.path.isdir(arguments["settings_path"]): arguments["settings_path"] = os.path.dirname(arguments["settings_path"]) config_dict = arguments.copy() ask_to_apply = config_dict.pop("ask_to_apply", False) jobs = config_dict.pop("jobs", None) check = config_dict.pop("check", False) show_diff = config_dict.pop("show_diff", False) write_to_stdout = config_dict.pop("write_to_stdout", False) stream_filename = config_dict.pop("filename", None) ext_format = config_dict.pop("ext_format", None) allow_root = config_dict.pop("allow_root", None) resolve_all_configs = config_dict.pop("resolve_all_configs", False) if "config_root" in config_dict and not resolve_all_configs: sys.exit("Error: --config-root (--cr) has no effect without --resolve-all-configs.") wrong_sorted_files = False all_attempt_broken = False no_valid_encodings = False config_trie: Trie | None = None if resolve_all_configs: config_trie = find_all_configs(config_dict.pop("config_root", ".")) if "src_paths" in config_dict: # Keep CLI-provided values as-is so wildcard patterns can be expanded later # relative to the resolved config directory. config_dict["src_paths"] = set(config_dict.get("src_paths", ())) config = Config(**config_dict) if show_config: print(json.dumps(asdict(config), indent=4, separators=(",", ": "), default=_preconvert)) return if file_names == ["-"]: file_path = Path(stream_filename) if stream_filename else None if show_files: sys.exit("Error: can't show files for streaming input.") if config.sort_reexports: sys.exit("Error: --sort-reexports is not supported with streaming input (stdin).") input_stream = sys.stdin if stdin is None else stdin if check: incorrectly_sorted = not api.check_stream( input_stream=input_stream, config=config, show_diff=show_diff, file_path=file_path, extension=ext_format, ) wrong_sorted_files = incorrectly_sorted else: try: api.sort_stream( input_stream=input_stream, output_stream=sys.stdout, config=config, show_diff=show_diff, file_path=file_path, extension=ext_format, raise_on_skip=False, ) except FileSkipped: sys.stdout.write(input_stream.read()) elif "/" in file_names and not allow_root: printer = create_terminal_printer( color=config.color_output, error=config.format_error, success=config.format_success ) printer.error("it is dangerous to operate recursively on '/'") printer.error("use --allow-root to override this failsafe") sys.exit(1) else: if stream_filename: printer = create_terminal_printer( color=config.color_output, error=config.format_error, success=config.format_success ) printer.error("Filename override is intended only for stream (-) sorting.") sys.exit(1) skipped: list[str] = [] broken: list[str] = [] if config.filter_files: filtered_files = [] for file_name in file_names: if config.is_skipped(Path(file_name)): skipped.append(str(Path(file_name).resolve())) else: filtered_files.append(file_name) file_names = filtered_files file_names = files.find(file_names, config, skipped, broken) if show_files: for file_name in file_names: print(file_name) return num_skipped = 0 num_broken = 0 num_invalid_encoding = 0 if config.verbose: print(ASCII_ART) if jobs: import multiprocessing.pool # noqa: PLC0415 executor_ctx: multiprocessing.pool.Pool | AbstractContextManager[None] = ( multiprocessing.pool.Pool(jobs if jobs > 0 else multiprocessing.cpu_count()) ) else: executor_ctx = nullcontext() with executor_ctx as executor: if executor is not None: attempt_iterator: Iterator[SortAttempt | None] = executor.imap( functools.partial( sort_imports, config=config, check=check, ask_to_apply=ask_to_apply, show_diff=show_diff, write_to_stdout=write_to_stdout, extension=ext_format, config_trie=config_trie, ), file_names, ) else: # https://github.com/python/typeshed/pull/2814 attempt_iterator = ( sort_imports( file_name, config=config, check=check, ask_to_apply=ask_to_apply, show_diff=show_diff, write_to_stdout=write_to_stdout, extension=ext_format, config_trie=config_trie, ) for file_name in file_names ) # If any files passed in are missing considered as error, should be removed is_no_attempt = True any_encoding_valid = False for sort_attempt in attempt_iterator: if not sort_attempt: continue # pragma: no cover - shouldn't happen, satisfies type constraint incorrectly_sorted = sort_attempt.incorrectly_sorted if arguments.get("check", False) and incorrectly_sorted: wrong_sorted_files = True if sort_attempt.skipped: num_skipped += ( 1 # pragma: no cover - shouldn't happen, due to skip in iter_source_code ) if not sort_attempt.supported_encoding: num_invalid_encoding += 1 else: any_encoding_valid = True is_no_attempt = False num_skipped += len(skipped) if num_skipped and not config.quiet: if config.verbose: for was_skipped in skipped: print( f"{was_skipped} was skipped as it's listed in 'skip' setting, " "matches a glob in 'skip_glob' setting, or is in a .gitignore file with " "--skip-gitignore enabled." ) print(f"Skipped {num_skipped} files") num_broken += len(broken) if num_broken and not config.quiet: if config.verbose: for was_broken in broken: warn( f"{was_broken} was broken path, make sure it exists correctly", stacklevel=2 ) print(f"Broken {num_broken} paths") if num_broken > 0 and is_no_attempt: all_attempt_broken = True if num_invalid_encoding > 0 and not any_encoding_valid: no_valid_encodings = True if wrong_sorted_files: sys.exit(1) if all_attempt_broken: sys.exit(1) if no_valid_encodings: printer = create_terminal_printer( color=config.color_output, error=config.format_error, success=config.format_success ) printer.error("No valid encodings.") sys.exit(1) if __name__ == "__main__": main() isort-9.0.1/isort/output.py0000644000000000000000000010574313615410400012665 0ustar00import copy import itertools from collections.abc import Iterable from functools import partial from typing import Literal from isort.format import format_simplified from . import _parse_utils, parse, sorting, wrap, wrap_modes from .comments import add_to_line as with_comments from .identify import STATEMENT_DECLARATIONS from .place import module_with_reason from .settings import DEFAULT_CONFIG, Config # Ignore DeepSource cyclomatic complexity check for this function. # skipcq: PY-R1000 def sorted_imports( parsed: parse.ParsedContent, config: Config = DEFAULT_CONFIG, extension: str = "py", import_type: str = "import", ) -> str: """Adds the imports back to the file. (at the index of the first import) sorted alphabetically and split between groups """ if parsed.import_index == -1: return _output_as_string(parsed.lines_without_imports, parsed.line_separator) formatted_output: list[str] = parsed.lines_without_imports.copy() remove_imports = [format_simplified(removal) for removal in config.remove_imports] sections: Iterable[str] = itertools.chain(parsed.sections, config.forced_separate) if config.no_sections: parsed.imports["no_sections"] = { "straight": {}, "from": {}, "lazy_straight": {}, "lazy_from": {}, } base_sections: tuple[str, ...] = () for section in sections: if section == "FUTURE": base_sections = ("FUTURE",) continue parsed.imports["no_sections"]["straight"].update(parsed.imports[section]["straight"]) parsed.imports["no_sections"]["from"].update(parsed.imports[section]["from"]) parsed.imports["no_sections"]["lazy_straight"].update( parsed.imports[section]["lazy_straight"] ) parsed.imports["no_sections"]["lazy_from"].update(parsed.imports[section]["lazy_from"]) sections = (*base_sections, "no_sections") output: list[str] = [] seen_headings: set[str] = set() pending_lines_before = False for section in sections: section_output = _build_import_group( parsed, config, section, remove_imports, import_type, is_lazy=False ) # PEP 810 lazy imports always follow all eager imports within the section. lazy_section_output = _build_import_group( parsed, config, section, remove_imports, import_type, is_lazy=True ) if lazy_section_output: if section_output: section_output += [""] * config.lines_between_types + lazy_section_output else: section_output = lazy_section_output section_name = section no_lines_before = section_name in config.no_lines_before if section_output: if section_name in parsed.place_imports: parsed.place_imports[section_name] = section_output continue section_title = config.import_headings.get(section_name.lower(), "") if section_title and section_title not in seen_headings: if config.dedup_headings: seen_headings.add(section_title) section_comment = f"# {section_title}" if section_comment not in parsed.lines_without_imports[0:1]: # pragma: no branch section_output.insert(0, section_comment) section_footer = config.import_footers.get(section_name.lower(), "") if section_footer and section_footer not in seen_headings: if config.dedup_headings: seen_headings.add(section_footer) section_comment_end = f"# {section_footer}" if ( section_comment_end not in parsed.lines_without_imports[-1:] ): # pragma: no branch section_output.append("") # Empty line for black compatibility section_output.append(section_comment_end) if section in config.separate_packages: section_output = _separate_packages(section_output, config) if pending_lines_before or not no_lines_before: output += [""] * config.lines_between_sections output += section_output pending_lines_before = False else: pending_lines_before = pending_lines_before or not no_lines_before if config.ensure_newline_before_comments: output = _ensure_newline_before_comment(output) while output and output[-1].strip() == "": output.pop() # pragma: no cover while output and output[0].strip() == "": output.pop(0) if config.formatting_function: output = config.formatting_function( parsed.line_separator.join(output), extension, config ).splitlines() output_at = 0 if parsed.import_index < parsed.original_line_count: output_at = parsed.import_index formatted_output[output_at:0] = output if output: imports_tail = output_at + len(output) trailing_blank_lines: list[str] = [] while [ character.strip() for character in formatted_output[imports_tail : imports_tail + 1] ] == [""]: trailing_blank_lines.append(formatted_output.pop(imports_tail)) if config.lines_before_imports != -1: lines_before_imports = config.lines_before_imports if config.profile == "black" and extension == "pyi": # special case for black lines_before_imports = 1 formatted_output[:0] = ["" for line in range(lines_before_imports)] imports_tail += lines_before_imports if len(formatted_output) > imports_tail: next_construct = "" tail = formatted_output[imports_tail:] for index, line in enumerate(tail): # pragma: no branch should_skip, in_quote = _parse_utils.skip_line( line, in_quote="", needs_import=False ) if not should_skip and line.strip(): if ( line.strip().startswith("#") and len(tail) > (index + 1) and tail[index + 1].strip() ): continue next_construct = line break if in_quote: # pragma: no branch next_construct = line break if config.lines_after_imports != -1: lines_after_imports = config.lines_after_imports if config.profile == "black" and extension == "pyi": # special case for black lines_after_imports = 1 elif extension != "pyi" and next_construct.startswith(STATEMENT_DECLARATIONS): lines_after_imports = 2 else: lines_after_imports = 1 blank_lines = [""] * lines_after_imports overflow_form_feeds = "" for index, line in enumerate(trailing_blank_lines): form_feeds = "".join(character for character in line if character == "\f") if index < lines_after_imports: blank_lines[index] += form_feeds else: overflow_form_feeds += form_feeds if overflow_form_feeds: formatted_output[imports_tail] = ( overflow_form_feeds + formatted_output[imports_tail] ) formatted_output[imports_tail:0] = blank_lines if parsed.place_imports: new_out_lines = [] for index, line in enumerate(formatted_output): new_out_lines.append(line) if line in parsed.import_placements: new_out_lines.extend(parsed.place_imports[parsed.import_placements[line]]) if ( len(formatted_output) <= (index + 1) or formatted_output[index + 1].strip() != "" ): new_out_lines.append("") formatted_output = new_out_lines return _output_as_string(formatted_output, parsed.line_separator) # Ignore DeepSource cyclomatic complexity check for this function. # skipcq: PY-R1000 def _build_import_group( parsed: parse.ParsedContent, config: Config, section: str, remove_imports: list[str], import_type: str, *, is_lazy: bool, ) -> list[str]: """Build the sorted import lines for one group (eager or lazy) within a section.""" straight_key: Literal["lazy_straight", "straight"] = "lazy_straight" if is_lazy else "straight" from_key: Literal["lazy_from", "from"] = "lazy_from" if is_lazy else "from" straight_modules: Iterable[str] = parsed.imports[section][straight_key] if not config.only_sections: straight_modules = sorting.sort( config, straight_modules, key=lambda key: sorting.module_key( key, config, section_name=section, straight_import=True ), reverse=config.reverse_sort, ) from_modules: Iterable[str] = parsed.imports[section][from_key] if not config.only_sections: from_modules = sorting.sort( config, from_modules, key=lambda key: sorting.module_key(key, config, section_name=section), reverse=config.reverse_sort, ) if not is_lazy and config.star_first: star_modules = [] other_modules = [] for module in from_modules: if "*" in parsed.imports[section]["from"][module]: star_modules.append(module) else: other_modules.append(module) from_modules = star_modules + other_modules straight_imports = _with_straight_imports( parsed, config, straight_modules, section, remove_imports, import_type, is_lazy=is_lazy ) from_imports = _with_from_imports( parsed, config, from_modules, section, remove_imports, import_type, is_lazy=is_lazy ) lines_between = [""] * (config.lines_between_types if from_modules and straight_modules else 0) if config.from_first or section == "FUTURE": group_output = from_imports + lines_between + straight_imports else: group_output = straight_imports + lines_between + from_imports if config.force_sort_within_sections: # collapse comments comments_above: list[str] = [] new_group_output: list[str] = [] lines_with_comments: dict[str, list[str]] = {} for line in group_output: if not line: continue if line.startswith("#"): comments_above.append(line) elif comments_above: lines_with_comments[line] = comments_above new_group_output.append(line) comments_above = [] else: new_group_output.append(line) # only_sections option is not imposed if force_sort_within_sections is True new_group_output = sorting.sort( config, new_group_output, key=partial(sorting.section_key, config=config), reverse=config.reverse_sort, ) # uncollapse comments group_output = [] for line in new_group_output: line_comments = lines_with_comments.get(line) if line_comments: group_output.extend(line_comments) group_output.append(line) return group_output def _build_as_imports( *, from_import: str, # Y in `from X import Y as Z` as_imports: list[str], # `Y as Z` in `from X import Y as Z` import_start: str, # `from X import ` in `from X import Y as Z` line_separator: str, config: Config, straight_comments: list[str], nested_comments: dict[str, str], # Whether to include the straight import line (e.g. `from X import Y`) in the output. Can be # determined by combining star imports. include_straight_import: bool, ) -> list[str]: """ Build the import statements for a given list (`as_imports`) of aliased imports. """ if not config.only_sections: as_imports = sorting.sort(config, as_imports) output: list[str] = [] if include_straight_import: specific_comment = nested_comments.pop(from_import, None) if specific_comment is not None: straight_comments.append(specific_comment) output.append( wrap.line( with_comments( straight_comments, import_start + from_import, removed=config.ignore_comments, comment_prefix=config.comment_prefix, ), line_separator, config, ) ) straight_comments = [] for as_import in as_imports: # `We snapshot `straight_comments` here so that we can later distinguish # the two types: opening-line comments must stay on the "import (" line # when parentheses are used, while attribute-line comments stay on the # import attribute line. opening_line_comments = list(straight_comments) specific_comment = nested_comments.pop(as_import, None) # Collect the attribute-line comment (if any) separately so it can be # embedded in the attribute line regardless of wrapping mode. if specific_comment is not None: straight_comments.append(specific_comment) import_line = import_start + as_import if opening_line_comments and config.use_parentheses: # When parentheses are used, opening-line comments (e.g. "# noqa") must # remain on the "from X import (" line. If we naively embedded them in # the attribute string and then called wrap.line(), the comment would # end up inside the parentheses on the alias attribute line. # Wrap the import with only the attribute-line comment. Afterwards, add # the opening-line comment back to the first line of the wrapped import # statement. lines = wrap.line( with_comments( [specific_comment] if specific_comment else [], import_line, removed=config.ignore_comments, comment_prefix=config.comment_prefix, ), line_separator, config, ).split(line_separator) opening_comment = with_comments( opening_line_comments, "", removed=config.ignore_comments, comment_prefix=config.comment_prefix, ) if opening_comment: lines[0] += opening_comment if config.multi_line_output == wrap_modes.WrapModes.NOQA: lines[0] = wrap.line(lines[0], line_separator, config) output.append(line_separator.join(lines)) else: output.append( wrap.line( with_comments( straight_comments, import_line, removed=config.ignore_comments, comment_prefix=config.comment_prefix, ), line_separator, config, ) ) straight_comments = [] return output def _build_grouped_from_imports( *, imports: list[str], comments: list[str], module: str, import_start: str, processed_as_imports_this_iteration: bool, parsed: parse.ParsedContent, config: Config, ) -> str: """ Build a string of imports, wrapping them across multiple lines if necessary. """ # If there are no imports to output, return an empty string. Make sure to do this after popping # any comments, so that they don't remain. if not imports: return "" import_statement = with_comments( comments, import_start + (", ").join(imports), removed=config.ignore_comments, comment_prefix=config.comment_prefix, ) do_multiline_reformat = False if ( (config.force_grid_wrap and len(imports) >= config.force_grid_wrap) or (len(import_statement) > config.line_length and len(imports) > 1) or ( # If line too long AND have imports AND we are # NOT using GRID or VERTICAL wrap modes len(import_statement) > config.line_length and len(imports) > 0 and config.multi_line_output not in (wrap_modes.WrapModes.GRID, wrap_modes.WrapModes.VERTICAL) ) ): do_multiline_reformat = True if ( # When ``include_trailing_comma`` is enabled isort always appends a # trailing comma to multi-line imports. Combined with # ``split_on_trailing_comma`` that means any import wrapped across # multiple lines would gain a trailing comma and therefore be exploded # onto individual lines on the *next* run. Treat such imports as if they # already carried a trailing comma so the explode happens on the first # pass too, keeping the output idempotent regardless of the requested # ``multi_line_output`` mode. config.split_on_trailing_comma and ( module in parsed.trailing_commas or (config.include_trailing_comma and do_multiline_reformat) ) and not processed_as_imports_this_iteration ): return wrap.import_statement( import_start=import_start, from_imports=imports, comments=comments, line_separator=parsed.line_separator, config=config, explode=True, ) if do_multiline_reformat: import_statement = wrap.import_statement( import_start=import_start, from_imports=imports, comments=comments, line_separator=parsed.line_separator, config=config, ) if config.multi_line_output == wrap_modes.WrapModes.GRID: other_import_statement = wrap.import_statement( import_start=import_start, from_imports=imports, comments=comments, line_separator=parsed.line_separator, config=config, multi_line_output=wrap_modes.WrapModes.VERTICAL_GRID, ) if ( max( len(import_line) for import_line in import_statement.split(parsed.line_separator) ) > config.line_length ): import_statement = other_import_statement return import_statement if len(import_statement) > config.line_length: return wrap.line(import_statement, parsed.line_separator, config) return import_statement def _with_from_imports( parsed: parse.ParsedContent, config: Config, from_modules: Iterable[str], section: str, remove_imports: list[str], import_type: str, *, is_lazy: bool, ) -> list[str]: output: list[str] = [] import_key: Literal["lazy_from", "from"] = "lazy_from" if is_lazy else "from" for module in from_modules: module_output = _with_from_imports_for_module( module=module, parsed=parsed, config=config, section=section, remove_imports=remove_imports, import_type=import_type, import_key=import_key, ) if module_output is not None: output.extend(module_output) return output # Ignore DeepSource cyclomatic complexity check for this function. It was # already complex when this check was enabled. # skipcq: PY-R1000 def _with_from_imports_for_module( *, module: str, parsed: parse.ParsedContent, config: Config, section: str, remove_imports: list[str], import_type: str, import_key: Literal["lazy_from", "from"], ) -> list[str]: """ Build the from-import lines for a given module and section. """ output: list[str] = [] if module in remove_imports: return output import_start = f"from {module} {import_type} " if import_key == "lazy_from": import_start = f"lazy {import_start}" from_imports = list(parsed.imports[section][import_key][module]) if ( not config.no_inline_sort or (config.force_single_line and module not in config.single_line_exclusions) ) and not config.only_sections: from_imports = sorting.sort( config, from_imports, key=lambda key: sorting.module_key( key, config, True, config.force_alphabetical_sort_within_sections, section_name=section, ), reverse=config.reverse_sort, ) if remove_imports: from_imports = [line for line in from_imports if f"{module}.{line}" not in remove_imports] sub_modules = [f"{module}.{from_import}" for from_import in from_imports] as_imports = { from_import: [ f"{from_import} as {as_module}" for as_module in parsed.as_map["from"][sub_module] ] for from_import, sub_module in zip(from_imports, sub_modules, strict=False) if sub_module in parsed.as_map["from"] } if config.combine_as_imports and not ("*" in from_imports and config.combine_star): if not config.no_inline_sort: for as_import in as_imports: if not config.only_sections: as_imports[as_import] = sorting.sort(config, as_imports[as_import]) for from_import in copy.copy(from_imports): if from_import in as_imports: idx = from_imports.index(from_import) if parsed.imports[section][import_key][module][from_import]: from_imports[(idx + 1) : (idx + 1)] = as_imports.pop(from_import) else: from_imports[idx : (idx + 1)] = as_imports.pop(from_import) comments: list[str] = parsed.categorized_comments["from"].pop(module, []) above_comments = parsed.categorized_comments["above"]["from"].pop(module, None) if above_comments: output.extend(above_comments) only_show_as_imports = False if "*" in from_imports: from_imports.remove("*") output.append( wrap.line( with_comments( _with_star_comments( parsed, module, # If we are combining the star imports we want to include all from-import # comments we found above. comments if config.combine_star else [], ), f"{import_start}*", removed=config.ignore_comments, comment_prefix=config.comment_prefix, ), parsed.line_separator, config, ) ) if config.combine_star: from_imports = [ from_import for from_import in from_imports if from_import in as_imports ] only_show_as_imports = True # Handle force_single_line if config.force_single_line and module not in config.single_line_exclusions: for from_import in from_imports: if from_import in as_imports: output.extend( _build_as_imports( from_import=from_import, as_imports=as_imports[from_import], import_start=import_start, line_separator=parsed.line_separator, config=config, straight_comments=[ *comments, *parsed.categorized_comments["straight"].get( f"{module}.{from_import}", [] ), ], nested_comments=parsed.categorized_comments["nested"].get(module, {}), include_straight_import=( parsed.imports[section][import_key][module][from_import] and not only_show_as_imports ), ) ) else: single_import_line = with_comments( [ c for c in ( *comments, parsed.categorized_comments["nested"] .get(module, {}) .pop(from_import, None), ) if c is not None ], import_start + from_import, removed=config.ignore_comments, comment_prefix=config.comment_prefix, ) output.append(wrap.line(single_import_line, parsed.line_separator, config)) comments = [] return output while from_imports: # Tracks whether any aliased imports were emitted before the grouped # non-aliased imports in this pass of the outer loop. When True it # suppresses the split_on_trailing_comma explode behaviour for the # non-aliased group, because those imports are not the sole content # of the statement and forcing them onto individual lines would break # the intended output structure. processed_as_imports_this_iteration = False while from_imports and from_imports[0] in as_imports: processed_as_imports_this_iteration = True from_import = from_imports.pop(0) output.extend( _build_as_imports( from_import=from_import, as_imports=as_imports[from_import], import_start=import_start, line_separator=parsed.line_separator, config=config, # `from_comments` at this point contains any comments that appeared on # the *opening* "from X import" line. These are distinct from # `nested_comments`, which is an inline comment on the attribute line # itself. straight_comments=parsed.categorized_comments["straight"].get( f"{module}.{from_import}", [] ), nested_comments=parsed.categorized_comments["nested"].get(module, {}), include_straight_import=( parsed.imports[section][import_key][module][from_import] and not only_show_as_imports ), ) ) for from_import in copy.copy(from_imports): comment = parsed.categorized_comments["nested"].get(module, {}).pop(from_import, None) if ( comment is not None and from_import in as_imports and config.multi_line_output != wrap_modes.WrapModes.NOQA ): # This name also has an alias; the leading-alias loop keeps the two # together on a later pass. Popping the comment here would drop the # alias, so put it back and leave the name for that loop. parsed.categorized_comments["nested"].setdefault(module, {})[from_import] = comment continue if comment is not None: # If the comment is a noqa and hanging indent wrapping is used, # keep the name in the main list and hoist the comment to the statement. if ( comment.lower().startswith("noqa") and config.multi_line_output == wrap_modes.WrapModes.HANGING_INDENT ): comments = list(comments) if comments else [] comments.append(comment) continue from_imports.remove(from_import) if from_imports: use_comments: list[str] | None = [] else: use_comments = comments comments = [] single_import_line = with_comments( use_comments, import_start + from_import, removed=config.ignore_comments, comment_prefix=config.comment_prefix, ) comment_text = f" {comment}" if comment else "" single_import_line += ( f"{(use_comments and ';') or config.comment_prefix}{comment_text}" ) output.append(wrap.line(single_import_line, parsed.line_separator, config)) from_import_section = [] while from_imports and ( from_imports[0] not in as_imports or ( config.combine_as_imports and parsed.imports[section][import_key][module][from_import] ) ): from_import_section.append(from_imports.pop(0)) # If we are combining aliased imports we need to pop any associated comments. if config.combine_as_imports: comments = [ *comments, *(parsed.categorized_comments["from"].pop(f"{module}.__combined_as__", ())), ] grouped_from_import_statement = _build_grouped_from_imports( config=config, parsed=parsed, module=module, imports=from_import_section, import_start=import_start, comments=comments, processed_as_imports_this_iteration=processed_as_imports_this_iteration, ) if grouped_from_import_statement: output.append(grouped_from_import_statement) # Reset comments as we have just parsed them. comments = [] return output # Ignore DeepSource cyclomatic complexity check for this function. # skipcq: PY-R1000 def _with_straight_imports( parsed: parse.ParsedContent, config: Config, straight_modules: Iterable[str], section: str, remove_imports: list[str], import_type: str, *, is_lazy: bool, ) -> list[str]: output: list[str] = [] import_type = f"lazy {import_type}" if is_lazy else import_type as_imports = any(module in parsed.as_map["straight"] for module in straight_modules) # combine_straight_imports only works for bare imports, 'as' imports not included if config.combine_straight_imports and not as_imports: if not straight_modules: return [] above_comments: list[str] = [] inline_comments: list[str] = [] for module in straight_modules: if module in parsed.categorized_comments["above"]["straight"]: above_comments.extend(parsed.categorized_comments["above"]["straight"].pop(module)) if module in parsed.categorized_comments["straight"]: inline_comments.extend(parsed.categorized_comments["straight"][module]) combined_straight_imports = ", ".join(straight_modules) output.extend(above_comments) if inline_comments: combined_inline_comments = " ".join(c for c in inline_comments if c) if combined_inline_comments: line_content = ( f"{import_type} {combined_straight_imports} # {combined_inline_comments}" ) else: line_content = f"{import_type} {combined_straight_imports} #" else: line_content = f"{import_type} {combined_straight_imports}" output.append(wrap.line(line_content, parsed.line_separator, config)) return output for module in straight_modules: if module in remove_imports: continue import_definition = [] if module in parsed.as_map["straight"]: if parsed.imports[section]["lazy_straight" if is_lazy else "straight"][module]: import_definition.append((f"{import_type} {module}", module)) import_definition.extend( (f"{import_type} {module} as {as_import}", f"{module} as {as_import}") for as_import in parsed.as_map["straight"][module] ) else: import_definition.append((f"{import_type} {module}", module)) comments_above = parsed.categorized_comments["above"]["straight"].pop(module, None) if comments_above: output.extend(comments_above) for idef, imodule in import_definition: line_content = with_comments( parsed.categorized_comments["straight"].get(imodule), idef, removed=config.ignore_comments, comment_prefix=config.comment_prefix, ) if config.multi_line_output == wrap_modes.WrapModes.NOQA: line_content = wrap.line(line_content, parsed.line_separator, config) output.append(line_content) return output def _output_as_string(lines: list[str], line_separator: str) -> str: return line_separator.join(_normalize_empty_lines(lines)) def _normalize_empty_lines(lines: list[str]) -> list[str]: while lines and lines[-1].strip() == "": lines.pop(-1) lines.append("") return lines def _ensure_newline_before_comment(output: list[str]) -> list[str]: new_output: list[str] = [] def is_comment(line: str | None) -> bool: return line.startswith("#") if line else False for line, prev_line in zip(output, [None, *output], strict=False): if is_comment(line) and prev_line != "" and not is_comment(prev_line): new_output.append("") new_output.append(line) return new_output def _with_star_comments(parsed: parse.ParsedContent, module: str, comments: list[str]) -> list[str]: star_comment = parsed.categorized_comments["nested"].get(module, {}).pop("*", None) if star_comment: return [*comments, star_comment] return comments def _separate_packages(section_output: list[str], config: Config) -> list[str]: group_keys: set[str] = set() comments_above: list[str] = [] processed_section_output: list[str] = [] for section_line in section_output: if section_line.startswith("#"): comments_above.append(section_line) continue package_name: str = section_line.split(" ")[1] _, reason = module_with_reason(package_name, config) if "Matched configured known pattern" in reason: package_depth = len(reason.split(".")) - 1 # minus 1 for re.compile key = ".".join(package_name.split(".")[: package_depth + 1]) else: key = package_name.split(".")[0] if key not in group_keys: if group_keys: processed_section_output.append("") group_keys.add(key) if comments_above: processed_section_output.extend(comments_above) comments_above = [] processed_section_output.append(section_line) return processed_section_output isort-9.0.1/isort/parse.py0000644000000000000000000005123613615410400012434 0ustar00"""Defines parsing functions used by isort for parsing import definitions""" from collections import OrderedDict, defaultdict from functools import partial from itertools import chain from typing import TYPE_CHECKING, NamedTuple, TypedDict from warnings import warn from . import place from ._parse_utils import ( collect_import_continuation, import_type, normalize_from_import_string, normalize_line, skip_line, strip_syntax, ) from .comments import parse as parse_comments from .exceptions import MissingSection from .settings import DEFAULT_CONFIG, Config if TYPE_CHECKING: CommentsAboveDict = TypedDict( "CommentsAboveDict", {"straight": dict[str, list[str]], "from": dict[str, list[str]]} ) CommentsDict = TypedDict( "CommentsDict", { "from": dict[str, list[str]], "straight": dict[str, list[str]], "nested": dict[str, dict[str, str]], "above": CommentsAboveDict, }, ) def _infer_line_separator(contents: str) -> str: if "\r\n" in contents: return "\r\n" if "\r" in contents: return "\r" return "\n" ParsedImports = TypedDict( "ParsedImports", { "straight": dict[str, bool], "from": dict[str, dict[str, bool]], "lazy_straight": dict[str, bool], "lazy_from": dict[str, dict[str, bool]], }, ) class ParsedContent(NamedTuple): in_lines: list[str] lines_without_imports: list[str] import_index: int place_imports: dict[str, list[str]] import_placements: dict[str, str] as_map: dict[str, dict[str, list[str]]] imports: dict[str, ParsedImports] categorized_comments: "CommentsDict" change_count: int original_line_count: int line_separator: str sections: tuple[str, ...] verbose_output: list[str] trailing_commas: set[str] # Ignore DeepSource cyclomatic complexity check for this function. It is one # the main entrypoints so sort of expected to be complex. # skipcq: PY-R1000 def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedContent: """Parses a python file taking out and categorizing imports.""" line_separator: str = config.line_ending or _infer_line_separator(contents) # ``str.splitlines`` also treats characters such as form feed as line # boundaries, even though Python's universal-newline handling does not. # Normalize actual newline sequences explicitly so those characters stay # attached to their source line. in_lines = contents.replace("\r\n", "\n").replace("\r", "\n").split("\n") if not contents: in_lines = [] out_lines = [] original_line_count = len(in_lines) finder = partial(place.module, config=config) line_count = len(in_lines) place_imports: dict[str, list[str]] = {} import_placements: dict[str, str] = {} as_map: dict[str, dict[str, list[str]]] = { "straight": defaultdict(list), "from": defaultdict(list), } imports: OrderedDict[str, ParsedImports] = OrderedDict() verbose_output: list[str] = [] for section in chain(config.sections, config.forced_separate): imports[section] = { "straight": OrderedDict(), "from": OrderedDict(), "lazy_straight": OrderedDict(), "lazy_from": OrderedDict(), } categorized_comments: CommentsDict = { "from": {}, "straight": {}, "nested": {}, "above": {"straight": {}, "from": {}}, } trailing_commas: set[str] = set() index = 0 import_index = -1 in_quote = "" while index < line_count: line = in_lines[index] index += 1 statement_index = index (skipping_line, in_quote) = skip_line(line, in_quote=in_quote) if ( line in config.section_comments or line in config.section_comments_end ) and not skipping_line: if import_index == -1: # pragma: no branch import_index = index - 1 continue if "isort:imports-" in line and line.startswith("#"): section = line.split("isort:imports-")[-1].split()[0].upper() place_imports[section] = [] import_placements[line] = section elif "isort: imports-" in line and line.startswith("#"): section = line.split("isort: imports-")[-1].split()[0].upper() place_imports[section] = [] import_placements[line] = section if skipping_line: out_lines.append(line) continue lstripped_line = line.lstrip() if ( config.float_to_top and import_index == -1 and line and not in_quote and not lstripped_line.startswith("#") and not lstripped_line.startswith("'''") and not lstripped_line.startswith('"""') ): if not lstripped_line.startswith("import") and not lstripped_line.startswith("from"): import_index = index - 1 while import_index and not in_lines[import_index - 1]: import_index -= 1 else: commentless = line.split("#", 1)[0].strip() if ( ("isort:skip" in line or "isort: skip" in line) and "(" in commentless and ")" not in commentless ): import_index = index starting_line = line while "isort:skip" in starting_line or "isort: skip" in starting_line: commentless = starting_line.split("#", 1)[0] if ( "(" in commentless and not commentless.rstrip().endswith(")") and import_index < line_count ): while import_index < line_count and not commentless.rstrip().endswith( ")" ): commentless = in_lines[import_index].split("#", 1)[0] import_index += 1 else: import_index += 1 if import_index >= line_count: break starting_line = in_lines[import_index] line, *end_of_line_comment = line.split("#", 1) if ";" in line: statements = [line.strip() for line in line.split(";")] else: statements = [line] if end_of_line_comment: statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" for statement in statements: line, raw_line = normalize_line(statement) type_of_import = import_type(line, config) or "" raw_lines = [raw_line] if not type_of_import: out_lines.append(raw_line) continue # Detect PEP 810 lazy imports (``lazy import X`` / ``lazy from X import Y``). # We strip the ``lazy `` prefix so the rest of the parsing logic works normally # on the resulting ``import X`` / ``from X import Y`` string. The original # lazy type is remembered in ``is_lazy`` and used later when storing the result. is_lazy = type_of_import in ("lazy_straight", "lazy_from") if is_lazy: line = line[len("lazy ") :] type_of_import = "straight" if type_of_import == "lazy_straight" else "from" if import_index == -1: import_index = index - 1 nested_comments = {} import_string, comment = parse_comments(line) comments = [comment] if comment is not None else [] line_parts = [part for part in strip_syntax(import_string).strip().split(" ") if part] if type_of_import == "from" and len(line_parts) == 2 and comments: nested_comments[line_parts[-1]] = comments[0] def _get_next_line() -> tuple[str, str | None]: nonlocal index if index >= line_count: raise StopIteration result = parse_comments(in_lines[index]) index += 1 return result line, import_string, extra_lines = collect_import_continuation( line, import_string, _get_next_line, line_separator ) for extra_line in extra_lines: raw_lines.append(extra_line.line) # If during parsing of the continuation lines we encounter a comment, we record it. if extra_line.comment is not None: comments.append(extra_line.comment) stripped_line = strip_syntax(extra_line.line).strip() if ( type_of_import == "from" and stripped_line and " " not in stripped_line.replace(" as ", "") ): nested_comments[stripped_line] = extra_line.comment if type_of_import == "from": import_string = normalize_from_import_string(import_string) if "import " not in import_string: out_lines.extend(raw_lines) continue just_imports = [ item.replace("{|", "{ ").replace("|}", " }") for item in strip_syntax(import_string).split() ] attach_comments_to: list[str] | None = None direct_imports = just_imports[1:] straight_import = True top_level_module = "" if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports): straight_import = False while "as" in just_imports: nested_module = None as_index = just_imports.index("as") if type_of_import == "from": nested_module = just_imports[as_index - 1] top_level_module = just_imports[0] module = top_level_module + "." + nested_module as_name = just_imports[as_index + 1] direct_imports.remove(nested_module) direct_imports.remove(as_name) direct_imports.remove("as") if nested_module == as_name and config.remove_redundant_aliases: pass elif as_name not in as_map["from"][module]: # pragma: no branch as_map["from"][module].append(as_name) full_name = f"{nested_module} as {as_name}" associated_comment = nested_comments.get(full_name) if associated_comment is not None: categorized_comments["nested"].setdefault(top_level_module, {})[ full_name ] = associated_comment if associated_comment in comments: # pragma: no branch comments.pop(comments.index(associated_comment)) else: module = just_imports[as_index - 1] as_name = just_imports[as_index + 1] if module == as_name and config.remove_redundant_aliases: pass elif as_name not in as_map["straight"][module]: as_map["straight"][module].append(as_name) if comments and attach_comments_to is None: if nested_module and config.combine_as_imports: attach_comments_to = categorized_comments["from"].setdefault( f"{top_level_module}.__combined_as__", [] ) else: if type_of_import == "from" or ( config.remove_redundant_aliases and as_name == module.split(".")[-1] ): attach_comments_to = categorized_comments["straight"].setdefault( module, [] ) else: attach_comments_to = categorized_comments["straight"].setdefault( f"{module} as {as_name}", [] ) del just_imports[as_index : as_index + 2] if type_of_import == "from": import_from = just_imports.pop(0) placed_module = finder(import_from) if config.verbose and not config.only_modified: print(f"from-type place_module for {import_from} returned {placed_module}") elif config.verbose: verbose_output.append( f"from-type place_module for {import_from} returned {placed_module}" ) if placed_module == "": warn( f"could not place module {import_from} of line {line} --" " Do you need to define a default section?", stacklevel=2, ) if placed_module and placed_module not in imports: raise MissingSection(import_module=import_from, section=placed_module) root = imports[placed_module]["lazy_from" if is_lazy else type_of_import] for import_name in just_imports: associated_comment = nested_comments.get(import_name) if associated_comment is not None: categorized_comments["nested"].setdefault(import_from, {})[import_name] = ( associated_comment ) if associated_comment in comments: # pragma: no branch comments.pop(comments.index(associated_comment)) if ( config.force_single_line and comments and attach_comments_to is None and len(just_imports) == 1 ): nested_from_comments = categorized_comments["nested"].setdefault( import_from, {} ) existing_comment = nested_from_comments.get(just_imports[0], "") nested_from_comments[just_imports[0]] = ( f"{existing_comment}{'; ' if existing_comment else ''}{'; '.join(comments)}" ) comments = [] if comments and attach_comments_to is None: attach_comments_to = categorized_comments["from"].setdefault(import_from, []) if len(out_lines) > max(import_index, 1) - 1: last = out_lines[-1].rstrip() if out_lines else "" while ( last.startswith("#") and not last.endswith('"""') and not last.endswith("'''") and "isort:imports-" not in last and "isort: imports-" not in last and not config.treat_all_comments_as_code and last.strip() not in config.treat_comments_as_code ): categorized_comments["above"]["from"].setdefault(import_from, []).insert( 0, out_lines.pop(-1) ) if out_lines: last = out_lines[-1].rstrip() else: last = "" if statement_index - 1 == import_index: # pragma: no cover import_index -= len( categorized_comments["above"]["from"].get(import_from, []) ) if import_from not in root: root[import_from] = OrderedDict( (module, module in direct_imports) for module in just_imports ) else: root[import_from].update( (module, root[import_from].get(module, False) or module in direct_imports) for module in just_imports ) if comments and attach_comments_to is not None: attach_comments_to.extend(comments) if ( just_imports and just_imports[-1] and "," in import_string.split(just_imports[-1])[-1] ): trailing_commas.add(import_from) else: assert type_of_import == "straight" # noqa: S101 # Only needed for type checker if comments and attach_comments_to is not None: attach_comments_to.extend(comments) comments = [] for module in just_imports: if comments: categorized_comments["straight"][module] = comments comments = [] if len(out_lines) > max(import_index, +1, 1) - 1: last = out_lines[-1].rstrip() if out_lines else "" while ( last.startswith("#") and not last.endswith('"""') and not last.endswith("'''") and "isort:imports-" not in last and "isort: imports-" not in last and not config.treat_all_comments_as_code and last.strip() not in config.treat_comments_as_code ): categorized_comments["above"]["straight"].setdefault(module, []).insert( 0, out_lines.pop(-1) ) if out_lines: last = out_lines[-1].rstrip() else: last = "" if index - 1 == import_index: import_index -= len( categorized_comments["above"]["straight"].get(module, []) ) placed_module = finder(module) if config.verbose and not config.only_modified: print(f"else-type place_module for {module} returned {placed_module}") elif config.verbose: verbose_output.append( f"else-type place_module for {module} returned {placed_module}" ) if placed_module == "": warn( f"could not place module {module} of line {line} --" " Do you need to define a default section?", stacklevel=2, ) imports.setdefault( "", { "straight": OrderedDict(), "from": OrderedDict(), "lazy_straight": OrderedDict(), "lazy_from": OrderedDict(), }, ) if placed_module and placed_module not in imports: raise MissingSection(import_module=module, section=placed_module) straight_import |= bool( imports[placed_module]["lazy_straight" if is_lazy else type_of_import].get( module, False ) ) imports[placed_module]["lazy_straight" if is_lazy else type_of_import][ module ] = straight_import change_count = len(out_lines) - original_line_count return ParsedContent( in_lines=in_lines, lines_without_imports=out_lines, import_index=import_index, place_imports=place_imports, import_placements=import_placements, as_map=as_map, imports=imports, categorized_comments=categorized_comments, change_count=change_count, original_line_count=original_line_count, line_separator=line_separator, sections=config.sections, verbose_output=verbose_output, trailing_commas=trailing_commas, ) isort-9.0.1/isort/place.py0000644000000000000000000001202113615410400012373 0ustar00"""Contains all logic related to placing an import within a certain section.""" import importlib from collections.abc import Iterable from fnmatch import fnmatch from functools import lru_cache from pathlib import Path from isort import sections from isort.settings import DEFAULT_CONFIG, Config from isort.utils import exists_case_sensitive LOCAL = "LOCALFOLDER" def module(name: str, config: Config = DEFAULT_CONFIG) -> str: """Returns the section placement for the given module name.""" return module_with_reason(name, config)[0] @lru_cache(maxsize=1000) def module_with_reason(name: str, config: Config = DEFAULT_CONFIG) -> tuple[str, str]: """Returns the section placement for the given module name alongside the reasoning.""" return ( _forced_separate(name, config) or _local(name, config) or _known_pattern(name, config) or _src_path(name, config) or (config.default_section, "Default option in Config or universal default.") ) def _forced_separate(name: str, config: Config) -> tuple[str, str] | None: for forced_separate in config.forced_separate: # Ensure all forced_separate patterns will match to end of string path_glob = forced_separate if not forced_separate.endswith("*"): path_glob = f"{forced_separate}*" if fnmatch(name, path_glob) or fnmatch(name, "." + path_glob): return (forced_separate, f"Matched forced_separate ({forced_separate}) config value.") return None def _local(name: str, config: Config) -> tuple[str, str] | None: if name.startswith("."): return (LOCAL, "Module name started with a dot.") return None def _known_pattern(name: str, config: Config) -> tuple[str, str] | None: parts = name.split(".") module_names_to_check = (".".join(parts[:first_k]) for first_k in range(len(parts), 0, -1)) for module_name_to_check in module_names_to_check: for pattern, placement in config.known_patterns: if placement in config.sections and pattern.match(module_name_to_check): return (placement, f"Matched configured known pattern {pattern}") return None def _src_path( name: str, config: Config, src_paths: Iterable[Path] | None = None, prefix: tuple[str, ...] = (), ) -> tuple[str, str] | None: if src_paths is None: src_paths = config.src_paths root_module_name, *nested_module = name.split(".", 1) new_prefix = (*prefix, root_module_name) namespace = ".".join(new_prefix) for src_path in src_paths: module_path = (src_path / root_module_name).resolve() if not prefix and not module_path.is_dir() and src_path.name == root_module_name: module_path = src_path.resolve() if nested_module and ( namespace in config.namespace_packages or ( config.auto_identify_namespace_packages and _is_namespace_package(module_path, config.supported_extensions) ) ): return _src_path(nested_module[0], config, (module_path,), new_prefix) if ( _is_module(module_path) or _is_package(module_path) or _src_path_is_module(src_path, root_module_name) ): return (sections.FIRSTPARTY, f"Found in one of the configured src_paths: {src_path}.") return None def _is_module(path: Path) -> bool: return ( exists_case_sensitive(str(path.with_suffix(".py"))) or any( exists_case_sensitive(str(path.with_suffix(ext_suffix))) for ext_suffix in importlib.machinery.EXTENSION_SUFFIXES ) or exists_case_sensitive(str(path / "__init__.py")) ) def _is_package(path: Path) -> bool: return exists_case_sensitive(str(path)) and path.is_dir() def _is_namespace_package(path: Path, src_extensions: frozenset[str]) -> bool: if not _is_package(path): return False init_file = path / "__init__.py" if not init_file.exists(): filenames = [ filepath for filepath in path.iterdir() if filepath.suffix.lstrip(".") in src_extensions or filepath.name.lower() in ("setup.cfg", "pyproject.toml") ] if filenames: return False else: with init_file.open("rb") as open_init_file: file_start = open_init_file.read(4096) if ( b"__import__('pkg_resources').declare_namespace(__name__)" not in file_start and b'__import__("pkg_resources").declare_namespace(__name__)' not in file_start and b"__path__ = __import__('pkgutil').extend_path(__path__, __name__)" not in file_start and b'__path__ = __import__("pkgutil").extend_path(__path__, __name__)' not in file_start ): return False return True def _src_path_is_module(src_path: Path, module_name: str) -> bool: return ( module_name == src_path.name and src_path.is_dir() and exists_case_sensitive(str(src_path)) ) isort-9.0.1/isort/profiles.py0000644000000000000000000000432213615410400013137 0ustar00"""Common profiles are defined here to be easily used within a project using --profile {name}""" from typing import Any black = { "multi_line_output": 3, "include_trailing_comma": True, "split_on_trailing_comma": True, "force_grid_wrap": 0, "use_parentheses": True, "ensure_newline_before_comments": True, "line_length": 88, } django = { "combine_as_imports": True, "include_trailing_comma": True, "multi_line_output": 5, "line_length": 79, } pycharm = { "multi_line_output": 3, "force_grid_wrap": 2, "lines_after_imports": 2, } google = { "force_single_line": True, "force_sort_within_sections": True, "lexicographical": True, "line_length": 1000, "single_line_exclusions": ( "collections.abc", "six.moves", "typing", "typing_extensions", ), "order_by_type": False, "group_by_package": True, } open_stack = { "force_single_line": True, "force_sort_within_sections": True, "lexicographical": True, } plone = black.copy() plone.update( { "force_alphabetical_sort": True, "force_single_line": True, } ) attrs = { "atomic": True, "force_grid_wrap": 0, "include_trailing_comma": True, "lines_after_imports": 2, "lines_between_types": 1, "multi_line_output": 3, "use_parentheses": True, } hug = { "multi_line_output": 3, "include_trailing_comma": True, "force_grid_wrap": 0, "use_parentheses": True, "line_length": 100, } wemake = { "multi_line_output": 3, "include_trailing_comma": True, "use_parentheses": True, "line_length": 80, } appnexus = { **black, "force_sort_within_sections": True, "order_by_type": False, "case_sensitive": False, "reverse_relative": True, "sort_relative_in_force_sorted_sections": True, "sections": ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "APPLICATION", "LOCALFOLDER"], "no_lines_before": "LOCALFOLDER", } profiles: dict[str, dict[str, Any]] = { "black": black, "django": django, "pycharm": pycharm, "google": google, "open_stack": open_stack, "plone": plone, "attrs": attrs, "hug": hug, "wemake": wemake, "appnexus": appnexus, } isort-9.0.1/isort/py.typed0000644000000000000000000000000013615410400012426 0ustar00isort-9.0.1/isort/sections.py0000644000000000000000000000042013615410400013136 0ustar00"""Defines all sections isort uses by default""" FUTURE: str = "FUTURE" STDLIB: str = "STDLIB" THIRDPARTY: str = "THIRDPARTY" FIRSTPARTY: str = "FIRSTPARTY" LOCALFOLDER: str = "LOCALFOLDER" DEFAULT: tuple[str, ...] = (FUTURE, STDLIB, THIRDPARTY, FIRSTPARTY, LOCALFOLDER) isort-9.0.1/isort/settings.py0000644000000000000000000010526413615410400013163 0ustar00"""isort/settings.py. Defines how the default settings for isort should be loaded """ import configparser import fnmatch import os import posixpath import re import stat import subprocess # nosec # Needed for gitignore support. import sys from collections.abc import Callable, Iterable from dataclasses import asdict, dataclass, field from importlib.metadata import EntryPoints from pathlib import Path from re import Pattern from typing import Any from warnings import warn from mypy_extensions import mypyc_attr from . import stdlibs from .exceptions import ( FormattingPluginDoesNotExist, InvalidSettingsPath, ProfileDoesNotExist, SortingFunctionDoesNotExist, UnsupportedSettings, ) from .profiles import profiles as profiles from .sections import DEFAULT as SECTION_DEFAULTS from .sections import FIRSTPARTY, FUTURE, LOCALFOLDER, STDLIB, THIRDPARTY from .utils import Trie from .wrap_modes import WrapModes from .wrap_modes import from_string as wrap_mode_from_string if sys.version_info >= (3, 11): import tomllib else: from ._vendored import tomli as tomllib _SHEBANG_RE = re.compile(rb"^#!.*\bpython[23w]?\b") CYTHON_EXTENSIONS = frozenset({"pyx", "pxd"}) SUPPORTED_EXTENSIONS = frozenset({"py", "pyi", *CYTHON_EXTENSIONS}) BLOCKED_EXTENSIONS = frozenset({"pex"}) FILE_SKIP_COMMENTS: tuple[str, ...] = ( "isort:" + "skip_file", "isort: " + "skip_file", ) # Concatenated to avoid this file being skipped MAX_CONFIG_SEARCH_DEPTH: int = 25 # The number of parent directories to for a config file within STOP_CONFIG_SEARCH_ON_DIRS: tuple[str, ...] = (".git", ".hg") VALID_PY_TARGETS: tuple[str, ...] = tuple( target.replace("py", "") for target in dir(stdlibs) if not target.startswith("_") ) CONFIG_SOURCES: tuple[str, ...] = ( ".isort.cfg", "pyproject.toml", "setup.cfg", "tox.ini", ".editorconfig", ) DEFAULT_SKIP: frozenset[str] = frozenset( { ".venv", "venv", ".tox", ".eggs", ".git", ".hg", ".mypy_cache", ".nox", ".svn", ".bzr", "_build", "buck-out", "build", "dist", ".pants.d", ".direnv", "node_modules", "__pypackages__", ".pytype", } ) CONFIG_SECTIONS: dict[str, tuple[str, ...]] = { ".isort.cfg": ("settings", "isort"), "pyproject.toml": ("tool.isort",), "setup.cfg": ("isort", "tool:isort"), "tox.ini": ("isort", "tool:isort"), ".editorconfig": ("*", "*.py", "**.py", "*.{py}"), } FALLBACK_CONFIG_SECTIONS: tuple[str, ...] = ("isort", "tool:isort", "tool.isort") IMPORT_HEADING_PREFIX = "import_heading_" IMPORT_FOOTER_PREFIX = "import_footer_" KNOWN_PREFIX = "known_" KNOWN_SECTION_MAPPING: dict[str, str] = { STDLIB: "STANDARD_LIBRARY", FUTURE: "FUTURE_LIBRARY", FIRSTPARTY: "FIRST_PARTY", THIRDPARTY: "THIRD_PARTY", LOCALFOLDER: "LOCAL_FOLDER", } RUNTIME_SOURCE = "runtime" _STR_BOOLEAN_MAPPING = { "y": True, "yes": True, "t": True, "on": True, "1": True, "true": True, "n": False, "no": False, "f": False, "off": False, "0": False, "false": False, } # TODO: Make this work as native class, see https://github.com/PyCQA/isort/issues/2629 @mypyc_attr(native_class=False) @dataclass(frozen=True) class _Config: """Defines the data schema and defaults used for isort configuration. NOTE: known lists, such as known_standard_library, are intentionally not complete as they are dynamically determined later on. """ py_version: str = "3" force_to_top: frozenset[str] = frozenset() skip: frozenset[str] = DEFAULT_SKIP extend_skip: frozenset[str] = frozenset() skip_glob: frozenset[str] = frozenset() extend_skip_glob: frozenset[str] = frozenset() skip_gitignore: bool = False line_length: int = 79 wrap_length: int = 0 line_ending: str = "" sections: tuple[str, ...] = SECTION_DEFAULTS no_sections: bool = False known_future_library: frozenset[str] = frozenset(("__future__",)) known_third_party: frozenset[str] = frozenset() known_first_party: frozenset[str] = frozenset() known_local_folder: frozenset[str] = frozenset() known_standard_library: frozenset[str] = frozenset() extra_standard_library: frozenset[str] = frozenset() known_other: dict[str, frozenset[str]] = field(default_factory=dict) multi_line_output: WrapModes = WrapModes.GRID forced_separate: tuple[str, ...] = () indent: str = " " * 4 comment_prefix: str = " #" length_sort: bool = False length_sort_straight: bool = False length_sort_sections: frozenset[str] = frozenset() add_imports: frozenset[str] = frozenset() remove_imports: frozenset[str] = frozenset() append_only: bool = False reverse_relative: bool = False force_single_line: bool = False single_line_exclusions: tuple[str, ...] = () default_section: str = THIRDPARTY import_headings: dict[str, str] = field(default_factory=dict) import_footers: dict[str, str] = field(default_factory=dict) balanced_wrapping: bool = False use_parentheses: bool = False order_by_type: bool = True atomic: bool = False lines_before_imports: int = -1 lines_after_imports: int = -1 lines_between_sections: int = 1 lines_between_types: int = 0 combine_as_imports: bool = False combine_star: bool = False include_trailing_comma: bool = False from_first: bool = False verbose: bool = False quiet: bool = False force_adds: bool = False force_alphabetical_sort_within_sections: bool = False force_alphabetical_sort: bool = False force_grid_wrap: int = 0 force_sort_within_sections: bool = False lexicographical: bool = False group_by_package: bool = False separate_packages: frozenset[str] = frozenset() ignore_whitespace: bool = False no_lines_before: frozenset[str] = frozenset() no_inline_sort: bool = False ignore_comments: bool = False case_sensitive: bool = False sources: tuple[dict[str, Any], ...] = () virtual_env: str = "" conda_env: str = "" ensure_newline_before_comments: bool = False directory: str = "" profile: str = "" honor_noqa: bool = False src_paths: tuple[Path, ...] = () remove_redundant_aliases: bool = False float_to_top: bool = False filter_files: bool = False formatter: str = "" formatting_function: Callable[[str, str, object], str] | None = None color_output: bool = False treat_comments_as_code: frozenset[str] = frozenset() treat_all_comments_as_code: bool = False supported_extensions: frozenset[str] = SUPPORTED_EXTENSIONS blocked_extensions: frozenset[str] = BLOCKED_EXTENSIONS constants: frozenset[str] = frozenset() classes: frozenset[str] = frozenset() variables: frozenset[str] = frozenset() dedup_headings: bool = False only_sections: bool = False only_modified: bool = False combine_straight_imports: bool = False auto_identify_namespace_packages: bool = True namespace_packages: frozenset[str] = frozenset() follow_links: bool = True indented_import_headings: bool = True honor_case_in_force_sorted_sections: bool = False sort_relative_in_force_sorted_sections: bool = False overwrite_in_place: bool = False reverse_sort: bool = False star_first: bool = False git_ls_files: dict[Path, set[str]] = field(default_factory=dict) format_error: str = "{error}: {message}" format_success: str = "{success}: {message}" sort_order: str = "natural" sort_reexports: bool = False split_on_trailing_comma: bool = False def __post_init__(self) -> None: py_version = self.py_version if py_version == "auto": # pragma: no cover py_version = f"{sys.version_info.major}{sys.version_info.minor}" if py_version not in VALID_PY_TARGETS: raise ValueError( f"The python version {py_version} is not supported. " "You can set a python version with the -py or --python-version flag. " f"The following versions are supported: {VALID_PY_TARGETS}" ) if py_version != "all": object.__setattr__(self, "py_version", f"py{py_version}") if not self.known_standard_library: object.__setattr__( self, "known_standard_library", frozenset(getattr(stdlibs, self.py_version).stdlib) ) if self.multi_line_output == WrapModes.VERTICAL_GRID_GROUPED_NO_COMMA: vertical_grid_grouped = WrapModes.VERTICAL_GRID_GROUPED object.__setattr__(self, "multi_line_output", vertical_grid_grouped) if self.force_alphabetical_sort: object.__setattr__(self, "force_alphabetical_sort_within_sections", True) object.__setattr__(self, "no_sections", True) object.__setattr__(self, "lines_between_types", 1) object.__setattr__(self, "from_first", True) if self.wrap_length > self.line_length: raise ValueError( "wrap_length must be set lower than or equal to line_length: " f"{self.wrap_length} > {self.line_length}." ) def __hash__(self) -> int: return id(self) _DEFAULT_SETTINGS = {**asdict(_Config()), "source": "defaults"} # TODO: Make this work as native class, see https://github.com/PyCQA/isort/issues/2629 @mypyc_attr(native_class=False) class Config(_Config): def __init__( self, settings_file: str = "", settings_path: str | Path = "", config: _Config | None = None, **config_overrides: Any, ): self._known_patterns: list[tuple[Pattern[str], str]] | None = None self._section_comments: tuple[str, ...] | None = None self._section_comments_end: tuple[str, ...] | None = None self._skips: frozenset[str] | None = None self._posix_skips: frozenset[str] | None = None self._skip_globs: frozenset[str] | None = None self._sorting_function: Callable[..., list[str]] | None = None if config: config_vars = asdict(config).copy() config_vars.update(config_overrides) config_vars["py_version"] = config_vars["py_version"].replace("py", "") super().__init__(**config_vars) return # We can't use self.quiet to conditionally show warnings before super.__init__() is called # at the end of this method. _Config is also frozen so setting self.quiet isn't possible. # Therefore we extract quiet early here in a variable and use that in warning conditions. quiet = config_overrides.get("quiet", False) sources: list[dict[str, object]] = [_DEFAULT_SETTINGS] config_settings: dict[str, Any] project_root: str if settings_file: config_settings = _get_config_data( settings_file, CONFIG_SECTIONS.get(os.path.basename(settings_file), FALLBACK_CONFIG_SECTIONS), ) project_root = os.path.dirname(settings_file) if not config_settings and not quiet: warn( f"A custom settings file was specified: {settings_file} but no configuration " "was found inside. This can happen when [settings] is used as the config " "header instead of [isort]. " "See: https://isort.readthedocs.io/en/latest/configuration/config_files.html" "#custom-config-files for more information.", stacklevel=2, ) elif settings_path: if not os.path.exists(settings_path): raise InvalidSettingsPath(settings_path) settings_path = os.path.abspath(settings_path) project_root, config_settings = _find_config(settings_path) else: config_settings = {} project_root = os.getcwd() profile_name = config_overrides.get("profile", config_settings.get("profile", "")) profile: dict[str, Any] = {} if profile_name: if profile_name not in profiles: for plugin in entry_points(group="isort.profiles"): profiles.setdefault(plugin.name, plugin.load()) if profile_name not in profiles: raise ProfileDoesNotExist(profile_name) profile = profiles[profile_name].copy() profile["source"] = f"{profile_name} profile" sources.append(profile) if config_settings: sources.append(config_settings) if config_overrides: config_overrides["source"] = RUNTIME_SOURCE sources.append(config_overrides) combined_config = {**profile, **config_settings, **config_overrides} if "indent" in combined_config: indent = str(combined_config["indent"]) if indent.isdigit(): indent = " " * int(indent) else: indent = indent.strip("'").strip('"') if indent.lower() == "tab": indent = "\t" combined_config["indent"] = indent known_other = {} import_headings = {} import_footers = {} for key, value in tuple(combined_config.items()): # Collect all known sections beyond those that have direct entries if key.startswith(KNOWN_PREFIX) and key not in ( "known_standard_library", "known_future_library", "known_third_party", "known_first_party", "known_local_folder", ): import_heading = key[len(KNOWN_PREFIX) :].lower() maps_to_section = import_heading.upper() combined_config.pop(key) if maps_to_section in KNOWN_SECTION_MAPPING: section_name = f"known_{KNOWN_SECTION_MAPPING[maps_to_section].lower()}" if section_name in combined_config and not quiet: warn( f"Can't set both {key} and {section_name} in the same config file.\n" f"Default to {section_name} if unsure." "\n\n" "See: https://isort.readthedocs.io/en/latest/index.html" "#custom-sections-and-ordering.", stacklevel=2, ) else: combined_config[section_name] = frozenset(value) else: known_other[import_heading] = frozenset(value) if maps_to_section not in combined_config.get("sections", ()) and not quiet: warn( f"`{key}` setting is defined, but {maps_to_section} is not" " included in `sections` config option:" f" {combined_config.get('sections', SECTION_DEFAULTS)}.\n\n" "See: https://isort.readthedocs.io/en/latest/index.html" "#custom-sections-and-ordering.", stacklevel=2, ) if key.startswith(IMPORT_HEADING_PREFIX): import_headings[key[len(IMPORT_HEADING_PREFIX) :].lower()] = str(value) if key.startswith(IMPORT_FOOTER_PREFIX): import_footers[key[len(IMPORT_FOOTER_PREFIX) :].lower()] = str(value) # Coerce all provided config values into their correct type default_value = _DEFAULT_SETTINGS.get(key, None) if default_value is None: continue combined_config[key] = type(default_value)(value) for section in combined_config.get("sections", ()): if section in SECTION_DEFAULTS: continue if section.lower() not in known_other: config_keys = ", ".join(known_other.keys()) warn( f"`sections` setting includes {section}, but no known_{section.lower()} " "is defined. " f"The following known_SECTION config options are defined: {config_keys}.", stacklevel=2, ) if "directory" not in combined_config: combined_config["directory"] = ( os.path.dirname(str(config_settings["source"])) if config_settings.get("source", None) else os.getcwd() ) path_root = Path(combined_config.get("directory", project_root)).resolve() path_root = path_root if path_root.is_dir() else path_root.parent if "src_paths" not in combined_config: combined_config["src_paths"] = (path_root / "src", path_root) else: src_paths: list[Path] = [] for src_path in combined_config.get("src_paths", ()): full_paths = ( path_root.glob(src_path) if "*" in str(src_path) else [path_root / src_path] ) for path in full_paths: if path not in src_paths: src_paths.append(path) combined_config["src_paths"] = tuple(src_paths) if "formatter" in combined_config: for plugin in entry_points(group="isort.formatters"): if plugin.name == combined_config["formatter"]: combined_config["formatting_function"] = plugin.load() break else: raise FormattingPluginDoesNotExist(combined_config["formatter"]) # Remove any config values that are used for creating config object but # aren't defined in dataclass combined_config.pop("source", None) combined_config.pop("sources", None) combined_config.pop("runtime_src_paths", None) if known_other: combined_config["known_other"] = known_other if import_headings: for import_heading_key in import_headings: combined_config.pop(f"{IMPORT_HEADING_PREFIX}{import_heading_key}") combined_config["import_headings"] = import_headings if import_footers: for import_footer_key in import_footers: combined_config.pop(f"{IMPORT_FOOTER_PREFIX}{import_footer_key}") combined_config["import_footers"] = import_footers unsupported_config_errors = {} for option in set(combined_config.keys()).difference( getattr(_Config, "__dataclass_fields__", {}).keys() ): for source in reversed(sources): if option in source: unsupported_config_errors[option] = { "value": source[option], "source": source["source"], } if unsupported_config_errors: raise UnsupportedSettings(unsupported_config_errors) super().__init__(sources=tuple(sources), **combined_config) def is_supported_filetype(self, file_name: str) -> bool: _root, ext = os.path.splitext(file_name) ext = ext.lstrip(".") if ext in self.supported_extensions: return True if ext in self.blocked_extensions: return False # Skip editor backup files. if file_name.endswith("~"): return False try: if stat.S_ISFIFO(os.stat(file_name).st_mode): return False except OSError: pass try: with open(file_name, "rb") as fp: line = fp.readline(100) except OSError: return False return bool(_SHEBANG_RE.match(line)) def _check_folder_git_ls_files(self, folder: str) -> Path | None: env = {**os.environ, "LANG": "C.UTF-8"} try: topfolder_result = subprocess.check_output( # nosec # skipcq: PYL-W1510 ["git", "-C", folder, "rev-parse", "--show-toplevel"], encoding="utf-8", env=env ) except subprocess.CalledProcessError: return None git_folder = Path(topfolder_result.rstrip()).resolve() # files committed to git tracked_files = ( subprocess.check_output( # nosec # skipcq: PYL-W1510 ["git", "-C", str(git_folder), "ls-files", "-z"], encoding="utf-8", env=env, ) .rstrip("\0") .split("\0") ) # files that haven't been committed yet, but aren't ignored tracked_files_others = ( subprocess.check_output( # nosec # skipcq: PYL-W1510 ["git", "-C", str(git_folder), "ls-files", "-z", "--others", "--exclude-standard"], encoding="utf-8", env=env, ) .rstrip("\0") .split("\0") ) self.git_ls_files[git_folder] = { str(git_folder / Path(f)) for f in tracked_files + tracked_files_others } return git_folder def is_skipped(self, file_path: Path) -> bool: """Returns True if the file and/or folder should be skipped based on current settings.""" if self.directory and Path(self.directory) in file_path.resolve().parents: file_name = os.path.relpath(file_path.resolve(), self.directory) else: file_name = str(file_path) os_path = str(file_path) # Normalize the path to POSIX-style for consistent comparison with skip paths and globs normalized_path = os_path.replace("\\", "/") if normalized_path[1:2] == ":": normalized_path = normalized_path[2:] normalized_path = posixpath.abspath(normalized_path) for skip_path in self.posix_skips: if normalized_path == skip_path: return True position = os.path.split(file_name) while position[1]: if position[1] in self.skips: return True position = os.path.split(position[0]) for sglob in self.skip_globs: if fnmatch.fnmatch(file_name, sglob) or fnmatch.fnmatch("/" + file_name, sglob): return True if not (os.path.isfile(os_path) or os.path.isdir(os_path) or os.path.islink(os_path)): return True if self.skip_gitignore: if file_path.name == ".git": # pragma: no cover return True git_folder = None file_paths = [file_path, file_path.resolve()] for folder in self.git_ls_files: if any(folder in path.parents for path in file_paths): git_folder = folder break else: git_folder = self._check_folder_git_ls_files(str(file_path.parent)) # git_ls_files are good files you should parse. If you're not in the allow list, skip. if ( git_folder and not file_path.is_dir() and str(file_path.resolve()) not in self.git_ls_files[git_folder] ): return True return False @property def known_patterns(self) -> list[tuple[Pattern[str], str]]: if self._known_patterns is not None: return self._known_patterns self._known_patterns = [] pattern_sections = [STDLIB] + [section for section in self.sections if section != STDLIB] for placement in reversed(pattern_sections): known_placement = KNOWN_SECTION_MAPPING.get(placement, placement).lower() config_key = f"{KNOWN_PREFIX}{known_placement}" known_modules = getattr(self, config_key, self.known_other.get(known_placement, ())) extra_modules = getattr(self, f"extra_{known_placement}", ()) all_modules = set(extra_modules).union(known_modules) known_patterns = [ pattern for known_pattern in all_modules for pattern in self._parse_known_pattern(known_pattern) ] for known_pattern in known_patterns: regexp = "^" + known_pattern.replace("*", ".*").replace("?", ".?") + "$" self._known_patterns.append((re.compile(regexp), placement)) return self._known_patterns @property def section_comments(self) -> tuple[str, ...]: if self._section_comments is not None: return self._section_comments self._section_comments = tuple(f"# {heading}" for heading in self.import_headings.values()) return self._section_comments @property def section_comments_end(self) -> tuple[str, ...]: if self._section_comments_end is not None: return self._section_comments_end self._section_comments_end = tuple(f"# {footer}" for footer in self.import_footers.values()) return self._section_comments_end @property def skips(self) -> frozenset[str]: if self._skips is not None: return self._skips self._skips = self.skip.union(self.extend_skip) return self._skips @property def posix_skips(self) -> frozenset[str]: """Returns a frozenset of absolute paths to skip, normalized to POSIX-style paths.""" if self._posix_skips is not None: return self._posix_skips self._posix_skips = frozenset( posixpath.abspath(skip_path.replace("\\", "/")) for skip_path in self.skips ) return self._posix_skips @property def skip_globs(self) -> frozenset[str]: if self._skip_globs is not None: return self._skip_globs self._skip_globs = self.skip_glob.union(self.extend_skip_glob) return self._skip_globs @property def sorting_function(self) -> Callable[..., list[str]]: if self._sorting_function is not None: return self._sorting_function if self.sort_order == "natural": from . import sorting # noqa: PLC0415 # Circular import... self._sorting_function = sorting.naturally elif self.sort_order == "native": self._sorting_function = sorted else: available_sort_orders = ["natural", "native"] for sort_plugin in entry_points(group="isort.sort_function"): available_sort_orders.append(sort_plugin.name) if sort_plugin.name == self.sort_order: self._sorting_function = sort_plugin.load() break else: raise SortingFunctionDoesNotExist(self.sort_order, available_sort_orders) return self._sorting_function def _parse_known_pattern(self, pattern: str) -> list[str]: """Expand pattern if identified as a directory and return found sub packages""" if pattern.endswith(os.path.sep): patterns = [ filename for filename in os.listdir(os.path.join(self.directory, pattern)) if os.path.isdir(os.path.join(self.directory, pattern, filename)) ] else: patterns = [pattern] return patterns def _get_str_to_type_converter(setting_name: str) -> Callable[[object], object]: type_converter: Callable[[object], object] = type(_DEFAULT_SETTINGS.get(setting_name, "")) if type_converter == WrapModes: type_converter = wrap_mode_from_string return type_converter def _as_list(value: object) -> list[str]: if isinstance(value, list): return [item.strip() for item in value] filtered = [item.strip() for item in str(value).replace("\n", ",").split(",") if item.strip()] return filtered def _abspaths(cwd: str, values: Iterable[str]) -> set[str]: paths = { ( os.path.join(cwd, value) if not value.startswith(os.path.sep) and value.endswith(os.path.sep) else value ) for value in values } return paths def _find_config(path: str) -> tuple[str, dict[str, Any]]: current_directory = path tries = 0 while current_directory and tries < MAX_CONFIG_SEARCH_DEPTH: for config_file_name in CONFIG_SOURCES: potential_config_file = os.path.join(current_directory, config_file_name) if os.path.isfile(potential_config_file): config_data: dict[str, Any] try: config_data = _get_config_data( potential_config_file, CONFIG_SECTIONS[config_file_name] ) except Exception: warn( f"Failed to pull configuration information from {potential_config_file}", stacklevel=2, ) config_data = {} if config_data: return (current_directory, config_data) for stop_dir in STOP_CONFIG_SEARCH_ON_DIRS: if os.path.isdir(os.path.join(current_directory, stop_dir)): return (current_directory, {}) new_directory = os.path.split(current_directory)[0] if new_directory == current_directory: break current_directory = new_directory tries += 1 return (path, {}) def find_all_configs(path: str) -> Trie: """ Looks for config files in the path provided and in all of its sub-directories. Parses and stores any config file encountered in a trie and returns the root of the trie """ trie_root = Trie("default", {}) for dirpath, _, _ in os.walk(path): for config_file_name in CONFIG_SOURCES: potential_config_file = os.path.join(dirpath, config_file_name) if os.path.isfile(potential_config_file): config_data: dict[str, Any] try: config_data = _get_config_data( potential_config_file, CONFIG_SECTIONS[config_file_name] ) except Exception: warn( f"Failed to pull configuration information from {potential_config_file}", stacklevel=2, ) config_data = {} if config_data: trie_root.insert(potential_config_file, config_data) break return trie_root def _get_config_data(file_path: str, sections: tuple[str, ...]) -> dict[str, object]: settings: dict[str, object] = {} if file_path.endswith(".toml"): with open(file_path, "rb") as bin_config_file: config = tomllib.load(bin_config_file) for section in sections: config_section = config for key in section.split("."): config_section = config_section.get(key, {}) settings.update(config_section) else: with open(file_path, encoding="utf-8") as config_file: if file_path.endswith(".editorconfig"): line = "\n" last_position = config_file.tell() while line: line = config_file.readline() if "[" in line: config_file.seek(last_position) break last_position = config_file.tell() config_parser = configparser.ConfigParser(strict=False) config_parser.read_file(config_file) for section in sections: if section.startswith("*.{") and section.endswith("}"): extension = section[len("*.{") : -1] for config_key in config_parser: if ( config_key.startswith("*.{") and config_key.endswith("}") and extension in (text.strip() for text in config_key[len("*.{") : -1].split(",")) ): settings.update(config_parser.items(config_key)) elif config_parser.has_section(section): settings.update(config_parser.items(section)) if settings: settings["source"] = file_path if file_path.endswith(".editorconfig"): indent_style = str(settings.pop("indent_style", "")).strip() indent_size = str(settings.pop("indent_size", "")).strip() if indent_size == "tab": indent_size = str(settings.pop("tab_width", "")).strip() if indent_style == "space": settings["indent"] = " " * ((indent_size and int(indent_size)) or 4) elif indent_style == "tab": settings["indent"] = "\t" * ((indent_size and int(indent_size)) or 1) max_line_length = str(settings.pop("max_line_length", "")).strip() if max_line_length and (max_line_length == "off" or max_line_length.isdigit()): settings["line_length"] = ( float("inf") if max_line_length == "off" else int(max_line_length) ) settings = { key: value for key, value in settings.items() if key in _DEFAULT_SETTINGS or key.startswith(KNOWN_PREFIX) } for key, value in settings.items(): existing_value_type = _get_str_to_type_converter(key) if existing_value_type is tuple: settings[key] = tuple(_as_list(value)) elif existing_value_type is frozenset: settings[key] = frozenset(_as_list(settings.get(key))) elif existing_value_type is bool: # Only some configuration formats support native boolean values. if not isinstance(value, bool): value = _as_bool(value) settings[key] = value elif key.startswith(KNOWN_PREFIX): settings[key] = _abspaths(os.path.dirname(file_path), _as_list(value)) elif key == "force_grid_wrap": try: result = existing_value_type(value) except ValueError: # backwards compatibility for true / false force grid wrap result = 0 if str(value).lower().strip() == "false" else 2 settings[key] = result elif key == "comment_prefix": settings[key] = str(value).strip("'").strip('"') else: settings[key] = existing_value_type(value) return settings def _as_bool(value: object) -> bool: """Given a string value that represents True or False, returns the Boolean equivalent. Heavily inspired from distutils strtobool. """ try: return _STR_BOOLEAN_MAPPING[str(value).lower()] except KeyError: raise ValueError(f"invalid truth value {value}") def entry_points(group: str) -> "EntryPoints": """Call entry_point after lazy loading it. TODO: The reason for lazy loading here are unknown. """ from importlib.metadata import entry_points as ep # noqa: PLC0415 return ep(group=group) DEFAULT_CONFIG = Config() isort-9.0.1/isort/sorting.py0000644000000000000000000001052313615410400013001 0ustar00import re from collections.abc import Callable, Iterable from typing import Any from .settings import Config _import_line_intro_re = re.compile("^(?:from|import) ") _import_line_midline_import_re = re.compile(" import ") def module_key( module_name: str, config: Config, sub_imports: bool = False, ignore_case: bool = False, section_name: Any | None = None, straight_import: bool | None = False, ) -> str: match = re.match(r"^(\.+)\s*(.*)", module_name) if match: sep = " " if config.reverse_relative else "_" module_name = sep.join(match.groups()) prefix = "" if ignore_case: module_name = str(module_name).lower() else: module_name = str(module_name) if sub_imports and config.order_by_type: if module_name in config.constants: prefix = "A" elif module_name in config.classes: prefix = "B" elif module_name in config.variables: prefix = "C" elif module_name.isupper() and len(module_name) > 1: # see issue #376 prefix = "A" elif module_name in config.classes or module_name[0:1].isupper(): prefix = "B" else: prefix = "C" if not config.case_sensitive: module_name = module_name.lower() length_sort = ( config.length_sort or (config.length_sort_straight and straight_import) or str(section_name).lower() in config.length_sort_sections ) _length_sort_maybe = (str(len(module_name)) + ":" + module_name) if length_sort else module_name return f"{(module_name in config.force_to_top and 'A') or 'B'}{prefix}{_length_sort_maybe}" def section_key(line: str, config: Config) -> str: section = "B" if ( not config.sort_relative_in_force_sorted_sections and config.reverse_relative and line.startswith("from .") ): match = re.match(r"^from (\.+)\s*(.*)", line) if match: # pragma: no cover - regex always matches if line starts with "from ." line = f"from {' '.join(match.groups())}" if config.group_by_package and line.strip().startswith("from"): line = line.split(" import ", 1)[0] if config.lexicographical: line = _import_line_intro_re.sub("", _import_line_midline_import_re.sub(".", line)) else: line = re.sub("^from ", "", line) line = re.sub("^import ", "", line) if config.sort_relative_in_force_sorted_sections: sep = " " if config.reverse_relative else "_" line = re.sub(r"^(\.+)", rf"\1{sep}", line) if line.split(" ")[0] in config.force_to_top: section = "A" # * If honor_case_in_force_sorted_sections is true, and case_sensitive and # order_by_type are different, only ignore case in part of the line. # * Otherwise, let order_by_type decide the sorting of the whole line. This # is only "correct" if case_sensitive and order_by_type have the same value. if config.honor_case_in_force_sorted_sections and config.case_sensitive != config.order_by_type: split_module = line.split(" import ", 1) if len(split_module) > 1: module_name, names = split_module if not config.case_sensitive: module_name = module_name.lower() if not config.order_by_type: names = names.lower() line = f"{module_name} import {names}" elif not config.case_sensitive: line = line.lower() elif not config.order_by_type: line = line.lower() return f"{section}{len(line) if config.length_sort else ''}{line}" def sort( config: Config, to_sort: Iterable[str], key: Callable[[str], Any] | None = None, reverse: bool = False, ) -> list[str]: return config.sorting_function(to_sort, key=key, reverse=reverse) def naturally( to_sort: Iterable[str], key: Callable[[str], Any] | None = None, reverse: bool = False ) -> list[str]: """Returns a naturally sorted list""" if key is None: key_callback = _natural_keys else: def key_callback(text: str) -> list[Any]: return _natural_keys(key(text)) return sorted(to_sort, key=key_callback, reverse=reverse) def _atoi(text: str) -> Any: return int(text) if text.isdigit() else text def _natural_keys(text: str) -> list[Any]: return [_atoi(c) for c in re.split(r"(\d+)", text)] isort-9.0.1/isort/utils.py0000644000000000000000000000461113615410400012455 0ustar00import os import sys from functools import lru_cache from pathlib import Path from typing import Any class TrieNode: def __init__(self, config_file: str = "", config_data: dict[str, Any] | None = None) -> None: if not config_data: config_data = {} self.nodes: dict[str, TrieNode] = {} self.config_info: tuple[str, dict[str, Any]] = (config_file, config_data) class Trie: """ A prefix tree to store the paths of all config files and to search the nearest config associated with each file """ def __init__(self, config_file: str = "", config_data: dict[str, Any] | None = None) -> None: self.root: TrieNode = TrieNode(config_file, config_data) def insert(self, config_file: str, config_data: dict[str, Any]) -> None: resolved_config_path_as_tuple = Path(config_file).parent.resolve().parts temp = self.root for path in resolved_config_path_as_tuple: if path not in temp.nodes: temp.nodes[path] = TrieNode() temp = temp.nodes[path] temp.config_info = (config_file, config_data) def search(self, filename: str) -> tuple[str, dict[str, Any]]: """ Returns the closest config relative to filename by doing a depth first search on the prefix tree. """ resolved_file_path_as_tuple = Path(filename).resolve().parts temp = self.root last_stored_config: tuple[str, dict[str, Any]] = ("", {}) for path in resolved_file_path_as_tuple: if temp.config_info[0]: last_stored_config = temp.config_info if path not in temp.nodes: break temp = temp.nodes[path] return last_stored_config @lru_cache(maxsize=1000) def exists_case_sensitive(path: str) -> bool: """Returns if the given path exists and also matches the case on Windows. When finding files that can be imported, it is important for the cases to match because while file os.path.exists("module.py") and os.path.exists("MODULE.py") both return True on Windows, Python can only import using the case of the real file. """ result = os.path.exists(path) if result and (sys.platform.startswith("win") or sys.platform == "darwin"): # pragma: no cover directory, basename = os.path.split(path) result = basename in os.listdir(directory) return result isort-9.0.1/isort/wrap.py0000644000000000000000000001624213615410400012271 0ustar00import copy import re from collections.abc import Sequence from .settings import DEFAULT_CONFIG, Config from .wrap_modes import WrapModes as Modes from .wrap_modes import formatter_from_string, vertical_hanging_indent def import_statement( import_start: str, from_imports: list[str], comments: Sequence[str] | None = None, line_separator: str = "\n", config: Config = DEFAULT_CONFIG, multi_line_output: Modes | None = None, explode: bool = False, ) -> str: """Returns a multi-line wrapped form of the provided from import statement.""" if explode: formatter = vertical_hanging_indent line_length = 1 include_trailing_comma = True else: formatter = formatter_from_string((multi_line_output or config.multi_line_output).name) line_length = config.wrap_length or config.line_length include_trailing_comma = config.include_trailing_comma dynamic_indent = " " * (len(import_start) + 1) indent = config.indent statement = formatter( statement=import_start, imports=copy.copy(from_imports), white_space=dynamic_indent, indent=indent, line_length=line_length, comments=comments or [], line_separator=line_separator, comment_prefix=config.comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=config.ignore_comments, ) if config.balanced_wrapping: lines = statement.split(line_separator) line_count = len(lines) if len(lines) > 1: minimum_length = min(len(line) for line in lines[:-1]) else: minimum_length = 0 new_import_statement = statement while len(lines[-1]) < minimum_length and len(lines) == line_count and line_length > 10: statement = new_import_statement line_length -= 1 new_import_statement = formatter( statement=import_start, imports=copy.copy(from_imports), white_space=dynamic_indent, indent=indent, line_length=line_length, comments=comments or [], line_separator=line_separator, comment_prefix=config.comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=config.ignore_comments, ) lines = new_import_statement.split(line_separator) if statement.count(line_separator) == 0: return _wrap_line(statement, line_separator, config) return statement def line(content: str, line_separator: str, config: Config = DEFAULT_CONFIG) -> str: """Returns a line wrapped to the specified line-length, if possible.""" if len(content) <= config.line_length: return content wrap_mode = config.multi_line_output if wrap_mode is Modes.NOQA: if "# NOQA" not in content: return f"{content}{config.comment_prefix} NOQA" return content line_without_comment = content comment = None if "#" in content: line_without_comment, comment = content.split("#", 1) # A ``from ... import *`` / ``from ... cimport *`` statement cannot use # parenthesis-based wrapping because the wildcard ``*`` has no valid # continuation in that mode. Use a backslash continuation instead so the # statement is split across lines while remaining valid Python. # See https://github.com/PyCQA/isort/issues/2267 if line_without_comment.rstrip().endswith("*"): prefix, keyword, _ = line_without_comment.rstrip().rsplit(" ", 2) comment_suffix = f" #{comment}" if comment else "" return f"{prefix} {keyword} \\{line_separator}{config.indent}*{comment_suffix}" for splitter in ("import ", "cimport ", ".", "as "): exp = r"\b" + re.escape(splitter) + r"\b" if re.search(exp, line_without_comment) and not line_without_comment.strip().startswith( splitter ): line_parts = re.split(exp, line_without_comment) _is_vertical_mode = wrap_mode in ( Modes.VERTICAL_HANGING_INDENT, Modes.VERTICAL_GRID_GROUPED, ) # Determine whether the comment should be hoisted to the opening # parenthesis line rather than embedded in the import line. # This happens for noqa comments (when use_parentheses is True) # and for all comments in vertical hanging modes (when # use_parentheses is False), so that multi_line_output=3/5 is # respected even without an explicit use_parentheses=True setting. _hoist_comment_to_paren = comment and ( (config.use_parentheses and "noqa" in comment) or (_is_vertical_mode and not config.use_parentheses) ) if comment and not _hoist_comment_to_paren: _comma_maybe = ( "," if ( config.include_trailing_comma and config.use_parentheses and not line_without_comment.rstrip().endswith(",") ) else "" ) line_parts[-1] = ( f"{line_parts[-1].strip()}{_comma_maybe}{config.comment_prefix}{comment}" ) next_line = [] while (len(content) + 2) > (config.wrap_length or config.line_length) and line_parts: next_line.append(line_parts.pop()) content = splitter.join(line_parts) if not content: content = next_line.pop() cont_line = _wrap_line( config.indent + splitter.join(next_line).lstrip(), line_separator, config, ) if config.use_parentheses or _is_vertical_mode: if splitter == "as ": output = f"{content}{splitter}{cont_line.lstrip()}" else: _comma = "," if config.include_trailing_comma and not comment else "" if _is_vertical_mode: _separator = line_separator else: _separator = "" noqa_comment = "" if _hoist_comment_to_paren: noqa_comment = f"{config.comment_prefix}{comment}" cont_line = cont_line.rstrip() _comma = "," if config.include_trailing_comma else "" output = ( f"{content}{splitter}({noqa_comment}" f"{line_separator}{cont_line}{_comma}{_separator})" ) lines = output.split(line_separator) if config.comment_prefix in lines[-1] and lines[-1].endswith(")"): content, comment = lines[-1].split(config.comment_prefix, 1) lines[-1] = content + ")" + config.comment_prefix + comment[:-1] output = line_separator.join(lines) return output return f"{content}{splitter}\\{line_separator}{cont_line}" return content _wrap_line = line isort-9.0.1/isort/wrap_modes.py0000644000000000000000000003230313615410400013454 0ustar00"""Defines all wrap modes that can be used when outputting formatted imports""" import enum from collections.abc import Callable from typing import Any import isort.comments _wrap_modes: dict[str, Callable[..., str]] = {} def from_string(value: object) -> "WrapModes": value = str(value) return getattr(WrapModes, value, None) or WrapModes(int(value)) def formatter_from_string(name: str) -> Callable[..., str]: return _wrap_modes.get(name.upper(), grid) def _wrap_mode_interface( statement: str, imports: list[str], white_space: str, indent: str, line_length: int, comments: list[str], line_separator: str, comment_prefix: str, include_trailing_comma: bool, remove_comments: bool, ) -> str: """Defines the common interface used by all wrap mode functions""" return "" def _wrap_mode(function: Callable[..., str]) -> Callable[..., str]: """Registers an individual wrap mode. Function name and order are significant.""" _wrap_modes[function.__name__.upper()] = function return function @_wrap_mode def grid(**interface: Any) -> str: if not interface["imports"]: return "" interface["statement"] += "(" + interface["imports"].pop(0) while interface["imports"]: next_import = interface["imports"].pop(0) next_statement = isort.comments.add_to_line( interface["comments"], interface["statement"] + ", " + next_import, removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) if ( len(next_statement.split(interface["line_separator"])[-1]) + 1 > interface["line_length"] ): lines = [f"{interface['white_space']}{next_import.split(' ')[0]}"] for part in next_import.split(" ")[1:]: new_line = f"{lines[-1]} {part}" if len(new_line) + 1 > interface["line_length"]: lines.append(f"{interface['white_space']}{part}") else: lines[-1] = new_line next_import = interface["line_separator"].join(lines) interface["statement"] = ( isort.comments.add_to_line( interface["comments"], f"{interface['statement']},", removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) + f"{interface['line_separator']}{next_import}" ) interface["comments"] = [] else: interface["statement"] += ", " + next_import return f"{interface['statement']}{',' if interface['include_trailing_comma'] else ''})" @_wrap_mode def vertical(**interface: Any) -> str: if not interface["imports"]: return "" first_import = ( isort.comments.add_to_line( interface["comments"], interface["imports"].pop(0) + ",", removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) + interface["line_separator"] + interface["white_space"] ) _imports = ("," + interface["line_separator"] + interface["white_space"]).join( interface["imports"] ) _comma_maybe = "," if interface["include_trailing_comma"] else "" return f"{interface['statement']}({first_import}{_imports}{_comma_maybe})" def _hanging_indent_end_line(line: str) -> str: if not line.endswith(" "): line += " " return line + "\\" @_wrap_mode def hanging_indent(**interface: Any) -> str: if not interface["imports"]: return "" line_length_limit = interface["line_length"] - 3 next_import = interface["imports"].pop(0) next_statement = interface["statement"] + next_import # Check for first import if len(next_statement) > line_length_limit: next_statement = ( _hanging_indent_end_line(interface["statement"]) + interface["line_separator"] + interface["indent"] + next_import ) interface["statement"] = next_statement while interface["imports"]: next_import = interface["imports"].pop(0) next_statement = interface["statement"] + ", " + next_import if len(next_statement.split(interface["line_separator"])[-1]) > line_length_limit: next_statement = ( _hanging_indent_end_line(interface["statement"] + ",") + f"{interface['line_separator']}{interface['indent']}{next_import}" ) interface["statement"] = next_statement if interface["comments"]: statement_with_comments = isort.comments.add_to_line( interface["comments"], interface["statement"], removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) if len(statement_with_comments.split(interface["line_separator"])[-1]) <= ( line_length_limit + 2 ): return statement_with_comments return ( _hanging_indent_end_line(interface["statement"]) + str(interface["line_separator"]) + isort.comments.add_to_line( interface["comments"], interface["indent"], removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"].lstrip(), ) ) return str(interface["statement"]) @_wrap_mode def vertical_hanging_indent(**interface: Any) -> str: _line_with_comments = isort.comments.add_to_line( interface["comments"], "", removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) _imports = ("," + interface["line_separator"] + interface["indent"]).join(interface["imports"]) _comma_maybe = "," if interface["include_trailing_comma"] else "" return ( f"{interface['statement']}({_line_with_comments}{interface['line_separator']}" f"{interface['indent']}{_imports}{_comma_maybe}{interface['line_separator']})" ) def _vertical_grid_common(need_trailing_char: bool, **interface: Any) -> str: if not interface["imports"]: return "" interface["statement"] += ( isort.comments.add_to_line( interface["comments"], "(", removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) + interface["line_separator"] + interface["indent"] + interface["imports"].pop(0) ) while interface["imports"]: next_import = interface["imports"].pop(0) next_statement = f"{interface['statement']}, {next_import}" current_line_length = len(next_statement.split(interface["line_separator"])[-1]) if interface["imports"] or interface["include_trailing_comma"]: # We need to account for a comma after this import. current_line_length += 1 if not interface["imports"] and need_trailing_char: # We need to account for a closing ) we're going to add. current_line_length += 1 if current_line_length > interface["line_length"]: next_statement = ( f"{interface['statement']},{interface['line_separator']}" f"{interface['indent']}{next_import}" ) interface["statement"] = next_statement if interface["include_trailing_comma"]: interface["statement"] += "," return str(interface["statement"]) @_wrap_mode def vertical_grid(**interface: Any) -> str: return _vertical_grid_common(need_trailing_char=True, **interface) + ")" @_wrap_mode def vertical_grid_grouped(**interface: Any) -> str: return ( _vertical_grid_common(need_trailing_char=False, **interface) + str(interface["line_separator"]) + ")" ) @_wrap_mode def vertical_grid_grouped_no_comma(**interface: Any) -> str: # This is a deprecated alias for vertical_grid_grouped above. This function # needs to exist for backwards compatibility but should never get called. raise NotImplementedError @_wrap_mode def noqa(**interface: Any) -> str: _imports = ", ".join(interface["imports"]) retval = f"{interface['statement']}{_imports}" comment_str = " ".join(interface["comments"]) if interface["comments"]: if ( len(retval) + len(interface["comment_prefix"]) + 1 + len(comment_str) <= interface["line_length"] ): return f"{retval}{interface['comment_prefix']} {comment_str}" if "NOQA" in comment_str.split(): return f"{retval}{interface['comment_prefix']} {comment_str}" return f"{retval}{interface['comment_prefix']} NOQA {comment_str}" if len(retval) <= interface["line_length"]: return retval return f"{retval}{interface['comment_prefix']} NOQA" @_wrap_mode def vertical_hanging_indent_bracket(**interface: Any) -> str: if not interface["imports"]: return "" statement = vertical_hanging_indent(**interface) return f"{statement[:-1]}{interface['indent']})" @_wrap_mode def vertical_prefix_from_module_import(**interface: Any) -> str: if not interface["imports"]: return "" prefix_statement = interface["statement"] output_statement = prefix_statement + interface["imports"].pop(0) comments = interface["comments"] statement = output_statement statement_with_comments = "" for next_import in interface["imports"]: statement = statement + ", " + next_import statement_with_comments = isort.comments.add_to_line( comments, statement, removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) if ( len(statement_with_comments.split(interface["line_separator"])[-1]) + 1 > interface["line_length"] ): statement = ( isort.comments.add_to_line( comments, output_statement, removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) + f"{interface['line_separator']}{prefix_statement}{next_import}" ) comments = [] output_statement = statement if comments and statement_with_comments: output_statement = statement_with_comments return str(output_statement) @_wrap_mode def hanging_indent_with_parentheses(**interface: Any) -> str: if not interface["imports"]: return "" line_length_limit = interface["line_length"] - 1 interface["statement"] += "(" next_import = interface["imports"].pop(0) next_statement = interface["statement"] + next_import # Check for first import if len(next_statement) > line_length_limit: next_statement = ( isort.comments.add_to_line( interface["comments"], interface["statement"], removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) + f"{interface['line_separator']}{interface['indent']}{next_import}" ) interface["comments"] = [] interface["statement"] = next_statement while interface["imports"]: next_import = interface["imports"].pop(0) if ( interface["line_separator"] not in interface["statement"] and "#" in interface["statement"] ): # pragma: no cover # TODO: fix, this is because of test run inconsistency. line, comments = interface["statement"].split("#", 1) next_statement = ( f"{line.rstrip()}, {next_import}{interface['comment_prefix']}{comments}" ) else: next_statement = isort.comments.add_to_line( interface["comments"], interface["statement"] + ", " + next_import, removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) current_line = next_statement.split(interface["line_separator"])[-1] if len(current_line) > line_length_limit: next_statement = ( isort.comments.add_to_line( interface["comments"], interface["statement"] + ",", removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) + f"{interface['line_separator']}{interface['indent']}{next_import}" ) interface["comments"] = [] interface["statement"] = next_statement return f"{interface['statement']}{',' if interface['include_trailing_comma'] else ''})" @_wrap_mode def backslash_grid(**interface: Any) -> str: interface["indent"] = interface["white_space"][:-1] return hanging_indent(**interface) class WrapModes(enum.IntEnum): GRID = 0 VERTICAL = 1 HANGING_INDENT = 2 VERTICAL_HANGING_INDENT = 3 VERTICAL_GRID = 4 VERTICAL_GRID_GROUPED = 5 VERTICAL_GRID_GROUPED_NO_COMMA = 6 NOQA = 7 VERTICAL_HANGING_INDENT_BRACKET = 8 VERTICAL_PREFIX_FROM_MODULE_IMPORT = 9 HANGING_INDENT_WITH_PARENTHESES = 10 BACKSLASH_GRID = 11 isort-9.0.1/isort/_vendored/tomli/LICENSE0000644000000000000000000000206013615410400015035 0ustar00MIT License Copyright (c) 2021 Taneli Hukkinen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. isort-9.0.1/isort/_vendored/tomli/__init__.py0000644000000000000000000000047213615410400016146 0ustar00# SPDX-License-Identifier: MIT # SPDX-FileCopyrightText: 2021 Taneli Hukkinen # Licensed to PSF under a Contributor Agreement. __all__ = ("loads", "load", "TOMLDecodeError") __version__ = "2.4.1" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT from ._parser import TOMLDecodeError, load, loads isort-9.0.1/isort/_vendored/tomli/_parser.py0000644000000000000000000006332313615410400016046 0ustar00# SPDX-License-Identifier: MIT # SPDX-FileCopyrightText: 2021 Taneli Hukkinen # Licensed to PSF under a Contributor Agreement. from __future__ import annotations # Defer loading regular expressions until we actually need them in # parse_value(). __lazy_modules__ = ["tomli._re"] import sys from ._re import ( RE_DATETIME, RE_LOCALTIME, RE_NUMBER, match_to_datetime, match_to_localtime, match_to_number, ) if sys.version_info < (3, 15): # pragma: no cover from types import MappingProxyType as frozendict TYPE_CHECKING = False if TYPE_CHECKING: from collections.abc import Iterable from typing import IO, Any, Final from ._types import Key, ParseFloat, Pos # Inline tables/arrays are implemented using recursion. Pathologically # nested documents cause pure Python to raise RecursionError (which is OK), # but mypyc binary wheels will crash unrecoverably (not OK). According to # mypyc docs this will be fixed in the future: # https://mypyc.readthedocs.io/en/latest/differences_from_python.html#stack-overflows # Before mypyc's fix is in, recursion needs to be limited by this library. # Choosing `sys.getrecursionlimit()` as maximum inline table/array nesting # level, as it allows more nesting than pure Python, but still seems a far # lower number than where mypyc binaries crash. MAX_INLINE_NESTING: Final = sys.getrecursionlimit() # Pathologically excessive number of parts in a key runs into quadratic # behavior (e.g. in Flags.is_). # Even if keys aren't currently parsed using recursion, they name a # recursive structure, so it makes sense to limit it using getrecursionlimit() # and RecursionError. MAX_KEY_PARTS: Final = sys.getrecursionlimit() ASCII_CTRL: Final = frozenset(chr(i) for i in range(32)) | frozenset(chr(127)) # Neither of these sets include quotation mark or backslash. They are # currently handled as separate cases in the parser functions. ILLEGAL_BASIC_STR_CHARS: Final = ASCII_CTRL - frozenset("\t") ILLEGAL_MULTILINE_BASIC_STR_CHARS: Final = ASCII_CTRL - frozenset("\t\n") ILLEGAL_LITERAL_STR_CHARS: Final = ILLEGAL_BASIC_STR_CHARS ILLEGAL_MULTILINE_LITERAL_STR_CHARS: Final = ILLEGAL_MULTILINE_BASIC_STR_CHARS ILLEGAL_COMMENT_CHARS: Final = ILLEGAL_BASIC_STR_CHARS TOML_WS: Final = frozenset(" \t") TOML_WS_AND_NEWLINE: Final = TOML_WS | frozenset("\n") BARE_KEY_CHARS: Final = frozenset( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" ) KEY_INITIAL_CHARS: Final = BARE_KEY_CHARS | frozenset("\"'") HEXDIGIT_CHARS: Final = frozenset("abcdefABCDEF0123456789") BASIC_STR_ESCAPE_REPLACEMENTS: Final = frozendict( { "\\b": "\u0008", # backspace "\\t": "\u0009", # tab "\\n": "\u000a", # linefeed "\\f": "\u000c", # form feed "\\r": "\u000d", # carriage return "\\e": "\u001b", # escape '\\"': "\u0022", # quote "\\\\": "\u005c", # backslash } ) class DEPRECATED_DEFAULT: """Sentinel to be used as default arg during deprecation period of TOMLDecodeError's free-form arguments.""" class TOMLDecodeError(ValueError): """An error raised if a document is not valid TOML. Adds the following attributes to ValueError: msg: The unformatted error message doc: The TOML document being parsed pos: The index of doc where parsing failed lineno: The line corresponding to pos colno: The column corresponding to pos """ def __init__( self, msg: str | type[DEPRECATED_DEFAULT] = DEPRECATED_DEFAULT, doc: str | type[DEPRECATED_DEFAULT] = DEPRECATED_DEFAULT, pos: Pos | type[DEPRECATED_DEFAULT] = DEPRECATED_DEFAULT, *args: Any, ): if args or not isinstance(msg, str) or not isinstance(doc, str) or not isinstance(pos, int): import warnings warnings.warn( "Free-form arguments for TOMLDecodeError are deprecated. " "Please set 'msg' (str), 'doc' (str) and 'pos' (int) arguments only.", DeprecationWarning, stacklevel=2, ) if pos is not DEPRECATED_DEFAULT: args = pos, *args if doc is not DEPRECATED_DEFAULT: args = doc, *args if msg is not DEPRECATED_DEFAULT: args = msg, *args ValueError.__init__(self, *args) return lineno = doc.count("\n", 0, pos) + 1 if lineno == 1: colno = pos + 1 else: colno = pos - doc.rindex("\n", 0, pos) if pos >= len(doc): coord_repr = "end of document" else: coord_repr = f"line {lineno}, column {colno}" errmsg = f"{msg} (at {coord_repr})" ValueError.__init__(self, errmsg) self.msg = msg self.doc = doc self.pos = pos self.lineno = lineno self.colno = colno def load(__fp: IO[bytes], *, parse_float: ParseFloat = float) -> dict[str, Any]: """Parse TOML from a binary file object.""" b = __fp.read() try: s = b.decode() except AttributeError: raise TypeError( "File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`" ) from None return loads(s, parse_float=parse_float) def loads(__s: str, *, parse_float: ParseFloat = float) -> dict[str, Any]: """Parse TOML from a string.""" # The spec allows converting "\r\n" to "\n", even in string # literals. Let's do so to simplify parsing. try: src = __s.replace("\r\n", "\n") except (AttributeError, TypeError): raise TypeError(f"Expected str object, not '{type(__s).__qualname__}'") from None pos = 0 out = Output() header: Key = () parse_float = make_safe_parse_float(parse_float) # Parse one statement at a time # (typically means one line in TOML source) while True: # 1. Skip line leading whitespace pos = skip_chars(src, pos, TOML_WS) # 2. Parse rules. Expect one of the following: # - end of file # - end of line # - comment # - key/value pair # - append dict to list (and move to its namespace) # - create dict (and move to its namespace) # Skip trailing whitespace when applicable. try: char = src[pos] except IndexError: break if char == "\n": pos += 1 continue if char in KEY_INITIAL_CHARS: pos = key_value_rule(src, pos, out, header, parse_float) pos = skip_chars(src, pos, TOML_WS) elif char == "[": try: second_char: str | None = src[pos + 1] except IndexError: second_char = None out.flags.finalize_pending() if second_char == "[": pos, header = create_list_rule(src, pos, out) else: pos, header = create_dict_rule(src, pos, out) pos = skip_chars(src, pos, TOML_WS) elif char != "#": raise TOMLDecodeError("Invalid statement", src, pos) # 3. Skip comment pos = skip_comment(src, pos) # 4. Expect end of line or end of file try: char = src[pos] except IndexError: break if char != "\n": raise TOMLDecodeError("Expected newline or end of document after a statement", src, pos) pos += 1 return out.data.dict class Flags: """Flags that map to parsed keys/namespaces.""" # Marks an immutable namespace (inline array or inline table). FROZEN: Final = 0 # Marks a nest that has been explicitly created and can no longer # be opened using the "[table]" syntax. EXPLICIT_NEST: Final = 1 def __init__(self) -> None: self._flags: dict[str, dict[Any, Any]] = {} self._pending_flags: set[tuple[Key, int]] = set() def add_pending(self, key: Key, flag: int) -> None: self._pending_flags.add((key, flag)) def finalize_pending(self) -> None: for key, flag in self._pending_flags: self.set(key, flag, recursive=False) self._pending_flags.clear() def unset_all(self, key: Key) -> None: cont = self._flags for k in key[:-1]: if k not in cont: return cont = cont[k]["nested"] cont.pop(key[-1], None) def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003 cont = self._flags key_parent, key_stem = key[:-1], key[-1] for k in key_parent: if k not in cont: cont[k] = {"flags": set(), "recursive_flags": set(), "nested": {}} cont = cont[k]["nested"] if key_stem not in cont: cont[key_stem] = {"flags": set(), "recursive_flags": set(), "nested": {}} cont[key_stem]["recursive_flags" if recursive else "flags"].add(flag) def is_(self, key: Key, flag: int) -> bool: if not key: return False # document root has no flags cont = self._flags for k in key[:-1]: if k not in cont: return False inner_cont = cont[k] if flag in inner_cont["recursive_flags"]: return True cont = inner_cont["nested"] key_stem = key[-1] if key_stem in cont: inner_cont = cont[key_stem] return flag in inner_cont["flags"] or flag in inner_cont["recursive_flags"] return False class NestedDict: def __init__(self) -> None: # The parsed content of the TOML document self.dict: dict[str, Any] = {} def get_or_create_nest( self, key: Key, *, access_lists: bool = True, ) -> dict[str, Any]: cont: Any = self.dict for k in key: if k not in cont: cont[k] = {} cont = cont[k] if access_lists and isinstance(cont, list): cont = cont[-1] if not isinstance(cont, dict): raise KeyError("There is no nest behind this key") return cont # type: ignore[no-any-return] def append_nest_to_list(self, key: Key) -> None: cont = self.get_or_create_nest(key[:-1]) last_key = key[-1] if last_key in cont: list_ = cont[last_key] if not isinstance(list_, list): raise KeyError("An object other than list found behind this key") list_.append({}) else: cont[last_key] = [{}] class Output: def __init__(self) -> None: self.data = NestedDict() self.flags = Flags() def skip_chars(src: str, pos: Pos, chars: Iterable[str]) -> Pos: try: while src[pos] in chars: pos += 1 except IndexError: pass return pos def skip_until( src: str, pos: Pos, expect: str, *, error_on: frozenset[str], error_on_eof: bool, ) -> Pos: try: new_pos = src.index(expect, pos) except ValueError: new_pos = len(src) if error_on_eof: raise TOMLDecodeError(f"Expected {expect!r}", src, new_pos) from None if not error_on.isdisjoint(src[pos:new_pos]): while src[pos] not in error_on: pos += 1 raise TOMLDecodeError(f"Found invalid character {src[pos]!r}", src, pos) return new_pos def skip_comment(src: str, pos: Pos) -> Pos: try: char: str | None = src[pos] except IndexError: char = None if char == "#": return skip_until(src, pos + 1, "\n", error_on=ILLEGAL_COMMENT_CHARS, error_on_eof=False) return pos def skip_comments_and_array_ws(src: str, pos: Pos) -> Pos: while True: pos_before_skip = pos pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE) pos = skip_comment(src, pos) if pos == pos_before_skip: return pos def create_dict_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]: pos += 1 # Skip "[" pos = skip_chars(src, pos, TOML_WS) pos, key = parse_key(src, pos) if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN): raise TOMLDecodeError(f"Cannot declare {key} twice", src, pos) out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False) try: out.data.get_or_create_nest(key) except KeyError: raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None if not src.startswith("]", pos): raise TOMLDecodeError("Expected ']' at the end of a table declaration", src, pos) return pos + 1, key def create_list_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]: pos += 2 # Skip "[[" pos = skip_chars(src, pos, TOML_WS) pos, key = parse_key(src, pos) if out.flags.is_(key, Flags.FROZEN): raise TOMLDecodeError(f"Cannot mutate immutable namespace {key}", src, pos) # Free the namespace now that it points to another empty list item... out.flags.unset_all(key) # ...but this key precisely is still prohibited from table declaration out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False) try: out.data.append_nest_to_list(key) except KeyError: raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None if not src.startswith("]]", pos): raise TOMLDecodeError("Expected ']]' at the end of an array declaration", src, pos) return pos + 2, key def key_value_rule(src: str, pos: Pos, out: Output, header: Key, parse_float: ParseFloat) -> Pos: pos, key, value = parse_key_value_pair(src, pos, parse_float, nest_lvl=0) key_parent, key_stem = key[:-1], key[-1] abs_key_parent = header + key_parent relative_path_cont_keys = (header + key[:i] for i in range(1, len(key))) for cont_key in relative_path_cont_keys: # Check that dotted key syntax does not redefine an existing table if out.flags.is_(cont_key, Flags.EXPLICIT_NEST): raise TOMLDecodeError(f"Cannot redefine namespace {cont_key}", src, pos) # Containers in the relative path can't be opened with the table syntax or # dotted key/value syntax in following table sections. out.flags.add_pending(cont_key, Flags.EXPLICIT_NEST) if out.flags.is_(abs_key_parent, Flags.FROZEN): raise TOMLDecodeError(f"Cannot mutate immutable namespace {abs_key_parent}", src, pos) try: nest = out.data.get_or_create_nest(abs_key_parent) except KeyError: raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None if key_stem in nest: raise TOMLDecodeError("Cannot overwrite a value", src, pos) # Mark inline table and array namespaces recursively immutable if isinstance(value, (dict, list)): out.flags.set(header + key, Flags.FROZEN, recursive=True) nest[key_stem] = value return pos def parse_key_value_pair( src: str, pos: Pos, parse_float: ParseFloat, nest_lvl: int ) -> tuple[Pos, Key, Any]: pos, key = parse_key(src, pos) try: char: str | None = src[pos] except IndexError: char = None if char != "=": raise TOMLDecodeError("Expected '=' after a key in a key/value pair", src, pos) pos += 1 pos = skip_chars(src, pos, TOML_WS) pos, value = parse_value(src, pos, parse_float, nest_lvl) return pos, key, value def parse_key(src: str, pos: Pos) -> tuple[Pos, Key]: pos, key_part = parse_key_part(src, pos) key: Key = (key_part,) pos = skip_chars(src, pos, TOML_WS) while True: try: char: str | None = src[pos] except IndexError: char = None if char != ".": return pos, key pos += 1 pos = skip_chars(src, pos, TOML_WS) pos, key_part = parse_key_part(src, pos) key += (key_part,) if len(key) > MAX_KEY_PARTS: raise RecursionError(f"TOML key has more than the allowed {MAX_KEY_PARTS} parts") pos = skip_chars(src, pos, TOML_WS) def parse_key_part(src: str, pos: Pos) -> tuple[Pos, str]: try: char: str | None = src[pos] except IndexError: char = None if char in BARE_KEY_CHARS: start_pos = pos pos = skip_chars(src, pos, BARE_KEY_CHARS) return pos, src[start_pos:pos] if char == "'": return parse_literal_str(src, pos) if char == '"': return parse_one_line_basic_str(src, pos) raise TOMLDecodeError("Invalid initial character for a key part", src, pos) def parse_one_line_basic_str(src: str, pos: Pos) -> tuple[Pos, str]: pos += 1 return parse_basic_str(src, pos, multiline=False) def parse_array( src: str, pos: Pos, parse_float: ParseFloat, nest_lvl: int ) -> tuple[Pos, list[Any]]: pos += 1 array: list[Any] = [] pos = skip_comments_and_array_ws(src, pos) if src.startswith("]", pos): return pos + 1, array while True: pos, val = parse_value(src, pos, parse_float, nest_lvl) array.append(val) pos = skip_comments_and_array_ws(src, pos) c = src[pos : pos + 1] if c == "]": return pos + 1, array if c != ",": raise TOMLDecodeError("Unclosed array", src, pos) pos += 1 pos = skip_comments_and_array_ws(src, pos) if src.startswith("]", pos): return pos + 1, array def parse_inline_table( src: str, pos: Pos, parse_float: ParseFloat, nest_lvl: int ) -> tuple[Pos, dict[str, Any]]: pos += 1 nested_dict = NestedDict() flags = Flags() pos = skip_comments_and_array_ws(src, pos) if src.startswith("}", pos): return pos + 1, nested_dict.dict while True: pos, key, value = parse_key_value_pair(src, pos, parse_float, nest_lvl) key_parent, key_stem = key[:-1], key[-1] if flags.is_(key, Flags.FROZEN): raise TOMLDecodeError(f"Cannot mutate immutable namespace {key}", src, pos) try: nest = nested_dict.get_or_create_nest(key_parent, access_lists=False) except KeyError: raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None if key_stem in nest: raise TOMLDecodeError(f"Duplicate inline table key {key_stem!r}", src, pos) nest[key_stem] = value pos = skip_comments_and_array_ws(src, pos) c = src[pos : pos + 1] if c == "}": return pos + 1, nested_dict.dict if c != ",": raise TOMLDecodeError("Unclosed inline table", src, pos) pos += 1 pos = skip_comments_and_array_ws(src, pos) if src.startswith("}", pos): return pos + 1, nested_dict.dict if isinstance(value, (dict, list)): flags.set(key, Flags.FROZEN, recursive=True) def parse_basic_str_escape(src: str, pos: Pos, *, multiline: bool = False) -> tuple[Pos, str]: escape_id = src[pos : pos + 2] pos += 2 if multiline and escape_id in {"\\ ", "\\\t", "\\\n"}: # Skip whitespace until next non-whitespace character or end of # the doc. Error if non-whitespace is found before newline. if escape_id != "\\\n": pos = skip_chars(src, pos, TOML_WS) try: char = src[pos] except IndexError: return pos, "" if char != "\n": raise TOMLDecodeError("Unescaped '\\' in a string", src, pos) pos += 1 pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE) return pos, "" if escape_id == "\\x": return parse_hex_char(src, pos, 2) if escape_id == "\\u": return parse_hex_char(src, pos, 4) if escape_id == "\\U": return parse_hex_char(src, pos, 8) try: return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id] except KeyError: raise TOMLDecodeError("Unescaped '\\' in a string", src, pos) from None def parse_basic_str_escape_multiline(src: str, pos: Pos) -> tuple[Pos, str]: return parse_basic_str_escape(src, pos, multiline=True) def parse_hex_char(src: str, pos: Pos, hex_len: int) -> tuple[Pos, str]: hex_str = src[pos : pos + hex_len] if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str): raise TOMLDecodeError("Invalid hex value", src, pos) pos += hex_len hex_int = int(hex_str, 16) if not is_unicode_scalar_value(hex_int): raise TOMLDecodeError("Escaped character is not a Unicode scalar value", src, pos) return pos, chr(hex_int) def parse_literal_str(src: str, pos: Pos) -> tuple[Pos, str]: pos += 1 # Skip starting apostrophe start_pos = pos pos = skip_until(src, pos, "'", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True) return pos + 1, src[start_pos:pos] # Skip ending apostrophe def parse_multiline_str(src: str, pos: Pos, *, literal: bool) -> tuple[Pos, str]: pos += 3 if src.startswith("\n", pos): pos += 1 if literal: delim = "'" end_pos = skip_until( src, pos, "'''", error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS, error_on_eof=True, ) result = src[pos:end_pos] pos = end_pos + 3 else: delim = '"' pos, result = parse_basic_str(src, pos, multiline=True) # Add at maximum two extra apostrophes/quotes if the end sequence # is 4 or 5 chars long instead of just 3. if not src.startswith(delim, pos): return pos, result pos += 1 if not src.startswith(delim, pos): return pos, result + delim pos += 1 return pos, result + (delim * 2) def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]: if multiline: error_on = ILLEGAL_MULTILINE_BASIC_STR_CHARS parse_escapes = parse_basic_str_escape_multiline else: error_on = ILLEGAL_BASIC_STR_CHARS parse_escapes = parse_basic_str_escape result = "" start_pos = pos while True: try: char = src[pos] except IndexError: raise TOMLDecodeError("Unterminated string", src, pos) from None if char == '"': if not multiline: return pos + 1, result + src[start_pos:pos] if src.startswith('"""', pos): return pos + 3, result + src[start_pos:pos] pos += 1 continue if char == "\\": result += src[start_pos:pos] pos, parsed_escape = parse_escapes(src, pos) result += parsed_escape start_pos = pos continue if char in error_on: raise TOMLDecodeError(f"Illegal character {char!r}", src, pos) pos += 1 def parse_value(src: str, pos: Pos, parse_float: ParseFloat, nest_lvl: int) -> tuple[Pos, Any]: if nest_lvl > MAX_INLINE_NESTING: # Pure Python should have raised RecursionError already. # This ensures mypyc binaries eventually do the same. raise RecursionError( # pragma: no cover "TOML inline arrays/tables are nested more than the allowed" f" {MAX_INLINE_NESTING} levels" ) try: char: str | None = src[pos] except IndexError: char = None # IMPORTANT: order conditions based on speed of checking and likelihood # Basic strings if char == '"': if src.startswith('"""', pos): return parse_multiline_str(src, pos, literal=False) return parse_one_line_basic_str(src, pos) # Literal strings if char == "'": if src.startswith("'''", pos): return parse_multiline_str(src, pos, literal=True) return parse_literal_str(src, pos) # Booleans if char == "t": if src.startswith("true", pos): return pos + 4, True if char == "f": if src.startswith("false", pos): return pos + 5, False # Arrays if char == "[": return parse_array(src, pos, parse_float, nest_lvl + 1) # Inline tables if char == "{": return parse_inline_table(src, pos, parse_float, nest_lvl + 1) # Dates and times datetime_match = RE_DATETIME.match(src, pos) if datetime_match: try: datetime_obj = match_to_datetime(datetime_match) except ValueError as e: raise TOMLDecodeError("Invalid date or datetime", src, pos) from e return datetime_match.end(), datetime_obj localtime_match = RE_LOCALTIME.match(src, pos) if localtime_match: return localtime_match.end(), match_to_localtime(localtime_match) # Integers and "normal" floats. # The regex will greedily match any type starting with a decimal # char, so needs to be located after handling of dates and times. number_match = RE_NUMBER.match(src, pos) if number_match: return number_match.end(), match_to_number(number_match, parse_float) # Special floats first_three = src[pos : pos + 3] if first_three in {"inf", "nan"}: return pos + 3, parse_float(first_three) first_four = src[pos : pos + 4] if first_four in {"-inf", "+inf", "-nan", "+nan"}: return pos + 4, parse_float(first_four) raise TOMLDecodeError("Invalid value", src, pos) def is_unicode_scalar_value(codepoint: int) -> bool: return (0 <= codepoint <= 55295) or (57344 <= codepoint <= 1114111) def make_safe_parse_float(parse_float: ParseFloat) -> ParseFloat: """A decorator to make `parse_float` safe. `parse_float` must not return dicts or lists, because these types would be mixed with parsed TOML tables and arrays, thus confusing the parser. The returned decorated callable raises `ValueError` instead of returning illegal types. """ # The default `float` callable never returns illegal types. Optimize it. if parse_float is float: return float def safe_parse_float(float_str: str) -> Any: float_value = parse_float(float_str) if isinstance(float_value, (dict, list)): raise ValueError("parse_float must not return dicts or lists") return float_value return safe_parse_float isort-9.0.1/isort/_vendored/tomli/_re.py0000644000000000000000000000645613615410400015164 0ustar00# SPDX-License-Identifier: MIT # SPDX-FileCopyrightText: 2021 Taneli Hukkinen # Licensed to PSF under a Contributor Agreement. from __future__ import annotations import re from datetime import date, datetime, time, timedelta, timezone, tzinfo from functools import lru_cache TYPE_CHECKING = False if TYPE_CHECKING: from typing import Any, Final from ._types import ParseFloat _TIME_RE_STR: Final = r""" ([01][0-9]|2[0-3]) # hours :([0-5][0-9]) # minutes (?: :([0-5][0-9]) # optional seconds (?:\.([0-9]{1,6})[0-9]*)? # optional fractions of a second )? """ RE_NUMBER: Final = re.compile( r""" 0 (?: x[0-9A-Fa-f](?:_?[0-9A-Fa-f])* # hex | b[01](?:_?[01])* # bin | o[0-7](?:_?[0-7])* # oct ) | [+-]?(?:0|[1-9](?:_?[0-9])*) # dec, integer part (?P (?:\.[0-9](?:_?[0-9])*)? # optional fractional part (?:[eE][+-]?[0-9](?:_?[0-9])*)? # optional exponent part ) """, flags=re.VERBOSE, ) RE_LOCALTIME: Final = re.compile(_TIME_RE_STR, flags=re.VERBOSE) RE_DATETIME: Final = re.compile( rf""" ([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) # date, e.g. 1988-10-27 (?: [Tt ] {_TIME_RE_STR} (?:([Zz])|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))? # optional time offset )? """, flags=re.VERBOSE, ) def match_to_datetime(match: re.Match[str]) -> datetime | date: """Convert a `RE_DATETIME` match to `datetime.datetime` or `datetime.date`. Raises ValueError if the match does not correspond to a valid date or datetime. """ ( year_str, month_str, day_str, hour_str, minute_str, sec_str, micros_str, zulu_time, offset_sign_str, offset_hour_str, offset_minute_str, ) = match.groups() year, month, day = int(year_str), int(month_str), int(day_str) if hour_str is None: return date(year, month, day) hour, minute = int(hour_str), int(minute_str) sec = int(sec_str) if sec_str else 0 micros = int(micros_str.ljust(6, "0")) if micros_str else 0 if offset_sign_str: tz: tzinfo | None = cached_tz(offset_hour_str, offset_minute_str, offset_sign_str) elif zulu_time: tz = timezone.utc else: # local date-time tz = None return datetime(year, month, day, hour, minute, sec, micros, tzinfo=tz) # No need to limit cache size. This is only ever called on input # that matched RE_DATETIME, so there is an implicit bound of # 24 (hours) * 60 (minutes) * 2 (offset direction) = 2880. @lru_cache(maxsize=None) def cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone: sign = 1 if sign_str == "+" else -1 return timezone( timedelta( hours=sign * int(hour_str), minutes=sign * int(minute_str), ) ) def match_to_localtime(match: re.Match[str]) -> time: hour_str, minute_str, sec_str, micros_str = match.groups() sec = int(sec_str) if sec_str else 0 micros = int(micros_str.ljust(6, "0")) if micros_str else 0 return time(int(hour_str), int(minute_str), sec, micros) def match_to_number(match: re.Match[str], parse_float: ParseFloat) -> Any: if match.group("floatpart"): return parse_float(match.group()) return int(match.group(), 0) isort-9.0.1/isort/_vendored/tomli/_types.py0000644000000000000000000000037613615410400015715 0ustar00# SPDX-License-Identifier: MIT # SPDX-FileCopyrightText: 2021 Taneli Hukkinen # Licensed to PSF under a Contributor Agreement. from typing import Any, Callable, Tuple # Type annotations ParseFloat = Callable[[str], Any] Key = Tuple[str, ...] Pos = int isort-9.0.1/isort/_vendored/tomli/py.typed0000644000000000000000000000003213615410400015524 0ustar00# Marker file for PEP 561 isort-9.0.1/isort/stdlibs/__init__.py0000644000000000000000000000046413615410400014522 0ustar00from . import all as _all from . import py2, py3, py27, py36, py37, py38, py39, py310, py311, py312, py313, py314, py315 __all__ = ( "_all", "py2", "py3", "py27", "py36", "py37", "py38", "py39", "py310", "py311", "py312", "py313", "py314", "py315", ) isort-9.0.1/isort/stdlibs/all.py0000644000000000000000000000007113615410400013525 0ustar00from . import py2, py3 stdlib = py2.stdlib | py3.stdlib isort-9.0.1/isort/stdlibs/py2.py0000644000000000000000000000005113615410400013465 0ustar00from . import py27 stdlib = py27.stdlib isort-9.0.1/isort/stdlibs/py27.py0000644000000000000000000001063013615410400013560 0ustar00""" File contains the standard library of Python 2.7. DO NOT EDIT. If the standard library changes, a new list should be created using the mkstdlibs.py script. """ stdlib = { "AL", "BaseHTTPServer", "Bastion", "CGIHTTPServer", "Carbon", "ColorPicker", "ConfigParser", "Cookie", "DEVICE", "DocXMLRPCServer", "EasyDialogs", "FL", "FrameWork", "GL", "HTMLParser", "MacOS", "MimeWriter", "MiniAEFrame", "Nav", "PixMapWrapper", "Queue", "SUNAUDIODEV", "ScrolledText", "SimpleHTTPServer", "SimpleXMLRPCServer", "SocketServer", "StringIO", "Tix", "Tkinter", "UserDict", "UserList", "UserString", "W", "__builtin__", "_ast", "_winreg", "abc", "aepack", "aetools", "aetypes", "aifc", "al", "anydbm", "applesingle", "argparse", "array", "ast", "asynchat", "asyncore", "atexit", "audioop", "autoGIL", "base64", "bdb", "binascii", "binhex", "bisect", "bsddb", "buildtools", "bz2", "cPickle", "cProfile", "cStringIO", "calendar", "cd", "cfmfile", "cgi", "cgitb", "chunk", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "commands", "compileall", "compiler", "contextlib", "cookielib", "copy", "copy_reg", "crypt", "csv", "ctypes", "curses", "datetime", "dbhash", "dbm", "decimal", "difflib", "dircache", "dis", "distutils", "dl", "doctest", "dumbdbm", "dummy_thread", "dummy_threading", "email", "encodings", "ensurepip", "errno", "exceptions", "fcntl", "filecmp", "fileinput", "findertools", "fl", "flp", "fm", "fnmatch", "formatter", "fpectl", "fpformat", "fractions", "ftplib", "functools", "future_builtins", "gc", "gdbm", "gensuitemodule", "getopt", "getpass", "gettext", "gl", "glob", "grp", "gzip", "hashlib", "heapq", "hmac", "hotshot", "htmlentitydefs", "htmllib", "httplib", "ic", "icopen", "imageop", "imaplib", "imgfile", "imghdr", "imp", "importlib", "imputil", "inspect", "io", "itertools", "jpeg", "json", "keyword", "lib2to3", "linecache", "locale", "logging", "macerrors", "macostools", "macpath", "macresource", "mailbox", "mailcap", "marshal", "math", "md5", "mhlib", "mimetools", "mimetypes", "mimify", "mmap", "modulefinder", "msilib", "msvcrt", "multifile", "multiprocessing", "mutex", "netrc", "new", "nis", "nntplib", "ntpath", "numbers", "operator", "optparse", "os", "ossaudiodev", "parser", "pdb", "pickle", "pickletools", "pipes", "pkgutil", "platform", "plistlib", "popen2", "poplib", "posix", "posixfile", "posixpath", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "quopri", "random", "re", "readline", "resource", "rexec", "rfc822", "rlcompleter", "robotparser", "runpy", "sched", "select", "sets", "sgmllib", "sha", "shelve", "shlex", "shutil", "signal", "site", "smtpd", "smtplib", "sndhdr", "socket", "spwd", "sqlite3", "sre", "sre_compile", "sre_constants", "sre_parse", "ssl", "stat", "statvfs", "string", "stringprep", "struct", "subprocess", "sunau", "sunaudiodev", "symbol", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "telnetlib", "tempfile", "termios", "test", "textwrap", "thread", "threading", "time", "timeit", "token", "tokenize", "trace", "traceback", "ttk", "tty", "turtle", "types", "unicodedata", "unittest", "urllib", "urllib2", "urlparse", "user", "uu", "uuid", "videoreader", "warnings", "wave", "weakref", "webbrowser", "whichdb", "winsound", "wsgiref", "xdrlib", "xml", "xmlrpclib", "zipfile", "zipimport", "zlib", } isort-9.0.1/isort/stdlibs/py3.py0000644000000000000000000000042513615410400013473 0ustar00from . import py36, py37, py38, py39, py310, py311, py312, py313, py314, py315 stdlib = ( py36.stdlib | py37.stdlib | py38.stdlib | py39.stdlib | py310.stdlib | py311.stdlib | py312.stdlib | py313.stdlib | py314.stdlib | py315.stdlib ) isort-9.0.1/isort/stdlibs/py310.py0000644000000000000000000001176613615410400013646 0ustar00""" File contains the standard library of Python 3.10. DO NOT EDIT. If the standard library changes, a new list should be created using the mkstdlibs.py script. """ stdlib = { "__future__", "__hello__", "__phello__", "_abc", "_aix_support", "_ast", "_asyncio", "_bisect", "_blake2", "_bootsubprocess", "_bz2", "_codecs", "_codecs_cn", "_codecs_hk", "_codecs_iso2022", "_codecs_jp", "_codecs_kr", "_codecs_tw", "_collections", "_collections_abc", "_compat_pickle", "_compression", "_contextvars", "_crypt", "_csv", "_ctypes", "_ctypes_test", "_curses", "_curses_panel", "_datetime", "_dbm", "_decimal", "_elementtree", "_frozen_importlib", "_frozen_importlib_external", "_functools", "_gdbm", "_hashlib", "_heapq", "_imp", "_io", "_json", "_locale", "_lsprof", "_lzma", "_markupbase", "_md5", "_msi", "_multibytecodec", "_multiprocessing", "_opcode", "_operator", "_osx_support", "_overlapped", "_pickle", "_posixshmem", "_posixsubprocess", "_py_abc", "_pydecimal", "_pyio", "_queue", "_random", "_scproxy", "_sha1", "_sha256", "_sha3", "_sha512", "_signal", "_sitebuiltins", "_socket", "_sqlite3", "_sre", "_ssl", "_stat", "_statistics", "_string", "_strptime", "_struct", "_symtable", "_testbuffer", "_testcapi", "_testclinic", "_testconsole", "_testimportmultiple", "_testinternalcapi", "_testmultiphase", "_thread", "_threading_local", "_tkinter", "_tracemalloc", "_uuid", "_warnings", "_weakref", "_weakrefset", "_winapi", "_xxsubinterpreters", "_xxtestfuzz", "_zoneinfo", "abc", "aifc", "antigravity", "argparse", "array", "ast", "asynchat", "asyncio", "asyncore", "atexit", "audioop", "base64", "bdb", "binascii", "binhex", "bisect", "builtins", "bz2", "cProfile", "calendar", "cgi", "cgitb", "chunk", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "concurrent", "configparser", "contextlib", "contextvars", "copy", "copyreg", "crypt", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal", "difflib", "dis", "distutils", "doctest", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "fractions", "ftplib", "functools", "gc", "genericpath", "getopt", "getpass", "gettext", "glob", "graphlib", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http", "idlelib", "imaplib", "imghdr", "imp", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "lib2to3", "linecache", "locale", "logging", "lzma", "mailbox", "mailcap", "marshal", "math", "mimetypes", "mmap", "modulefinder", "msilib", "msvcrt", "multiprocessing", "netrc", "nis", "nntplib", "nt", "ntpath", "nturl2path", "numbers", "opcode", "operator", "optparse", "os", "ossaudiodev", "pathlib", "pdb", "pickle", "pickletools", "pipes", "pkgutil", "platform", "plistlib", "poplib", "posix", "posixpath", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "pydoc_data", "pyexpat", "queue", "quopri", "random", "re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "smtpd", "smtplib", "sndhdr", "socket", "socketserver", "spwd", "sqlite3", "sre_compile", "sre_constants", "sre_parse", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "sunau", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "telnetlib", "tempfile", "termios", "textwrap", "this", "threading", "time", "timeit", "tkinter", "token", "tokenize", "trace", "traceback", "tracemalloc", "tty", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "uu", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "winreg", "winsound", "wsgiref", "xdrlib", "xml", "xmlrpc", "xx", "xxlimited", "xxlimited_35", "xxsubtype", "zipapp", "zipfile", "zipimport", "zlib", "zoneinfo", } isort-9.0.1/isort/stdlibs/py311.py0000644000000000000000000001213413615410400013635 0ustar00""" File contains the standard library of Python 3.11. DO NOT EDIT. If the standard library changes, a new list should be created using the mkstdlibs.py script. """ stdlib = { "__future__", "__hello__", "__hello_alias__", "__hello_only__", "__phello__", "__phello_alias__", "_abc", "_aix_support", "_ast", "_asyncio", "_bisect", "_blake2", "_bootsubprocess", "_bz2", "_codecs", "_codecs_cn", "_codecs_hk", "_codecs_iso2022", "_codecs_jp", "_codecs_kr", "_codecs_tw", "_collections", "_collections_abc", "_compat_pickle", "_compression", "_contextvars", "_crypt", "_csv", "_ctypes", "_ctypes_test", "_curses", "_curses_panel", "_datetime", "_dbm", "_decimal", "_elementtree", "_frozen_importlib", "_frozen_importlib_external", "_functools", "_gdbm", "_hashlib", "_heapq", "_imp", "_io", "_json", "_locale", "_lsprof", "_lzma", "_markupbase", "_md5", "_msi", "_multibytecodec", "_multiprocessing", "_opcode", "_operator", "_osx_support", "_overlapped", "_pickle", "_posixshmem", "_posixsubprocess", "_py_abc", "_pydecimal", "_pyio", "_queue", "_random", "_scproxy", "_sha1", "_sha256", "_sha3", "_sha512", "_signal", "_sitebuiltins", "_socket", "_sqlite3", "_sre", "_ssl", "_stat", "_statistics", "_string", "_strptime", "_struct", "_symtable", "_testbuffer", "_testcapi", "_testclinic", "_testconsole", "_testimportmultiple", "_testinternalcapi", "_testmultiphase", "_thread", "_threading_local", "_tkinter", "_tokenize", "_tracemalloc", "_typing", "_uuid", "_warnings", "_weakref", "_weakrefset", "_winapi", "_xxsubinterpreters", "_xxtestfuzz", "_zoneinfo", "abc", "aifc", "antigravity", "argparse", "array", "ast", "asynchat", "asyncio", "asyncore", "atexit", "audioop", "base64", "bdb", "binascii", "bisect", "builtins", "bz2", "cProfile", "calendar", "cgi", "cgitb", "chunk", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "concurrent", "configparser", "contextlib", "contextvars", "copy", "copyreg", "crypt", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal", "difflib", "dis", "distutils", "doctest", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "fractions", "ftplib", "functools", "gc", "genericpath", "getopt", "getpass", "gettext", "glob", "graphlib", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http", "idlelib", "imaplib", "imghdr", "imp", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "lib2to3", "linecache", "locale", "logging", "lzma", "mailbox", "mailcap", "marshal", "math", "mimetypes", "mmap", "modulefinder", "msilib", "msvcrt", "multiprocessing", "netrc", "nis", "nntplib", "nt", "ntpath", "nturl2path", "numbers", "opcode", "operator", "optparse", "os", "ossaudiodev", "pathlib", "pdb", "pickle", "pickletools", "pipes", "pkgutil", "platform", "plistlib", "poplib", "posix", "posixpath", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "pydoc_data", "pyexpat", "queue", "quopri", "random", "re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "smtpd", "smtplib", "sndhdr", "socket", "socketserver", "spwd", "sqlite3", "sre_compile", "sre_constants", "sre_parse", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "sunau", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "telnetlib", "tempfile", "termios", "textwrap", "this", "threading", "time", "timeit", "tkinter", "token", "tokenize", "tomllib", "trace", "traceback", "tracemalloc", "tty", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "uu", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "winreg", "winsound", "wsgiref", "xdrlib", "xml", "xmlrpc", "xx", "xxlimited", "xxlimited_35", "xxsubtype", "zipapp", "zipfile", "zipimport", "zlib", "zoneinfo", } isort-9.0.1/isort/stdlibs/py312.py0000644000000000000000000001211213615410400013632 0ustar00""" File contains the standard library of Python 3.12. DO NOT EDIT. If the standard library changes, a new list should be created using the mkstdlibs.py script. """ stdlib = { "__future__", "__hello__", "__hello_alias__", "__hello_only__", "__phello__", "__phello_alias__", "_abc", "_aix_support", "_ast", "_asyncio", "_bisect", "_blake2", "_bz2", "_codecs", "_codecs_cn", "_codecs_hk", "_codecs_iso2022", "_codecs_jp", "_codecs_kr", "_codecs_tw", "_collections", "_collections_abc", "_compat_pickle", "_compression", "_contextvars", "_crypt", "_csv", "_ctypes", "_ctypes_test", "_curses", "_curses_panel", "_datetime", "_dbm", "_decimal", "_elementtree", "_frozen_importlib", "_frozen_importlib_external", "_functools", "_gdbm", "_hashlib", "_heapq", "_imp", "_io", "_json", "_locale", "_lsprof", "_lzma", "_markupbase", "_md5", "_msi", "_multibytecodec", "_multiprocessing", "_opcode", "_operator", "_osx_support", "_overlapped", "_pickle", "_posixshmem", "_posixsubprocess", "_py_abc", "_pydatetime", "_pydecimal", "_pyio", "_pylong", "_queue", "_random", "_scproxy", "_sha1", "_sha2", "_sha3", "_signal", "_sitebuiltins", "_socket", "_sqlite3", "_sre", "_ssl", "_stat", "_statistics", "_string", "_strptime", "_struct", "_symtable", "_testbuffer", "_testcapi", "_testclinic", "_testconsole", "_testimportmultiple", "_testinternalcapi", "_testmultiphase", "_testsinglephase", "_thread", "_threading_local", "_tkinter", "_tokenize", "_tracemalloc", "_typing", "_uuid", "_warnings", "_weakref", "_weakrefset", "_winapi", "_wmi", "_xxinterpchannels", "_xxsubinterpreters", "_xxtestfuzz", "_zoneinfo", "abc", "aifc", "antigravity", "argparse", "array", "ast", "asyncio", "atexit", "audioop", "base64", "bdb", "binascii", "bisect", "builtins", "bz2", "cProfile", "calendar", "cgi", "cgitb", "chunk", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "concurrent", "configparser", "contextlib", "contextvars", "copy", "copyreg", "crypt", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal", "difflib", "dis", "doctest", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "fractions", "ftplib", "functools", "gc", "genericpath", "getopt", "getpass", "gettext", "glob", "graphlib", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http", "idlelib", "imaplib", "imghdr", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "lib2to3", "linecache", "locale", "logging", "lzma", "mailbox", "mailcap", "marshal", "math", "mimetypes", "mmap", "modulefinder", "msilib", "msvcrt", "multiprocessing", "netrc", "nis", "nntplib", "nt", "ntpath", "nturl2path", "numbers", "opcode", "operator", "optparse", "os", "ossaudiodev", "pathlib", "pdb", "pickle", "pickletools", "pipes", "pkgutil", "platform", "plistlib", "poplib", "posix", "posixpath", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "pydoc_data", "pyexpat", "queue", "quopri", "random", "re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "smtplib", "sndhdr", "socket", "socketserver", "spwd", "sqlite3", "sre_compile", "sre_constants", "sre_parse", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "sunau", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "telnetlib", "tempfile", "termios", "textwrap", "this", "threading", "time", "timeit", "tkinter", "token", "tokenize", "tomllib", "trace", "traceback", "tracemalloc", "tty", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "uu", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "winreg", "winsound", "wsgiref", "xdrlib", "xml", "xmlrpc", "xx", "xxlimited", "xxlimited_35", "xxsubtype", "zipapp", "zipfile", "zipimport", "zlib", "zoneinfo", } isort-9.0.1/isort/stdlibs/py313.py0000644000000000000000000001207113615410400013637 0ustar00""" File contains the standard library of Python 3.13. DO NOT EDIT. If the standard library changes, a new list should be created using the mkstdlibs.py script. """ stdlib = { "__future__", "__hello__", "__hello_alias__", "__hello_only__", "__phello__", "__phello_alias__", "_abc", "_aix_support", "_android_support", "_apple_support", "_ast", "_asyncio", "_bisect", "_blake2", "_bz2", "_codecs", "_codecs_cn", "_codecs_hk", "_codecs_iso2022", "_codecs_jp", "_codecs_kr", "_codecs_tw", "_collections", "_collections_abc", "_colorize", "_compat_pickle", "_compression", "_contextvars", "_csv", "_ctypes", "_ctypes_test", "_curses", "_curses_panel", "_datetime", "_dbm", "_decimal", "_elementtree", "_frozen_importlib", "_frozen_importlib_external", "_functools", "_gdbm", "_hashlib", "_heapq", "_imp", "_interpchannels", "_interpqueues", "_interpreters", "_io", "_ios_support", "_json", "_locale", "_lsprof", "_lzma", "_markupbase", "_md5", "_multibytecodec", "_multiprocessing", "_opcode", "_opcode_metadata", "_operator", "_osx_support", "_overlapped", "_pickle", "_posixshmem", "_posixsubprocess", "_py_abc", "_pydatetime", "_pydecimal", "_pyio", "_pylong", "_pyrepl", "_queue", "_random", "_scproxy", "_sha1", "_sha2", "_sha3", "_signal", "_sitebuiltins", "_socket", "_sqlite3", "_sre", "_ssl", "_stat", "_statistics", "_string", "_strptime", "_struct", "_suggestions", "_symtable", "_sysconfig", "_testbuffer", "_testcapi", "_testcapi_datetime", "_testclinic", "_testclinic_limited", "_testconsole", "_testexternalinspection", "_testimportmultiple", "_testinternalcapi", "_testlimitedcapi", "_testmultiphase", "_testsinglephase", "_thread", "_threading_local", "_tkinter", "_tokenize", "_tracemalloc", "_typing", "_uuid", "_warnings", "_weakref", "_weakrefset", "_winapi", "_wmi", "_xxtestfuzz", "_zoneinfo", "abc", "antigravity", "argparse", "array", "ast", "asyncio", "atexit", "base64", "bdb", "binascii", "bisect", "builtins", "bz2", "cProfile", "calendar", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "concurrent", "configparser", "contextlib", "contextvars", "copy", "copyreg", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal", "difflib", "dis", "doctest", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "fractions", "ftplib", "functools", "gc", "genericpath", "getopt", "getpass", "gettext", "glob", "graphlib", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http", "idlelib", "imaplib", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "linecache", "locale", "logging", "lzma", "mailbox", "marshal", "math", "mimetypes", "mmap", "modulefinder", "msvcrt", "multiprocessing", "netrc", "nt", "ntpath", "nturl2path", "numbers", "opcode", "operator", "optparse", "os", "pathlib", "pdb", "pickle", "pickletools", "pkgutil", "platform", "plistlib", "poplib", "posix", "posixpath", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "pydoc_data", "pyexpat", "queue", "quopri", "random", "re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "smtplib", "socket", "socketserver", "sqlite3", "sre_compile", "sre_constants", "sre_parse", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "tempfile", "termios", "textwrap", "this", "threading", "time", "timeit", "tkinter", "token", "tokenize", "tomllib", "trace", "traceback", "tracemalloc", "tty", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "winreg", "winsound", "wsgiref", "xml", "xmlrpc", "xx", "xxlimited", "xxlimited_35", "xxsubtype", "zipapp", "zipfile", "zipimport", "zlib", "zoneinfo", } isort-9.0.1/isort/stdlibs/py314.py0000644000000000000000000001222713615410400013643 0ustar00""" File contains the standard library of Python 3.14. DO NOT EDIT. If the standard library changes, a new list should be created using the mkstdlibs.py script. """ stdlib = { "__future__", "__hello__", "__hello_alias__", "__hello_only__", "__phello__", "__phello_alias__", "_abc", "_aix_support", "_android_support", "_apple_support", "_ast", "_ast_unparse", "_asyncio", "_bisect", "_blake2", "_bz2", "_codecs", "_codecs_cn", "_codecs_hk", "_codecs_iso2022", "_codecs_jp", "_codecs_kr", "_codecs_tw", "_collections", "_collections_abc", "_colorize", "_compat_pickle", "_contextvars", "_csv", "_ctypes", "_ctypes_test", "_curses", "_curses_panel", "_datetime", "_dbm", "_decimal", "_elementtree", "_frozen_importlib", "_frozen_importlib_external", "_functools", "_gdbm", "_hashlib", "_heapq", "_hmac", "_imp", "_interpchannels", "_interpqueues", "_interpreters", "_io", "_ios_support", "_json", "_locale", "_lsprof", "_lzma", "_markupbase", "_md5", "_multibytecodec", "_multiprocessing", "_opcode", "_opcode_metadata", "_operator", "_osx_support", "_overlapped", "_pickle", "_posixshmem", "_posixsubprocess", "_py_abc", "_py_warnings", "_pydatetime", "_pydecimal", "_pyio", "_pylong", "_pyrepl", "_queue", "_random", "_remote_debugging", "_scproxy", "_sha1", "_sha2", "_sha3", "_signal", "_sitebuiltins", "_socket", "_sqlite3", "_sre", "_ssl", "_stat", "_statistics", "_string", "_strptime", "_struct", "_suggestions", "_symtable", "_sysconfig", "_testbuffer", "_testcapi", "_testcapi_datetime", "_testclinic", "_testclinic_limited", "_testconsole", "_testimportmultiple", "_testinternalcapi", "_testlimitedcapi", "_testmultiphase", "_testsinglephase", "_thread", "_threading_local", "_tkinter", "_tokenize", "_tracemalloc", "_types", "_typing", "_uuid", "_warnings", "_weakref", "_weakrefset", "_winapi", "_wmi", "_xxtestfuzz", "_zoneinfo", "_zstd", "abc", "annotationlib", "antigravity", "argparse", "array", "ast", "asyncio", "atexit", "base64", "bdb", "binascii", "bisect", "builtins", "bz2", "cProfile", "calendar", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "compression", "concurrent", "configparser", "contextlib", "contextvars", "copy", "copyreg", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal", "difflib", "dis", "doctest", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "fractions", "ftplib", "functools", "gc", "genericpath", "getopt", "getpass", "gettext", "glob", "graphlib", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http", "idlelib", "imaplib", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "linecache", "locale", "logging", "lzma", "mailbox", "marshal", "math", "mimetypes", "mmap", "modulefinder", "msvcrt", "multiprocessing", "netrc", "nt", "ntpath", "nturl2path", "numbers", "opcode", "operator", "optparse", "os", "pathlib", "pdb", "pickle", "pickletools", "pkgutil", "platform", "plistlib", "poplib", "posix", "posixpath", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "pydoc_data", "pyexpat", "queue", "quopri", "random", "re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "smtplib", "socket", "socketserver", "sqlite3", "sre_compile", "sre_constants", "sre_parse", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "tempfile", "termios", "textwrap", "this", "threading", "time", "timeit", "tkinter", "token", "tokenize", "tomllib", "trace", "traceback", "tracemalloc", "tty", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "winreg", "winsound", "wsgiref", "xml", "xmlrpc", "xx", "xxlimited", "xxlimited_35", "xxsubtype", "zipapp", "zipfile", "zipimport", "zlib", "zoneinfo", } isort-9.0.1/isort/stdlibs/py315.py0000644000000000000000000001220413615410400013637 0ustar00""" File contains the standard library of Python 3.15. DO NOT EDIT. If the standard library changes, a new list should be created using the mkstdlibs.py script. """ stdlib = { "__future__", "__hello__", "__hello_alias__", "__hello_only__", "__phello__", "__phello_alias__", "_abc", "_aix_support", "_android_support", "_apple_support", "_ast", "_ast_unparse", "_asyncio", "_bisect", "_blake2", "_bz2", "_codecs", "_codecs_cn", "_codecs_hk", "_codecs_iso2022", "_codecs_jp", "_codecs_kr", "_codecs_tw", "_collections", "_collections_abc", "_colorize", "_compat_pickle", "_contextvars", "_csv", "_ctypes", "_ctypes_test", "_curses", "_curses_panel", "_datetime", "_dbm", "_decimal", "_elementtree", "_frozen_importlib", "_frozen_importlib_external", "_functools", "_gdbm", "_hashlib", "_heapq", "_hmac", "_imp", "_interpchannels", "_interpqueues", "_interpreters", "_io", "_ios_support", "_json", "_locale", "_lsprof", "_lzma", "_markupbase", "_math_integer", "_md5", "_multibytecodec", "_multiprocessing", "_opcode", "_opcode_metadata", "_operator", "_osx_support", "_overlapped", "_pickle", "_posixshmem", "_posixsubprocess", "_py_abc", "_py_warnings", "_pydatetime", "_pydecimal", "_pyio", "_pylong", "_pyrepl", "_queue", "_random", "_remote_debugging", "_scproxy", "_sha1", "_sha2", "_sha3", "_signal", "_sitebuiltins", "_socket", "_sqlite3", "_sre", "_ssl", "_stat", "_statistics", "_string", "_strptime", "_struct", "_suggestions", "_symtable", "_sysconfig", "_testbuffer", "_testcapi", "_testcapi_datetime", "_testclinic", "_testclinic_limited", "_testconsole", "_testimportmultiple", "_testinternalcapi", "_testlimitedcapi", "_testmultiphase", "_testsinglephase", "_thread", "_threading_local", "_tkinter", "_tokenize", "_tracemalloc", "_types", "_typing", "_uuid", "_warnings", "_weakref", "_weakrefset", "_winapi", "_wmi", "_xxtestfuzz", "_zoneinfo", "_zstd", "abc", "annotationlib", "antigravity", "argparse", "array", "ast", "asyncio", "atexit", "base64", "bdb", "binascii", "bisect", "builtins", "bz2", "cProfile", "calendar", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "compression", "concurrent", "configparser", "contextlib", "contextvars", "copy", "copyreg", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal", "difflib", "dis", "doctest", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "fractions", "ftplib", "functools", "gc", "genericpath", "getopt", "getpass", "gettext", "glob", "graphlib", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http", "idlelib", "imaplib", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "linecache", "locale", "logging", "lzma", "mailbox", "marshal", "math", "mimetypes", "mmap", "modulefinder", "msvcrt", "multiprocessing", "netrc", "nt", "ntpath", "nturl2path", "numbers", "opcode", "operator", "optparse", "os", "pathlib", "pdb", "pickle", "pickletools", "pkgutil", "platform", "plistlib", "poplib", "posix", "posixpath", "pprint", "profile", "profiling", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "pydoc_data", "pyexpat", "queue", "quopri", "random", "re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "smtplib", "socket", "socketserver", "sqlite3", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "tempfile", "termios", "textwrap", "this", "threading", "time", "timeit", "tkinter", "token", "tokenize", "tomllib", "trace", "traceback", "tracemalloc", "tty", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "winreg", "winsound", "wsgiref", "xml", "xmlrpc", "xx", "xxlimited", "xxlimited_35", "xxsubtype", "zipapp", "zipfile", "zipimport", "zlib", "zoneinfo", } isort-9.0.1/isort/stdlibs/py36.py0000644000000000000000000000635613615410400013572 0ustar00""" File contains the standard library of Python 3.6. DO NOT EDIT. If the standard library changes, a new list should be created using the mkstdlibs.py script. """ stdlib = { "_ast", "_dummy_thread", "_thread", "abc", "aifc", "argparse", "array", "ast", "asynchat", "asyncio", "asyncore", "atexit", "audioop", "base64", "bdb", "binascii", "binhex", "bisect", "builtins", "bz2", "cProfile", "calendar", "cgi", "cgitb", "chunk", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "concurrent", "configparser", "contextlib", "copy", "copyreg", "crypt", "csv", "ctypes", "curses", "datetime", "dbm", "decimal", "difflib", "dis", "distutils", "doctest", "dummy_threading", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "formatter", "fpectl", "fractions", "ftplib", "functools", "gc", "getopt", "getpass", "gettext", "glob", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http", "imaplib", "imghdr", "imp", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "lib2to3", "linecache", "locale", "logging", "lzma", "macpath", "mailbox", "mailcap", "marshal", "math", "mimetypes", "mmap", "modulefinder", "msilib", "msvcrt", "multiprocessing", "netrc", "nis", "nntplib", "ntpath", "numbers", "operator", "optparse", "os", "ossaudiodev", "parser", "pathlib", "pdb", "pickle", "pickletools", "pipes", "pkgutil", "platform", "plistlib", "poplib", "posix", "posixpath", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "queue", "quopri", "random", "re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "smtpd", "smtplib", "sndhdr", "socket", "socketserver", "spwd", "sqlite3", "sre", "sre_compile", "sre_constants", "sre_parse", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "sunau", "symbol", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "telnetlib", "tempfile", "termios", "test", "textwrap", "threading", "time", "timeit", "tkinter", "token", "tokenize", "trace", "traceback", "tracemalloc", "tty", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "uu", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "winreg", "winsound", "wsgiref", "xdrlib", "xml", "xmlrpc", "zipapp", "zipfile", "zipimport", "zlib", } isort-9.0.1/isort/stdlibs/py37.py0000644000000000000000000000640613615410400013567 0ustar00""" File contains the standard library of Python 3.7. DO NOT EDIT. If the standard library changes, a new list should be created using the mkstdlibs.py script. """ stdlib = { "_ast", "_dummy_thread", "_thread", "abc", "aifc", "argparse", "array", "ast", "asynchat", "asyncio", "asyncore", "atexit", "audioop", "base64", "bdb", "binascii", "binhex", "bisect", "builtins", "bz2", "cProfile", "calendar", "cgi", "cgitb", "chunk", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "concurrent", "configparser", "contextlib", "contextvars", "copy", "copyreg", "crypt", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal", "difflib", "dis", "distutils", "doctest", "dummy_threading", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "formatter", "fractions", "ftplib", "functools", "gc", "getopt", "getpass", "gettext", "glob", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http", "imaplib", "imghdr", "imp", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "lib2to3", "linecache", "locale", "logging", "lzma", "macpath", "mailbox", "mailcap", "marshal", "math", "mimetypes", "mmap", "modulefinder", "msilib", "msvcrt", "multiprocessing", "netrc", "nis", "nntplib", "ntpath", "numbers", "operator", "optparse", "os", "ossaudiodev", "parser", "pathlib", "pdb", "pickle", "pickletools", "pipes", "pkgutil", "platform", "plistlib", "poplib", "posix", "posixpath", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "queue", "quopri", "random", "re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "smtpd", "smtplib", "sndhdr", "socket", "socketserver", "spwd", "sqlite3", "sre", "sre_compile", "sre_constants", "sre_parse", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "sunau", "symbol", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "telnetlib", "tempfile", "termios", "test", "textwrap", "threading", "time", "timeit", "tkinter", "token", "tokenize", "trace", "traceback", "tracemalloc", "tty", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "uu", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "winreg", "winsound", "wsgiref", "xdrlib", "xml", "xmlrpc", "zipapp", "zipfile", "zipimport", "zlib", } isort-9.0.1/isort/stdlibs/py38.py0000644000000000000000000001173613615410400013572 0ustar00""" File contains the standard library of Python 3.8. DO NOT EDIT. If the standard library changes, a new list should be created using the mkstdlibs.py script. """ stdlib = { "__future__", "__hello__", "__phello__", "_abc", "_ast", "_asyncio", "_bisect", "_blake2", "_bootlocale", "_bz2", "_codecs", "_codecs_cn", "_codecs_hk", "_codecs_iso2022", "_codecs_jp", "_codecs_kr", "_codecs_tw", "_collections", "_collections_abc", "_compat_pickle", "_compression", "_contextvars", "_crypt", "_csv", "_ctypes", "_ctypes_test", "_curses", "_curses_panel", "_datetime", "_dbm", "_decimal", "_dummy_thread", "_elementtree", "_frozen_importlib", "_frozen_importlib_external", "_functools", "_gdbm", "_hashlib", "_heapq", "_imp", "_io", "_json", "_locale", "_lsprof", "_lzma", "_markupbase", "_md5", "_msi", "_multibytecodec", "_multiprocessing", "_opcode", "_operator", "_osx_support", "_overlapped", "_pickle", "_posixshmem", "_posixsubprocess", "_py_abc", "_pydecimal", "_pyio", "_queue", "_random", "_scproxy", "_sha1", "_sha256", "_sha3", "_sha512", "_signal", "_sitebuiltins", "_socket", "_sqlite3", "_sre", "_ssl", "_stat", "_statistics", "_string", "_strptime", "_struct", "_symtable", "_testbuffer", "_testcapi", "_testconsole", "_testimportmultiple", "_testinternalcapi", "_testmultiphase", "_thread", "_threading_local", "_tkinter", "_tracemalloc", "_uuid", "_warnings", "_weakref", "_weakrefset", "_winapi", "_xxsubinterpreters", "_xxtestfuzz", "abc", "aifc", "antigravity", "argparse", "array", "ast", "asynchat", "asyncio", "asyncore", "atexit", "audioop", "base64", "bdb", "binascii", "binhex", "bisect", "builtins", "bz2", "cProfile", "calendar", "cgi", "cgitb", "chunk", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "concurrent", "configparser", "contextlib", "contextvars", "copy", "copyreg", "crypt", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal", "difflib", "dis", "distutils", "doctest", "dummy_threading", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "formatter", "fractions", "ftplib", "functools", "gc", "genericpath", "getopt", "getpass", "gettext", "glob", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http", "idlelib", "imaplib", "imghdr", "imp", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "lib2to3", "linecache", "locale", "logging", "lzma", "mailbox", "mailcap", "marshal", "math", "mimetypes", "mmap", "modulefinder", "msilib", "msvcrt", "multiprocessing", "netrc", "nis", "nntplib", "nt", "ntpath", "nturl2path", "numbers", "opcode", "operator", "optparse", "os", "ossaudiodev", "parser", "pathlib", "pdb", "pickle", "pickletools", "pipes", "pkgutil", "platform", "plistlib", "poplib", "posix", "posixpath", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "pydoc_data", "pyexpat", "queue", "quopri", "random", "re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "smtpd", "smtplib", "sndhdr", "socket", "socketserver", "spwd", "sqlite3", "sre_compile", "sre_constants", "sre_parse", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "sunau", "symbol", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "telnetlib", "tempfile", "termios", "textwrap", "this", "threading", "time", "timeit", "tkinter", "token", "tokenize", "trace", "traceback", "tracemalloc", "tty", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "uu", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "winreg", "winsound", "wsgiref", "xdrlib", "xml", "xmlrpc", "xx", "xxlimited", "xxsubtype", "zipapp", "zipfile", "zipimport", "zlib", } isort-9.0.1/isort/stdlibs/py39.py0000644000000000000000000001204113615410400013561 0ustar00""" File contains the standard library of Python 3.9. DO NOT EDIT. If the standard library changes, a new list should be created using the mkstdlibs.py script. """ stdlib = { "__future__", "__hello__", "__phello__", "_abc", "_aix_support", "_ast", "_asyncio", "_bisect", "_blake2", "_bootlocale", "_bootsubprocess", "_bz2", "_codecs", "_codecs_cn", "_codecs_hk", "_codecs_iso2022", "_codecs_jp", "_codecs_kr", "_codecs_tw", "_collections", "_collections_abc", "_compat_pickle", "_compression", "_contextvars", "_crypt", "_csv", "_ctypes", "_ctypes_test", "_curses", "_curses_panel", "_datetime", "_dbm", "_decimal", "_elementtree", "_frozen_importlib", "_frozen_importlib_external", "_functools", "_gdbm", "_hashlib", "_heapq", "_imp", "_io", "_json", "_locale", "_lsprof", "_lzma", "_markupbase", "_md5", "_msi", "_multibytecodec", "_multiprocessing", "_opcode", "_operator", "_osx_support", "_overlapped", "_peg_parser", "_pickle", "_posixshmem", "_posixsubprocess", "_py_abc", "_pydecimal", "_pyio", "_queue", "_random", "_scproxy", "_sha1", "_sha256", "_sha3", "_sha512", "_signal", "_sitebuiltins", "_socket", "_sqlite3", "_sre", "_ssl", "_stat", "_statistics", "_string", "_strptime", "_struct", "_symtable", "_testbuffer", "_testcapi", "_testconsole", "_testimportmultiple", "_testinternalcapi", "_testmultiphase", "_thread", "_threading_local", "_tkinter", "_tracemalloc", "_uuid", "_warnings", "_weakref", "_weakrefset", "_winapi", "_xxsubinterpreters", "_xxtestfuzz", "_zoneinfo", "abc", "aifc", "antigravity", "argparse", "array", "ast", "asynchat", "asyncio", "asyncore", "atexit", "audioop", "base64", "bdb", "binascii", "binhex", "bisect", "builtins", "bz2", "cProfile", "calendar", "cgi", "cgitb", "chunk", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "concurrent", "configparser", "contextlib", "contextvars", "copy", "copyreg", "crypt", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal", "difflib", "dis", "distutils", "doctest", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "formatter", "fractions", "ftplib", "functools", "gc", "genericpath", "getopt", "getpass", "gettext", "glob", "graphlib", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http", "idlelib", "imaplib", "imghdr", "imp", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "lib2to3", "linecache", "locale", "logging", "lzma", "mailbox", "mailcap", "marshal", "math", "mimetypes", "mmap", "modulefinder", "msilib", "msvcrt", "multiprocessing", "netrc", "nis", "nntplib", "nt", "ntpath", "nturl2path", "numbers", "opcode", "operator", "optparse", "os", "ossaudiodev", "parser", "pathlib", "pdb", "pickle", "pickletools", "pipes", "pkgutil", "platform", "plistlib", "poplib", "posix", "posixpath", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "pydoc_data", "pyexpat", "queue", "quopri", "random", "re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "smtpd", "smtplib", "sndhdr", "socket", "socketserver", "spwd", "sqlite3", "sre_compile", "sre_constants", "sre_parse", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "sunau", "symbol", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "telnetlib", "tempfile", "termios", "textwrap", "this", "threading", "time", "timeit", "tkinter", "token", "tokenize", "trace", "traceback", "tracemalloc", "tty", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "uu", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "winreg", "winsound", "wsgiref", "xdrlib", "xml", "xmlrpc", "xx", "xxlimited", "xxsubtype", "zipapp", "zipfile", "zipimport", "zlib", "zoneinfo", } isort-9.0.1/scripts/build_config_option_docs.py0000755000000000000000000003206513615410400016677 0ustar00#! /bin/env python import dataclasses import os from collections.abc import Generator, Iterable from textwrap import dedent from typing import Any from isort.main import _build_arg_parser from isort.settings import _DEFAULT_SETTINGS as config OUTPUT_FILE = os.path.abspath( os.path.join(os.path.dirname(os.path.abspath(__file__)), "../docs/configuration/options.md") ) MD_NEWLINE = " " HUMAN_NAME = { "py_version": "Python Version", "vn": "Version Number", "str": "String", "frozenset": "List of Strings", "tuple": "List of Strings", } CONFIG_DEFAULTS = {"False": "false", "True": "true", "None": ""} DESCRIPTIONS = {} IGNORED = {"source", "help", "sources", "directory"} COLUMNS = ["Name", "Type", "Default", "Python / Config file", "CLI", "Description"] HEADER = """# Configuration options for isort As a code formatter isort has opinions. However, it also allows you to have your own. If your opinions disagree with those of isort, isort will disagree but commit to your way of formatting. To enable this, isort exposes a plethora of options to specify how you want your imports sorted, organized, and formatted. Too busy to build your perfect isort configuration? For curated common configurations, see isort's [built-in profiles](https://isort.readthedocs.io/en/latest/configuration/profiles.html). """ parser = _build_arg_parser() @dataclasses.dataclass class Example: section_complete: str = "" cfg: str = "" pyproject_toml: str = "" cli: str = "" def __post_init__(self): if self.cfg or self.pyproject_toml or self.cli: if self.cfg: cfg = dedent(self.cfg).lstrip() self.cfg = ( dedent( """ ### Example `.isort.cfg` ``` [settings] {cfg} ``` """ ) .format(cfg=cfg) .lstrip() ) if self.pyproject_toml: pyproject_toml = dedent(self.pyproject_toml).lstrip() self.pyproject_toml = ( dedent( """ ### Example `pyproject.toml` ``` [tool.isort] {pyproject_toml} ``` """ ) .format(pyproject_toml=pyproject_toml) .lstrip() ) if self.cli: cli = dedent(self.cli).lstrip() self.cli = ( dedent( """ ### Example cli usage `{cli}` """ ) .format(cli=cli) .lstrip() ) sections = [s for s in [self.cfg, self.pyproject_toml, self.cli] if s] sections_str = "\n".join(sections) self.section_complete = f"""**Examples:** {sections_str}""" else: self.section_complete = "" def __str__(self): return self.section_complete description_mapping: dict[str, str] description_mapping = { "length_sort_sections": "Sort the given sections by length", "forced_separate": "Force certain sub modules to show separately", "sections": "What sections isort should display imports for and in what order", "known_other": "known_OTHER is how imports of custom sections are defined. " "OTHER is a placeholder for the custom section name.", "comment_prefix": "Allows customizing how isort prefixes comments that it adds or modifies on import lines" "Generally ` #` (two spaces before a pound symbol) is use, though one space is also common.", "lines_before_imports": "The number of blank lines to place before imports. -1 for automatic determination", "lines_after_imports": "The number of blank lines to place after imports. -1 for automatic determination", "lines_between_sections": "The number of lines to place between sections", "lines_between_types": "The number of lines to place between direct and from imports", "lexicographical": "Lexicographical order is strictly alphabetical order. " "For example by default isort will sort `1, 10, 2` into `1, 2, 10` - but with " "lexicographical sorting enabled it will remain `1, 10, 2`.", "ignore_comments": "If enabled, isort will strip comments that exist within import lines.", "constants": "An override list of tokens to always recognize as a CONSTANT for order_by_type regardless of casing.", "classes": "An override list of tokens to always recognize as a Class for order_by_type regardless of casing.", "variables": "An override list of tokens to always recognize as a var for order_by_type regardless of casing.", "auto_identify_namespace_packages": "Automatically determine local namespace packages, generally by lack of any src files before a src containing directory.", "namespace_packages": "Manually specify one or more namespace packages.", "follow_links": "If `True` isort will follow symbolic links when doing recursive sorting.", "formatting_function": "The fully qualified Python path of a function to apply to format code sorted by isort.", "group_by_package": "If `True` isort will automatically create section groups by the top-level package they come from.", "indented_import_headings": "If `True` isort will apply import headings to indented imports the same way it does unindented ones.", "import_headings": "A mapping of import sections to import heading comments that should show above them.", "import_footers": "A mapping of import sections to import footer comments that should show below them.", } example_mapping: dict[str, Example] example_mapping = { "skip": Example( cfg=""" skip=.gitignore,.dockerignore""", pyproject_toml=""" skip = [".gitignore", ".dockerignore"] """, ), "extend_skip": Example( cfg=""" extend_skip=.md,.json""", pyproject_toml=""" extend_skip = [".md", ".json"] """, ), "skip_glob": Example( cfg=""" skip_glob=docs/* """, pyproject_toml=""" skip_glob = ["docs/*"] """, ), "extend_skip_glob": Example( cfg=""" extend_skip_glob=my_*_module.py,test/* """, pyproject_toml=""" extend_skip_glob = ["my_*_module.py", "test/*"] """, ), "known_third_party": Example( cfg=""" known_third_party=my_module1,my_module2 """, pyproject_toml=""" known_third_party = ["my_module1", "my_module2"] """, ), "known_first_party": Example( cfg=""" known_first_party=my_module1,my_module2 """, pyproject_toml=""" known_first_party = ["my_module1", "my_module2"] """, ), "known_local_folder": Example( cfg=""" known_local_folder=my_module1,my_module2 """, pyproject_toml=""" known_local_folder = ["my_module1", "my_module2"] """, ), "known_standard_library": Example( cfg=""" known_standard_library=my_module1,my_module2 """, pyproject_toml=""" known_standard_library = ["my_module1", "my_module2"] """, ), "extra_standard_library": Example( cfg=""" extra_standard_library=my_module1,my_module2 """, pyproject_toml=""" extra_standard_library = ["my_module1", "my_module2"] """, ), "forced_separate": Example( cfg=""" forced_separate=glob_exp1,glob_exp2 """, pyproject_toml=""" forced_separate = ["glob_exp1", "glob_exp2"] """, ), "length_sort_sections": Example( cfg=""" length_sort_sections=future,stdlib """, pyproject_toml=""" length_sort_sections = ["future", "stdlib"] """, ), "add_imports": Example( cfg=""" add_imports=import os,import json """, pyproject_toml=""" add_imports = ["import os", "import json"] """, ), "remove_imports": Example( cfg=""" remove_imports=os,json """, pyproject_toml=""" remove_imports = ["os", "json"] """, ), "single_line_exclusions": Example( cfg=""" single_line_exclusions=os,json """, pyproject_toml=""" single_line_exclusions = ["os", "json"] """, ), "no_lines_before": Example( cfg=""" no_lines_before=future,stdlib """, pyproject_toml=""" no_lines_before = ["future", "stdlib"] """, ), "src_paths": Example( cfg=""" src_paths = src,tests """, pyproject_toml=""" src_paths = ["src", "tests"] """, ), "treat_comments_as_code": Example( cfg=""" treat_comments_as_code = # my comment 1, # my other comment """, pyproject_toml=""" treat_comments_as_code = ["# my comment 1", "# my other comment"] """, ), "supported_extensions": Example( cfg=""" supported_extensions=pyw,ext """, pyproject_toml=""" supported_extensions = ["pyw", "ext"] """, ), "blocked_extensions": Example( cfg=""" blocked_extensions=pyw,pyc """, pyproject_toml=""" blocked_extensions = ["pyw", "pyc"] """, ), "known_other": Example( cfg=""" sections=FUTURE,STDLIB,THIRDPARTY,AIRFLOW,FIRSTPARTY,LOCALFOLDER known_airflow=airflow""", pyproject_toml=""" sections = ['FUTURE', 'STDLIB', 'THIRDPARTY', 'AIRFLOW', 'FIRSTPARTY', 'LOCALFOLDER'] known_airflow = ['airflow']""", ), "multi_line_output": Example(cfg="multi_line_output=3", pyproject_toml="multi_line_output = 3"), "show_version": Example(cli="isort --version"), "py_version": Example( cli="isort --py 39", pyproject_toml=""" py_version=39 """, cfg=""" py_version=39 """, ), } @dataclasses.dataclass class ConfigOption: name: str type: type = str default: Any = "" config_name: str = "**Not Supported**" cli_options: Iterable[str] = (" **Not Supported**",) description: str = "**No Description**" example: Example | None = None def __str__(self): if self.name in IGNORED: return "" if self.cli_options == (" **Not Supported**",): cli_options = self.cli_options[0] else: cli_options = "\n\n- " + "\n- ".join(self.cli_options) # new line if example otherwise nothing example = f"\n{self.example}" if self.example else "" return f""" ## {human(self.name)} {self.description} **Type:** {human(self.type.__name__)}{MD_NEWLINE} **Default:** `{str(self.default) or " "}`{MD_NEWLINE} **Config default:** `{config_default(self.default) or " "}`{MD_NEWLINE} **Python & Config File Name:** {self.config_name}{MD_NEWLINE} **CLI Flags:**{cli_options} {example}""" def config_default(default: Any) -> str: if isinstance(default, (frozenset, tuple)): default = list(default) default_str = str(default) if default_str in CONFIG_DEFAULTS: return CONFIG_DEFAULTS[default_str] if default_str.startswith("py"): return default_str[2:] return default_str def human(name: str) -> str: if name in HUMAN_NAME: return HUMAN_NAME[name] return " ".join( part if part in ("of",) else part.capitalize() for part in name.replace("-", "_").split("_") ) def config_options() -> Generator[ConfigOption, None, None]: cli_actions = {action.dest: action for action in parser._actions} for name, default in config.items(): extra_kwargs = {} description: str | None = description_mapping.get(name, None) cli = cli_actions.pop(name, None) if cli: extra_kwargs["cli_options"] = cli.option_strings if cli.help and not description: description = cli.help default_display = default if isinstance(default, (set, frozenset)) and len(default) > 0: default_display = tuple(sorted(default)) # todo: refactor place for example params # needs to integrate with isort/settings/_Config # needs to integrate with isort/main/_build_arg_parser yield ConfigOption( name=name, type=type(default), default=default_display, config_name=name, description=description or "**No Description**", example=example_mapping.get(name, None), **extra_kwargs, ) for name, cli in cli_actions.items(): extra_kwargs = {} description: str | None = description_mapping.get(name, None) if cli.type: extra_kwargs["type"] = cli.type elif cli.default is not None: extra_kwargs["type"] = type(cli.default) if cli.help and not description: description = cli.help yield ConfigOption( name=name, default=cli.default, cli_options=cli.option_strings, example=example_mapping.get(name, None), description=description or "**No Description**", **extra_kwargs, ) def document_text() -> str: return f"{HEADER}{''.join(str(config_option) for config_option in config_options())}" def write_document(): with open(OUTPUT_FILE, "w") as output_file: output_file.write(document_text()) if __name__ == "__main__": write_document() isort-9.0.1/scripts/build_profile_docs.py0000755000000000000000000000210413615410400015471 0ustar00#! /bin/env python import os from typing import Any from isort.profiles import profiles OUTPUT_FILE = os.path.abspath( os.path.join(os.path.dirname(os.path.abspath(__file__)), "../docs/configuration/profiles.md") ) HEADER = """Built-in Profile for isort ======== The following profiles are built into isort to allow easy interoperability with common projects and code styles. To use any of the listed profiles, use `isort --profile PROFILE_NAME` from the command line, or `profile=PROFILE_NAME` in your configuration file. """ def format_profile(profile_name: str, profile: dict[str, Any]) -> str: options = "\n".join(f" - **{name}**: `{value!r}`" for name, value in profile.items()) return f""" #{profile_name} {profile.get("description", "")} {options} """ def document_text() -> str: return f"{HEADER}{''.join(format_profile(profile_name, profile) for profile_name, profile in profiles.items())}" def write_document(): with open(OUTPUT_FILE, "w") as output_file: output_file.write(document_text()) if __name__ == "__main__": write_document() isort-9.0.1/scripts/clean.sh0000755000000000000000000000022313615410400012706 0ustar00#!/usr/bin/env bash set -euxo pipefail uv run isort --profile hug isort/ tests/ scripts/ uv run isort --profile hug example_*/ uv run ruff format isort-9.0.1/scripts/docker.sh0000755000000000000000000000051013615410400013072 0ustar00#!/usr/bin/env bash set -ux result=0 for ver in {3.10,3.11,3.12,3.13,3.14,3.15}; do # latest tag will override after each build, leaving only the newest python version tagged docker build ./ --build-arg VERSION=$ver -t "isort:$ver" -t "isort:latest" && docker run "isort:$ver" result=$(( $? + $result )) done exit $result isort-9.0.1/scripts/done.sh0000755000000000000000000000011513615410400012551 0ustar00#!/usr/bin/env bash set -euxo pipefail ./scripts/clean.sh ./scripts/test.sh isort-9.0.1/scripts/mkstdlibs.py0000755000000000000000000000203413615410400013640 0ustar00#!/usr/bin/env python3 import re from stdlibs import py38, py39, py310, py311, py312, py313, py314, py315 PATH = "isort/stdlibs/py{}.py" VERSIONS = [ py38, py39, py310, py311, py312, py313, py314, py315, ] DOCSTRING = """ File contains the standard library of Python {}. DO NOT EDIT. If the standard library changes, a new list should be created using the mkstdlibs.py script. """ for version_module in VERSIONS: version_match = re.match( r"^stdlibs\.py(?P\d)(?P\d+)$", version_module.__name__, ) version_info = (version_match.groupdict()["major"], version_match.groupdict()["minor"]) path = PATH.format("".join(version_info)) with open(path, "w") as stdlib_file: docstring = DOCSTRING.format(".".join(version_info)) stdlib_file.write(f'"""{docstring}"""\n\n') stdlib_file.write("stdlib = {\n") for module in sorted(version_module.module_names): stdlib_file.write(f' "{module}",\n') stdlib_file.write("}\n") isort-9.0.1/scripts/test.sh0000755000000000000000000000024313615410400012605 0ustar00#!/usr/bin/env bash set -euxo pipefail uv run coverage run --parallel -m pytest tests/unit/ -s uv run coverage combine uv run coverage report uv run coverage xml isort-9.0.1/scripts/test_integration.sh0000755000000000000000000000011413615410400015205 0ustar00#!/usr/bin/env bash set -euxo pipefail uv run pytest tests/integration/ -s isort-9.0.1/tests/__init__.py0000644000000000000000000000000013615410400013042 0ustar00isort-9.0.1/tests/benchmark/test_api.py0000644000000000000000000000143113615410400015056 0ustar00from typing import Any import pytest from isort import api imperfect_content = "import b\nimport a\n" fixed_content = "import a\nimport b\n" @pytest.fixture def imperfect(tmpdir) -> Any: imperfect_file = tmpdir.join("test_needs_changes.py") imperfect_file.write_text(imperfect_content, "utf8") return imperfect_file def test_sort_file(benchmark, imperfect) -> None: def sort_file(): api.sort_file(imperfect) benchmark.pedantic(sort_file, iterations=10, rounds=100) assert imperfect.read() == fixed_content def test_sort_file_in_place(benchmark, imperfect) -> None: def sort_file(): api.sort_file(imperfect, overwrite_in_place=True) benchmark.pedantic(sort_file, iterations=10, rounds=100) assert imperfect.read() == fixed_content isort-9.0.1/tests/integration/test_hypothesmith.py0000644000000000000000000000673513615410400017437 0ustar00import ast from typing import get_type_hints import hypothesis import libcst from hypothesis import strategies as st from hypothesmith import from_grammar, from_node import isort def _as_config(kw) -> isort.Config: if "wrap_length" in kw and "line_length" in kw: kw["wrap_length"], kw["line_length"] = sorted([kw["wrap_length"], kw["line_length"]]) try: return isort.Config(**kw) except ValueError: kw["wrap_length"] = 0 return isort.Config(**kw) def _record_targets(code: str, prefix: str = "") -> str: # target larger inputs - the Hypothesis engine will do a multi-objective # hill-climbing search using these scores to generate 'better' examples. nodes = list(ast.walk(ast.parse(code))) import_nodes = [n for n in nodes if isinstance(n, (ast.Import, ast.ImportFrom))] uniq_nodes = {type(n) for n in nodes} for value, label in [ (len(import_nodes), "total number of import nodes"), (len(uniq_nodes), "number of unique ast node types"), ]: hypothesis.target(float(value), label=prefix + label) return code def configs(**force_strategies: st.SearchStrategy) -> st.SearchStrategy: """Generate arbitrary Config objects.""" skip = { "line_ending", "sections", "known_future_library", "forced_separate", "lines_before_imports", "lines_after_imports", "lines_between_sections", "lines_between_types", "sources", "virtual_env", "conda_env", "directory", "formatter", "formatting_function", } inferred_kwargs = { k: st.from_type(v) for k, v in get_type_hints(isort.settings._Config).items() if k not in skip } specific = { "line_length": st.integers(0, 200), "wrap_length": st.integers(0, 200), "indent": st.integers(0, 20).map(lambda n: n * " "), "default_section": st.sampled_from(sorted(isort.settings.KNOWN_SECTION_MAPPING)), "force_grid_wrap": st.integers(0, 20), "profile": st.sampled_from(sorted(isort.settings.profiles)), "py_version": st.sampled_from(("auto", *isort.settings.VALID_PY_TARGETS)), } kwargs = {**inferred_kwargs, **specific, **force_strategies} return st.fixed_dictionaries({}, optional=kwargs).map(_as_config) st.register_type_strategy(isort.Config, configs()) @hypothesis.example("import A\nimportA\r\n\n", isort.Config(), False) @hypothesis.given( source_code=st.lists( from_grammar(auto_target=False) | from_node(auto_target=False) | from_node(libcst.Import, auto_target=False) | from_node(libcst.ImportFrom, auto_target=False), min_size=1, max_size=10, ).map("\n".join), config=st.builds(isort.Config), disregard_skip=st.booleans(), ) @hypothesis.seed(235738473415671197623909623354096762459) @hypothesis.settings( suppress_health_check=[ hypothesis.HealthCheck.too_slow, hypothesis.HealthCheck.filter_too_much, ] ) def test_isort_is_idempotent(source_code: str, config: isort.Config, disregard_skip: bool) -> None: # NOTE: if this test finds a bug, please notify @Zac-HD so that it can be added to the # Hypothesmith trophy case. This really helps with research impact evaluations! _record_targets(source_code) result = isort.code(source_code, config=config, disregard_skip=disregard_skip) assert result == isort.code(result, config=config, disregard_skip=disregard_skip) isort-9.0.1/tests/integration/test_literal.py0000644000000000000000000000072413615410400016336 0ustar00"""Tests that need installation of other packages.""" # TODO: find a way to install example-isort-formatting-plugin to pass tests # import isort.literal # from isort.settings import Config # def test_value_assignment_list(): # assert isort.literal.assignment("x = ['b', 'a']", "list", "py") == "x = ['a', 'b']" # assert ( # isort.literal.assignment("x = ['b', 'a']", "list", "py", Config(formatter="example")) # == 'x = ["a", "b"]' # ) isort-9.0.1/tests/integration/test_projects_using_isort.py0000644000000000000000000000601113615410400021153 0ustar00"""Tests projects that use isort to see if any differences are found between their current imports and what isort suggest on the develop branch. This is an important early warning signal of regressions. NOTE: If you use isort within a public repository, please feel empowered to add your project here! It is important to isort that as few regressions as possible are experienced by our users. Having your project tested here is the most sure way to keep those regressions form ever happening. """ from __future__ import annotations from collections.abc import Generator, Sequence from pathlib import Path from subprocess import check_call import pytest from isort.main import main def git_clone(repository_url: str, directory: Path): """Clones the given repository into the given directory path""" check_call(["git", "clone", "--depth", "1", repository_url, str(directory)]) def run_isort(arguments: Generator[str, None, None] | Sequence[str]): """Runs isort in diff and check mode with the given arguments""" main(["--check-only", "--diff", *arguments]) def test_django(tmpdir): git_clone("https://github.com/django/django.git", tmpdir) run_isort( str(target_dir) for target_dir in (tmpdir / "django", tmpdir / "tests", tmpdir / "scripts") ) def test_plone(tmpdir): git_clone("https://github.com/plone/plone.app.multilingualindexes.git", tmpdir) run_isort([str(tmpdir / "src"), "--skip", "languagefallback.py"]) @pytest.mark.skip( "Skip for now as #2295 introduce a breaking change. Can be re-enabled after pandas has updated." ) def test_pandas(tmpdir): git_clone("https://github.com/pandas-dev/pandas.git", tmpdir) run_isort((str(tmpdir / "pandas"), "--skip", "__init__.py")) def test_habitat_lab(tmpdir): git_clone("https://github.com/facebookresearch/habitat-lab.git", tmpdir) run_isort([str(tmpdir)]) def test_pylint(tmpdir): git_clone("https://github.com/PyCQA/pylint.git", tmpdir) run_isort([str(tmpdir), "--skip", "bad.py"]) def test_hypothesis(tmpdir): git_clone("https://github.com/HypothesisWorks/hypothesis.git", tmpdir) run_isort( ( str(tmpdir), "--skip", "tests", "--profile", "black", "--ca", "--project", "hypothesis", "--project", "hypothesistooling", ) ) def test_pyramid(tmpdir): git_clone("https://github.com/Pylons/pyramid.git", tmpdir) run_isort( str(target_dir) for target_dir in (tmpdir / "src" / "pyramid", tmpdir / "tests", tmpdir / "setup.py") ) def test_products_zopetree(tmpdir): git_clone("https://github.com/jugmac00/Products.ZopeTree.git", tmpdir) run_isort([str(tmpdir)]) def test_dobby(tmpdir): git_clone("https://github.com/rocketDuck/dobby.git", tmpdir) run_isort([str(tmpdir / "tests"), str(tmpdir / "src")]) def test_zope(tmpdir): git_clone("https://github.com/zopefoundation/Zope.git", tmpdir) run_isort([str(tmpdir), "--skip", "util.py"]) isort-9.0.1/tests/integration/test_setting_combinations.py0000644000000000000000000016514113615410400021131 0ustar00from typing import get_type_hints import hypothesis from hypothesis import strategies as st import isort def _as_config(kw) -> isort.Config: kw["atomic"] = False if "wrap_length" in kw and "line_length" in kw: kw["wrap_length"], kw["line_length"] = sorted([kw["wrap_length"], kw["line_length"]]) try: return isort.Config(**kw) except ValueError: kw["wrap_length"] = 0 return isort.Config(**kw) def configs() -> st.SearchStrategy: """Generate arbitrary Config objects.""" skip = { "line_ending", "sections", "known_standard_library", "known_future_library", "known_third_party", "known_first_party", "known_local_folder", "extra_standard_library", "forced_separate", "lines_before_imports", "lines_after_imports", "add_imports", "lines_between_sections", "lines_between_types", "sources", "virtual_env", "conda_env", "directory", "formatter", "formatting_function", "comment_prefix", "atomic", "skip", "src_paths", } inferred_kwargs = { k: st.from_type(v) for k, v in get_type_hints(isort.settings._Config).items() if k not in skip } specific = { "line_length": st.integers(0, 200), "wrap_length": st.integers(0, 200), "indent": st.integers(0, 20).map(lambda n: n * " "), "default_section": st.sampled_from(sorted(isort.settings.KNOWN_SECTION_MAPPING)), "force_grid_wrap": st.integers(0, 20), "profile": st.sampled_from(sorted(isort.settings.profiles)), "sort_order": st.sampled_from(sorted(("native", "natural", "natural_plus"))), "py_version": st.sampled_from(("auto", *isort.settings.VALID_PY_TARGETS)), } kwargs = {**inferred_kwargs, **specific} return st.fixed_dictionaries({}, optional=kwargs).map(_as_config) st.register_type_strategy(isort.Config, configs()) CODE_SNIPPET = """ '''Taken from bottle.py Copyright (c) 2009-2018, Marcel Hellkamp. License: MIT (see LICENSE for details) ''' # Lots of stdlib and builtin differences. if py3k: import http.client as httplib import _thread as thread from urllib.parse import urljoin, SplitResult as UrlSplitResult from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote urlunquote = functools.partial(urlunquote, encoding='latin1') from http.cookies import SimpleCookie, Morsel, CookieError from collections.abc import MutableMapping as DictMixin import pickle # comment number 2 from io import BytesIO import configparser basestring = str unicode = str json_loads = lambda s: json_lds(touni(s)) callable = lambda x: hasattr(x, '__call__') imap = map def _raise(*a): raise a[0](a[1]).with_traceback(a[2]) else: # 2.x import httplib import thread from urlparse import urljoin, SplitResult as UrlSplitResult from urllib import urlencode, quote as urlquote, unquote as urlunquote from Cookie import SimpleCookie, Morsel, CookieError from itertools import imap import cPickle as pickle from StringIO import StringIO as BytesIO import ConfigParser as configparser # commentnumberone from collections import MutableMapping as DictMixin unicode = unicode json_loads = json_lds exec(compile('def _raise(*a): raise a[0], a[1], a[2]', '', 'exec')) """ SHOULD_RETAIN = [ """'''Taken from bottle.py Copyright (c) 2009-2018, Marcel Hellkamp. License: MIT (see LICENSE for details) '''""", "# Lots of stdlib and builtin differences.", "if py3k:", "http.client", "_thread", "urllib.parse", "urlencode", "urlunquote = functools.partial(urlunquote, encoding='latin1')", "http.cookies", "SimpleCookie", "collections.abc", "pickle", "comment number 2", "io", "configparser", """basestring = str unicode = str json_loads = lambda s: json_lds(touni(s)) callable = lambda x: hasattr(x, '__call__') imap = map def _raise(*a): raise a[0](a[1]).with_traceback(a[2]) else: # 2.x """, "httplib", "thread", "urlparse", "urllib", "Cookie", "itertools", "cPickle", "StringIO", "ConfigParser", "commentnumberone", "collections", """unicode = unicode json_loads = json_lds exec(compile('def _raise(*a): raise a[0], a[1], a[2]', '', 'exec'))""", ] @hypothesis.example( config=isort.Config( py_version="all", force_to_top=frozenset(), skip=frozenset( { ".svn", ".venv", "build", "dist", ".bzr", ".tox", ".hg", ".mypy_cache", ".nox", "_build", "buck-out", "node_modules", ".git", ".eggs", ".pants.d", "venv", ".direnv", } ), skip_glob=frozenset(), skip_gitignore=True, line_length=79, wrap_length=0, line_ending="", sections=("FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"), no_sections=False, known_future_library=frozenset({"__future__"}), known_third_party=frozenset(), known_first_party=frozenset(), known_local_folder=frozenset(), known_standard_library=frozenset( { "pwd", "types", "nntplib", "jpeg", "pyclbr", "encodings", "ctypes", "macerrors", "filecmp", "dbm", "mimetypes", "statvfs", "msvcrt", "spwd", "codecs", "SimpleHTTPServer", "compiler", "pickletools", "tkinter", "pickle", "fm", "bsddb", "contextvars", "dummy_thread", "pipes", "heapq", "dircache", "commands", "unicodedata", "ntpath", "marshal", "fpformat", "linecache", "calendar", "pty", "MimeWriter", "inspect", "mmap", "ic", "tty", "nis", "new", "wave", "HTMLParser", "anydbm", "tracemalloc", "pdb", "sunau", "GL", "parser", "winsound", "dbhash", "zlib", "MacOS", "pprint", "crypt", "aetools", "DEVICE", "fl", "gettext", "asyncore", "copyreg", "queue", "resource", "turtledemo", "fnmatch", "hotshot", "trace", "string", "plistlib", "gzip", "functools", "aepack", "hashlib", "imp", "MiniAEFrame", "getpass", "shutil", "ttk", "multifile", "operator", "reprlib", "subprocess", "cgi", "select", "SimpleXMLRPCServer", "audioop", "macresource", "stringprep", "wsgiref", "SUNAUDIODEV", "atexit", "lzma", "asyncio", "datetime", "binhex", "autoGIL", "doctest", "thread", "enum", "tempfile", "posixfile", "mhlib", "html", "itertools", "exceptions", "sgmllib", "array", "test", "imputil", "shlex", "flp", "uu", "gdbm", "urlparse", "msilib", "termios", "modulefinder", "ossaudiodev", "timeit", "binascii", "popen2", "ConfigParser", "poplib", "zipfile", "cfmfile", "pstats", "AL", "contextlib", "code", "zipimport", "base64", "platform", "ast", "fileinput", "locale", "buildtools", "stat", "quopri", "readline", "collections", "aetypes", "concurrent", "runpy", "copy_reg", "rexec", "cmath", "optparse", "dummy_threading", "ColorPicker", "sched", "netrc", "sunaudiodev", "socketserver", "logging", "PixMapWrapper", "sysconfig", "Nav", "copy", "cmd", "csv", "chunk", "multiprocessing", "warnings", "weakref", "py_compile", "sre", "sre_parse", "curses", "threading", "re", "FrameWork", "_thread", "imgfile", "cd", "sre_constants", "xdrlib", "dataclasses", "urllib2", "StringIO", "configparser", "importlib", "UserList", "posixpath", "mailbox", "rfc822", "grp", "pydoc", "sets", "textwrap", "numbers", "W", "gl", "htmllib", "macostools", "tarfile", "ipaddress", "xmlrpc", "icopen", "traceback", "_winreg", "random", "CGIHTTPServer", "dis", "sha", "selectors", "statistics", "DocXMLRPCServer", "imghdr", "venv", "keyword", "xmlrpclib", "ftplib", "getopt", "posix", "smtpd", "profile", "sndhdr", "signal", "EasyDialogs", "dumbdbm", "fcntl", "SocketServer", "distutils", "symbol", "pathlib", "cStringIO", "imaplib", "unittest", "al", "cProfile", "robotparser", "BaseHTTPServer", "os", "pkgutil", "socket", "fractions", "shelve", "aifc", "cgitb", "xml", "decimal", "sre_compile", "ssl", "user", "Bastion", "formatter", "time", "abc", "winreg", "difflib", "FL", "bz2", "asynchat", "gc", "gensuitemodule", "symtable", "secrets", "Carbon", "mailcap", "sys", "bdb", "fpectl", "httplib", "webbrowser", "smtplib", "Cookie", "whichdb", "turtle", "tokenize", "UserString", "tabnanny", "site", "struct", "codeop", "email", "typing", "cookielib", "Queue", "rlcompleter", "errno", "macpath", "videoreader", "md5", "cPickle", "Tix", "io", "faulthandler", "Tkinter", "glob", "syslog", "telnetlib", "_dummy_thread", "hmac", "uuid", "imageop", "future_builtins", "json", "htmlentitydefs", "lib2to3", "UserDict", "mutex", "sqlite3", "findertools", "bisect", "builtins", "urllib", "http", "compileall", "argparse", "ScrolledText", "token", "dl", "applesingle", "math", "ensurepip", "mimify", "mimetools", "colorsys", "zipapp", "__builtin__", } ), extra_standard_library=frozenset(), known_other={"other": frozenset({"", "\x10\x1bm"})}, multi_line_output=0, forced_separate=(), indent=" ", comment_prefix=" #", length_sort=True, length_sort_straight=False, length_sort_sections=frozenset(), add_imports=frozenset(), remove_imports=frozenset( { "", "\U00076fe7þs\x0c\U000c8b75v\U00106541", "𥒒>\U0001960euj𒎕\x9e", "\x15\x9b", "\x02l", "\U000b71ef.\x1c", "\x7f?\U000ec91c", "\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«", "Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø", ";À¨|\x1b 𑐒🍸V", } ), append_only=False, reverse_relative=True, force_single_line=False, single_line_exclusions=( "Y\U000347d9g\x957K", "", "Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.", "·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ", "", ":sI¶", "", ), default_section="THIRDPARTY", import_headings={}, import_footers={}, balanced_wrapping=False, use_parentheses=True, order_by_type=True, atomic=False, lines_before_imports=-1, lines_after_imports=-1, lines_between_sections=1, lines_between_types=0, combine_as_imports=True, combine_star=False, include_trailing_comma=False, from_first=False, verbose=False, quiet=False, force_adds=False, force_alphabetical_sort_within_sections=False, force_alphabetical_sort=False, force_grid_wrap=0, force_sort_within_sections=False, lexicographical=False, ignore_whitespace=False, no_lines_before=frozenset( { "uøø", "¢", "&\x8c5Ï\U000e5f01Ø", "\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ", "\U000e374c8", "w", } ), no_inline_sort=False, ignore_comments=False, case_sensitive=False, sources=( { "py_version": "py3", "force_to_top": frozenset(), "skip": frozenset( { ".svn", ".venv", "build", "dist", ".bzr", ".tox", ".hg", ".mypy_cache", ".nox", "_build", "buck-out", "node_modules", ".git", ".eggs", ".pants.d", "venv", ".direnv", } ), "skip_glob": frozenset(), "skip_gitignore": False, "line_length": 79, "wrap_length": 0, "line_ending": "", "sections": ("FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"), "no_sections": False, "known_future_library": frozenset({"__future__"}), "known_third_party": frozenset(), "known_first_party": frozenset(), "known_local_folder": frozenset(), "known_standard_library": frozenset( { "pwd", "copy", "cmd", "csv", "chunk", "multiprocessing", "warnings", "types", "weakref", "nntplib", "pyclbr", "encodings", "py_compile", "sre", "ctypes", "sre_parse", "filecmp", "curses", "threading", "dbm", "re", "_thread", "sre_constants", "xdrlib", "dataclasses", "mimetypes", "configparser", "importlib", "msvcrt", "spwd", "posixpath", "mailbox", "codecs", "grp", "pickletools", "tkinter", "pickle", "contextvars", "pydoc", "textwrap", "numbers", "pipes", "heapq", "tarfile", "unicodedata", "ntpath", "ipaddress", "marshal", "xmlrpc", "traceback", "linecache", "calendar", "pty", "random", "dis", "selectors", "statistics", "imghdr", "venv", "inspect", "mmap", "keyword", "ftplib", "tty", "nis", "getopt", "posix", "smtpd", "wave", "profile", "sndhdr", "signal", "tracemalloc", "pdb", "sunau", "winsound", "parser", "zlib", "fcntl", "pprint", "distutils", "crypt", "symbol", "gettext", "pathlib", "asyncore", "copyreg", "imaplib", "unittest", "queue", "resource", "turtledemo", "fnmatch", "cProfile", "os", "pkgutil", "socket", "trace", "fractions", "string", "shelve", "plistlib", "aifc", "gzip", "functools", "cgitb", "xml", "hashlib", "decimal", "imp", "sre_compile", "ssl", "formatter", "winreg", "time", "getpass", "shutil", "abc", "difflib", "bz2", "operator", "reprlib", "subprocess", "cgi", "select", "asynchat", "audioop", "gc", "secrets", "symtable", "mailcap", "sys", "bdb", "fpectl", "stringprep", "webbrowser", "smtplib", "wsgiref", "atexit", "lzma", "asyncio", "datetime", "binhex", "doctest", "turtle", "enum", "tempfile", "tokenize", "tabnanny", "site", "html", "struct", "itertools", "codeop", "email", "array", "test", "typing", "shlex", "uu", "msilib", "termios", "rlcompleter", "modulefinder", "ossaudiodev", "timeit", "binascii", "poplib", "errno", "macpath", "zipfile", "io", "faulthandler", "pstats", "contextlib", "code", "glob", "zipimport", "base64", "syslog", "platform", "ast", "fileinput", "telnetlib", "locale", "_dummy_thread", "hmac", "stat", "uuid", "quopri", "readline", "collections", "json", "concurrent", "lib2to3", "sqlite3", "runpy", "cmath", "optparse", "bisect", "builtins", "urllib", "dummy_threading", "http", "compileall", "argparse", "token", "sched", "netrc", "math", "ensurepip", "socketserver", "colorsys", "zipapp", "logging", "sysconfig", } ), "extra_standard_library": frozenset(), "known_other": {}, "multi_line_output": 0, "forced_separate": (), "indent": " ", "comment_prefix": " #", "length_sort": False, "length_sort_straight": False, "length_sort_sections": frozenset(), "add_imports": frozenset(), "remove_imports": frozenset(), "append_only": False, "reverse_relative": False, "force_single_line": False, "single_line_exclusions": (), "default_section": "THIRDPARTY", "import_headings": {}, "import_footers": {}, "balanced_wrapping": False, "use_parentheses": False, "order_by_type": True, "atomic": False, "lines_before_imports": -1, "lines_after_imports": -1, "lines_between_sections": 1, "lines_between_types": 0, "combine_as_imports": False, "combine_star": False, "include_trailing_comma": False, "from_first": False, "verbose": False, "quiet": False, "force_adds": False, "force_alphabetical_sort_within_sections": False, "force_alphabetical_sort": False, "force_grid_wrap": 0, "force_sort_within_sections": False, "lexicographical": False, "ignore_whitespace": False, "no_lines_before": frozenset(), "no_inline_sort": False, "ignore_comments": False, "case_sensitive": False, "sources": (), "virtual_env": "", "conda_env": "", "ensure_newline_before_comments": False, "directory": "", "profile": "", "honor_noqa": False, "src_paths": frozenset(), "remove_redundant_aliases": False, "float_to_top": False, "filter_files": False, "formatter": "", "formatting_function": None, "color_output": False, "treat_comments_as_code": frozenset(), "treat_all_comments_as_code": False, "supported_extensions": frozenset({"py", "pyx", "pyi"}), "blocked_extensions": frozenset({"pex"}), "constants": frozenset(), "classes": frozenset(), "variables": frozenset(), "dedup_headings": False, "source": "defaults", }, { "classes": frozenset( { "\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ", "C", "\x8e\U000422ac±\U000b5a1f\U000c4166", "ùÚ", } ), "single_line_exclusions": ( "Y\U000347d9g\x957K", "", "Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.", "·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ", "", ":sI¶", "", ), "indent": " ", "no_lines_before": frozenset( { "uøø", "¢", "&\x8c5Ï\U000e5f01Ø", "\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ", "\U000e374c8", "w", } ), "quiet": False, "honor_noqa": False, "dedup_headings": True, "known_other": { "\x10\x1bm": frozenset({"\U000682a49\U000e1a63²KǶ4", "", "\x1a", "©"}), "": frozenset({"íå\x94Ì", "\U000cf258"}), }, "treat_comments_as_code": frozenset({""}), "length_sort": True, "reverse_relative": True, "combine_as_imports": True, "py_version": "all", "use_parentheses": True, "skip_gitignore": True, "remove_imports": frozenset( { "", "\U00076fe7þs\x0c\U000c8b75v\U00106541", "𥒒>\U0001960euj𒎕\x9e", "\x15\x9b", "\x02l", "\U000b71ef.\x1c", "\x7f?\U000ec91c", "\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«", "Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø", ";À¨|\x1b 𑐒🍸V", } ), "atomic": False, "source": "runtime", }, ), virtual_env="", conda_env="", ensure_newline_before_comments=False, directory="/home/abuild/rpmbuild/BUILD/isort-5.11.0", profile="", honor_noqa=False, remove_redundant_aliases=False, float_to_top=False, filter_files=False, formatting_function=None, color_output=False, treat_comments_as_code=frozenset({""}), treat_all_comments_as_code=False, supported_extensions=frozenset({"py", "pyx", "pyi"}), blocked_extensions=frozenset({"pex"}), constants=frozenset(), classes=frozenset( {"\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ", "C", "\x8e\U000422ac±\U000b5a1f\U000c4166", "ùÚ"} ), variables=frozenset(), dedup_headings=True, ), disregard_skip=True, ) @hypothesis.example( config=isort.Config( py_version="2", combine_straight_imports=True, ), disregard_skip=True, ) @hypothesis.given( config=st.from_type(isort.Config), disregard_skip=st.booleans(), ) @hypothesis.settings(deadline=None) def test_isort_is_idempotent(config: isort.Config, disregard_skip: bool) -> None: try: result = isort.code(CODE_SNIPPET, config=config, disregard_skip=disregard_skip) result = isort.code(result, config=config, disregard_skip=disregard_skip) assert result == isort.code(result, config=config, disregard_skip=disregard_skip) except ValueError: pass @hypothesis.example( config=isort.Config( py_version="all", force_to_top=frozenset(), skip=frozenset( { ".svn", ".venv", "build", "dist", ".bzr", ".tox", ".hg", ".mypy_cache", ".nox", "_build", "buck-out", "node_modules", ".git", ".eggs", ".pants.d", "venv", ".direnv", } ), skip_glob=frozenset(), skip_gitignore=True, line_length=79, wrap_length=0, line_ending="", sections=("FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"), no_sections=False, known_future_library=frozenset({"__future__"}), known_third_party=frozenset(), known_first_party=frozenset(), known_local_folder=frozenset(), known_standard_library=frozenset( { "pwd", "types", "nntplib", "jpeg", "pyclbr", "encodings", "ctypes", "macerrors", "filecmp", "dbm", "mimetypes", "statvfs", "msvcrt", "spwd", "codecs", "SimpleHTTPServer", "compiler", "pickletools", "tkinter", "pickle", "fm", "bsddb", "contextvars", "dummy_thread", "pipes", "heapq", "dircache", "commands", "unicodedata", "ntpath", "marshal", "fpformat", "linecache", "calendar", "pty", "MimeWriter", "inspect", "mmap", "ic", "tty", "nis", "new", "wave", "HTMLParser", "anydbm", "tracemalloc", "pdb", "sunau", "GL", "parser", "winsound", "dbhash", "zlib", "MacOS", "pprint", "crypt", "aetools", "DEVICE", "fl", "gettext", "asyncore", "copyreg", "queue", "resource", "turtledemo", "fnmatch", "hotshot", "trace", "string", "plistlib", "gzip", "functools", "aepack", "hashlib", "imp", "MiniAEFrame", "getpass", "shutil", "ttk", "multifile", "operator", "reprlib", "subprocess", "cgi", "select", "SimpleXMLRPCServer", "audioop", "macresource", "stringprep", "wsgiref", "SUNAUDIODEV", "atexit", "lzma", "asyncio", "datetime", "binhex", "autoGIL", "doctest", "thread", "enum", "tempfile", "posixfile", "mhlib", "html", "itertools", "exceptions", "sgmllib", "array", "test", "imputil", "shlex", "flp", "uu", "gdbm", "urlparse", "msilib", "termios", "modulefinder", "ossaudiodev", "timeit", "binascii", "popen2", "ConfigParser", "poplib", "zipfile", "cfmfile", "pstats", "AL", "contextlib", "code", "zipimport", "base64", "platform", "ast", "fileinput", "locale", "buildtools", "stat", "quopri", "readline", "collections", "aetypes", "concurrent", "runpy", "copy_reg", "rexec", "cmath", "optparse", "dummy_threading", "ColorPicker", "sched", "netrc", "sunaudiodev", "socketserver", "logging", "PixMapWrapper", "sysconfig", "Nav", "copy", "cmd", "csv", "chunk", "multiprocessing", "warnings", "weakref", "py_compile", "sre", "sre_parse", "curses", "threading", "re", "FrameWork", "_thread", "imgfile", "cd", "sre_constants", "xdrlib", "dataclasses", "urllib2", "StringIO", "configparser", "importlib", "UserList", "posixpath", "mailbox", "rfc822", "grp", "pydoc", "sets", "textwrap", "numbers", "W", "gl", "htmllib", "macostools", "tarfile", "ipaddress", "xmlrpc", "icopen", "traceback", "_winreg", "random", "CGIHTTPServer", "dis", "sha", "selectors", "statistics", "DocXMLRPCServer", "imghdr", "venv", "keyword", "xmlrpclib", "ftplib", "getopt", "posix", "smtpd", "profile", "sndhdr", "signal", "EasyDialogs", "dumbdbm", "fcntl", "SocketServer", "distutils", "symbol", "pathlib", "cStringIO", "imaplib", "unittest", "al", "cProfile", "robotparser", "BaseHTTPServer", "os", "pkgutil", "socket", "fractions", "shelve", "aifc", "cgitb", "xml", "decimal", "sre_compile", "ssl", "user", "Bastion", "formatter", "time", "abc", "winreg", "difflib", "FL", "bz2", "asynchat", "gc", "gensuitemodule", "symtable", "secrets", "Carbon", "mailcap", "sys", "bdb", "fpectl", "httplib", "webbrowser", "smtplib", "Cookie", "whichdb", "turtle", "tokenize", "UserString", "tabnanny", "site", "struct", "codeop", "email", "typing", "cookielib", "Queue", "rlcompleter", "errno", "macpath", "videoreader", "md5", "cPickle", "Tix", "io", "faulthandler", "Tkinter", "glob", "syslog", "telnetlib", "_dummy_thread", "hmac", "uuid", "imageop", "future_builtins", "json", "htmlentitydefs", "lib2to3", "UserDict", "mutex", "sqlite3", "findertools", "bisect", "builtins", "urllib", "http", "compileall", "argparse", "ScrolledText", "token", "dl", "applesingle", "math", "ensurepip", "mimify", "mimetools", "colorsys", "zipapp", "__builtin__", } ), extra_standard_library=frozenset(), known_other={"other": frozenset({"", "\x10\x1bm"})}, multi_line_output=0, forced_separate=(), indent=" ", comment_prefix=" #", length_sort=True, length_sort_straight=False, length_sort_sections=frozenset(), add_imports=frozenset(), remove_imports=frozenset( { "", "\U00076fe7þs\x0c\U000c8b75v\U00106541", "𥒒>\U0001960euj𒎕\x9e", "\x15\x9b", "\x02l", "\U000b71ef.\x1c", "\x7f?\U000ec91c", "\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«", "Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø", ";À¨|\x1b 𑐒🍸V", } ), append_only=False, reverse_relative=True, force_single_line=False, single_line_exclusions=( "Y\U000347d9g\x957K", "", "Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.", "·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ", "", ":sI¶", "", ), default_section="THIRDPARTY", import_headings={}, import_footers={}, balanced_wrapping=False, use_parentheses=True, order_by_type=True, atomic=False, lines_before_imports=-1, lines_after_imports=-1, lines_between_sections=1, lines_between_types=0, combine_as_imports=True, combine_star=False, include_trailing_comma=False, from_first=False, verbose=False, quiet=False, force_adds=False, force_alphabetical_sort_within_sections=False, force_alphabetical_sort=False, force_grid_wrap=0, force_sort_within_sections=False, lexicographical=False, ignore_whitespace=False, no_lines_before=frozenset( { "uøø", "¢", "&\x8c5Ï\U000e5f01Ø", "\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ", "\U000e374c8", "w", } ), no_inline_sort=False, ignore_comments=False, case_sensitive=False, sources=( { "py_version": "py3", "force_to_top": frozenset(), "skip": frozenset( { ".svn", ".venv", "build", "dist", ".bzr", ".tox", ".hg", ".mypy_cache", ".nox", "_build", "buck-out", "node_modules", ".git", ".eggs", ".pants.d", "venv", ".direnv", } ), "skip_glob": frozenset(), "skip_gitignore": False, "line_length": 79, "wrap_length": 0, "line_ending": "", "sections": ("FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"), "no_sections": False, "known_future_library": frozenset({"__future__"}), "known_third_party": frozenset(), "known_first_party": frozenset(), "known_local_folder": frozenset(), "known_standard_library": frozenset( { "pwd", "copy", "cmd", "csv", "chunk", "multiprocessing", "warnings", "types", "weakref", "nntplib", "pyclbr", "encodings", "py_compile", "sre", "ctypes", "sre_parse", "filecmp", "curses", "threading", "dbm", "re", "_thread", "sre_constants", "xdrlib", "dataclasses", "mimetypes", "configparser", "importlib", "msvcrt", "spwd", "posixpath", "mailbox", "codecs", "grp", "pickletools", "tkinter", "pickle", "contextvars", "pydoc", "textwrap", "numbers", "pipes", "heapq", "tarfile", "unicodedata", "ntpath", "ipaddress", "marshal", "xmlrpc", "traceback", "linecache", "calendar", "pty", "random", "dis", "selectors", "statistics", "imghdr", "venv", "inspect", "mmap", "keyword", "ftplib", "tty", "nis", "getopt", "posix", "smtpd", "wave", "profile", "sndhdr", "signal", "tracemalloc", "pdb", "sunau", "winsound", "parser", "zlib", "fcntl", "pprint", "distutils", "crypt", "symbol", "gettext", "pathlib", "asyncore", "copyreg", "imaplib", "unittest", "queue", "resource", "turtledemo", "fnmatch", "cProfile", "os", "pkgutil", "socket", "trace", "fractions", "string", "shelve", "plistlib", "aifc", "gzip", "functools", "cgitb", "xml", "hashlib", "decimal", "imp", "sre_compile", "ssl", "formatter", "winreg", "time", "getpass", "shutil", "abc", "difflib", "bz2", "operator", "reprlib", "subprocess", "cgi", "select", "asynchat", "audioop", "gc", "secrets", "symtable", "mailcap", "sys", "bdb", "fpectl", "stringprep", "webbrowser", "smtplib", "wsgiref", "atexit", "lzma", "asyncio", "datetime", "binhex", "doctest", "turtle", "enum", "tempfile", "tokenize", "tabnanny", "site", "html", "struct", "itertools", "codeop", "email", "array", "test", "typing", "shlex", "uu", "msilib", "termios", "rlcompleter", "modulefinder", "ossaudiodev", "timeit", "binascii", "poplib", "errno", "macpath", "zipfile", "io", "faulthandler", "pstats", "contextlib", "code", "glob", "zipimport", "base64", "syslog", "platform", "ast", "fileinput", "telnetlib", "locale", "_dummy_thread", "hmac", "stat", "uuid", "quopri", "readline", "collections", "json", "concurrent", "lib2to3", "sqlite3", "runpy", "cmath", "optparse", "bisect", "builtins", "urllib", "dummy_threading", "http", "compileall", "argparse", "token", "sched", "netrc", "math", "ensurepip", "socketserver", "colorsys", "zipapp", "logging", "sysconfig", } ), "extra_standard_library": frozenset(), "known_other": {}, "multi_line_output": 0, "forced_separate": (), "indent": " ", "comment_prefix": " #", "length_sort": False, "length_sort_straight": False, "length_sort_sections": frozenset(), "add_imports": frozenset(), "remove_imports": frozenset(), "append_only": False, "reverse_relative": False, "force_single_line": False, "single_line_exclusions": (), "default_section": "THIRDPARTY", "import_headings": {}, "import_footers": {}, "balanced_wrapping": False, "use_parentheses": False, "order_by_type": True, "atomic": False, "lines_before_imports": -1, "lines_after_imports": -1, "lines_between_sections": 1, "lines_between_types": 0, "combine_as_imports": False, "combine_star": False, "include_trailing_comma": False, "from_first": False, "verbose": False, "quiet": False, "force_adds": False, "force_alphabetical_sort_within_sections": False, "force_alphabetical_sort": False, "force_grid_wrap": 0, "force_sort_within_sections": False, "lexicographical": False, "ignore_whitespace": False, "no_lines_before": frozenset(), "no_inline_sort": False, "ignore_comments": False, "case_sensitive": False, "sources": (), "virtual_env": "", "conda_env": "", "ensure_newline_before_comments": False, "directory": "", "profile": "", "honor_noqa": False, "src_paths": frozenset(), "remove_redundant_aliases": False, "float_to_top": False, "filter_files": False, "formatter": "", "formatting_function": None, "color_output": False, "treat_comments_as_code": frozenset(), "treat_all_comments_as_code": False, "supported_extensions": frozenset({"py", "pyx", "pyi"}), "blocked_extensions": frozenset({"pex"}), "constants": frozenset(), "classes": frozenset(), "variables": frozenset(), "dedup_headings": False, "source": "defaults", }, { "classes": frozenset( { "\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ", "C", "\x8e\U000422ac±\U000b5a1f\U000c4166", "ùÚ", } ), "single_line_exclusions": ( "Y\U000347d9g\x957K", "", "Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.", "·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ", "", ":sI¶", "", ), "indent": " ", "no_lines_before": frozenset( { "uøø", "¢", "&\x8c5Ï\U000e5f01Ø", "\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ", "\U000e374c8", "w", } ), "quiet": False, "honor_noqa": False, "dedup_headings": True, "known_other": { "\x10\x1bm": frozenset({"\U000682a49\U000e1a63²KǶ4", "", "\x1a", "©"}), "": frozenset({"íå\x94Ì", "\U000cf258"}), }, "treat_comments_as_code": frozenset({""}), "length_sort": True, "reverse_relative": True, "combine_as_imports": True, "py_version": "all", "use_parentheses": True, "skip_gitignore": True, "remove_imports": frozenset( { "", "\U00076fe7þs\x0c\U000c8b75v\U00106541", "𥒒>\U0001960euj𒎕\x9e", "\x15\x9b", "\x02l", "\U000b71ef.\x1c", "\x7f?\U000ec91c", "\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«", "Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø", ";À¨|\x1b 𑐒🍸V", } ), "atomic": False, "source": "runtime", }, ), virtual_env="", conda_env="", ensure_newline_before_comments=False, directory="/home/abuild/rpmbuild/BUILD/isort-5.11.0", profile="", honor_noqa=False, remove_redundant_aliases=False, float_to_top=False, filter_files=False, formatting_function=None, color_output=False, treat_comments_as_code=frozenset({""}), treat_all_comments_as_code=False, supported_extensions=frozenset({"py", "pyx", "pyi"}), blocked_extensions=frozenset({"pex"}), constants=frozenset(), classes=frozenset( {"\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ", "C", "\x8e\U000422ac±\U000b5a1f\U000c4166", "ùÚ"} ), variables=frozenset(), dedup_headings=True, ), disregard_skip=True, ) @hypothesis.given( config=st.from_type(isort.Config), disregard_skip=st.booleans(), ) @hypothesis.settings(deadline=None) def test_isort_doesnt_lose_imports_or_comments(config: isort.Config, disregard_skip: bool) -> None: result = isort.code(CODE_SNIPPET, config=config, disregard_skip=disregard_skip) for should_be_retained in SHOULD_RETAIN: if should_be_retained not in result: if config.ignore_comments and should_be_retained.startswith("comment"): continue assert should_be_retained in result isort-9.0.1/tests/integration/test_ticketed_features.py0000644000000000000000000000602413615410400020373 0ustar00"""Tests that need installation of other packages.""" # TODO: find a way to install example-isort-formatting-plugin to pass tests # from io import StringIO # import pytest # import isort # from isort import api, exceptions # def test_isort_supports_formatting_plugins(): # """Test to ensure isort provides a way to create and share formatting plugins. # See: https://github.com/pycqa/isort/issues/1353. # """ # # formatting plugin # assert isort.code("import a", formatter="example") == "import a\n" # # non-existent plugin # with pytest.raises(exceptions.FormattingPluginDoesNotExist): # assert isort.code("import a", formatter="madeupfake") == "import a\n" # def test_isort_literals_issue_1358(): # assert ( # isort.code( # """ # import x # import a # # isort: list # __all__ = ["b", "a", "b"] # # isort: unique-list # __all__ = ["b", "a", "b"] # # isort: tuple # __all__ = ("b", "a", "b") # # isort: unique-tuple # __all__ = ("b", "a", "b") # # isort: set # __all__ = {"b", "a", "b"} # def method(): # # isort: list # x = ["b", "a"] # # isort: dict # y = {"z": "z", "b": "b", "b": "c"}""" # ) # == """ # import a # import x # # isort: list # __all__ = ['a', 'b', 'b'] # # isort: unique-list # __all__ = ['a', 'b'] # # isort: tuple # __all__ = ('a', 'b', 'b') # # isort: unique-tuple # __all__ = ('a', 'b') # # isort: set # __all__ = {'a', 'b'} # def method(): # # isort: list # x = ['a', 'b'] # # isort: dict # y = {'b': 'c', 'z': 'z'}""" # ) # assert ( # isort.code( # """ # import x # import a # # isort: list # __all__ = ["b", "a", "b"] # # isort: unique-list # __all__ = ["b", "a", "b"] # # isort: tuple # __all__ = ("b", "a", "b") # # isort: unique-tuple # __all__ = ("b", "a", "b") # # isort: set # __all__ = {"b", "a", "b"} # def method(): # # isort: list # x = ["b", "a"] # # isort: assignments # d = 1 # b = 2 # a = 3 # # isort: dict # y = {"z": "z", "b": "b", "b": "c"}""", # formatter="example", # ) # == """ # import a # import x # # isort: list # __all__ = ["a", "b", "b"] # # isort: unique-list # __all__ = ["a", "b"] # # isort: tuple # __all__ = ("a", "b", "b") # # isort: unique-tuple # __all__ = ("a", "b") # # isort: set # __all__ = {"a", "b"} # def method(): # # isort: list # x = ["a", "b"] # # isort: assignments # a = 3 # b = 2 # d = 1 # # isort: dict # y = {"b": "c", "z": "z"}""" # ) # assert api.sort_stream( # input_stream=StringIO( # """ # import a # import x # # isort: list # __all__ = ["b", "a", "b"] # # isort: unique-list # __all__ = ["b", "a", "b"] # # isort: tuple # __all__ = ("b", "a", "b") # # isort: unique-tuple # __all__ = ("b", "a", "b") # # isort: set # __all__ = {"b", "a", "b"} # def method(): # # isort: list # x = ["b", "a"] # # isort: assignments # d = 1 # b = 2 # a = 3 # # isort: dict # y = {"z": "z", "b": "b", "b": "c"}""", # ), # output_stream=StringIO(), # ) isort-9.0.1/tests/unit/__init__.py0000644000000000000000000000000013615410400014021 0ustar00isort-9.0.1/tests/unit/conftest.py0000644000000000000000000000106313615410400014121 0ustar00"""isort test wide fixtures and configuration""" import os from pathlib import Path import pytest TEST_DIR = os.path.dirname(os.path.abspath(__file__)) SRC_DIR = os.path.abspath(os.path.join(TEST_DIR, "../../isort/")) @pytest.fixture def test_dir(): return TEST_DIR @pytest.fixture def src_dir(): return SRC_DIR @pytest.fixture def test_path(): return Path(TEST_DIR).resolve() @pytest.fixture def src_path(): return Path(SRC_DIR).resolve() @pytest.fixture def examples_path(): return Path(TEST_DIR).resolve() / "example_projects" isort-9.0.1/tests/unit/example_crlf_file.py0000644000000000000000000000015313615410400015733 0ustar00import b import a def func(): x = 1 y = 2 z = 3 c = 4 return x + y + z + c isort-9.0.1/tests/unit/test_action_comments.py0000644000000000000000000000150513615410400016516 0ustar00"""Tests for isort action comments, such as isort: skip""" import isort def test_isort_off_and_on(): """Test so ensure isort: off action comment and associated on action comment work together""" # as top of file comment assert ( isort.code( """# isort: off import a import a # isort: on import a import a """ ) == """# isort: off import a import a # isort: on import a """ ) # as middle comment assert ( isort.code( """ import a import a # isort: off import a import a """ ) == """ import a # isort: off import a import a """ ) def test_skip_sort_reexports(): code = """my_list = [\"bee\", \"Albatross\", \"Dinosaur\", \"cat\"] __all__ = my_list # isort: skip """ assert isort.code(code, sort_reexports=True) == code isort-9.0.1/tests/unit/test_api.py0000644000000000000000000000745413615410400014116 0ustar00"""Tests the isort API module""" import os from io import StringIO from unittest.mock import MagicMock, patch import pytest from isort import ImportKey, api from isort.settings import Config imperfect_content = "import b\nimport a\n" fixed_content = "import a\nimport b\n" fixed_diff = "+import a\n import b\n-import a\n" @pytest.fixture def imperfect(tmpdir): imperfect_file = tmpdir.join("test_needs_changes.py") imperfect_file.write_text(imperfect_content, "utf8") return imperfect_file def test_sort_file_with_bad_syntax(tmpdir) -> None: tmp_file = tmpdir.join("test_bad_syntax.py") tmp_file.write_text("""print('mismatching quotes")""", "utf8") with pytest.warns(UserWarning): api.sort_file(tmp_file, atomic=True) with pytest.warns(UserWarning): api.sort_file(tmp_file, atomic=True, write_to_stdout=True) def test_sort_file(imperfect) -> None: assert api.sort_file(imperfect) assert imperfect.read() == fixed_content def test_sort_file_in_place(imperfect) -> None: assert api.sort_file(imperfect, overwrite_in_place=True) assert imperfect.read() == fixed_content def test_sort_file_to_stdout(capsys, imperfect) -> None: assert api.sort_file(imperfect, write_to_stdout=True) out, _ = capsys.readouterr() assert out == fixed_content.replace("\n", os.linesep) def test_other_ask_to_apply(imperfect) -> None: # First show diff, but ensure change won't get written by asking to apply # and ensuring answer is no. with patch("builtins.input", MagicMock(return_value="n")): assert not api.sort_file(imperfect, ask_to_apply=True) assert imperfect.read() == imperfect_content # Then run again, but apply the change (answer is yes) with patch("builtins.input", MagicMock(return_value="y")): assert api.sort_file(imperfect, ask_to_apply=True) assert imperfect.read() == fixed_content def test_check_file_no_changes(capsys, tmpdir) -> None: perfect = tmpdir.join("test_no_changes.py") perfect.write_text("import a\nimport b\n", "utf8") assert api.check_file(perfect, show_diff=True) out, _ = capsys.readouterr() assert not out def test_check_file_with_changes(capsys, imperfect) -> None: assert not api.check_file(imperfect, show_diff=True) out, _ = capsys.readouterr() assert fixed_diff.replace("\n", os.linesep) in out def test_sorted_imports_multiple_configs() -> None: with pytest.raises(ValueError, match="You can either specify custom configuration options"): api.sort_code_string("import os", config=Config(line_length=80), line_length=80) def test_diff_stream() -> None: output = StringIO() assert api.sort_stream(StringIO("import b\nimport a\n"), output, show_diff=True) output.seek(0) assert fixed_diff in output.read() def test_sort_code_string_mixed_newlines(): assert api.sort_code_string("import A\n\r\nimportA\n\n") == "import A\r\n\r\nimportA\r\n\n" def test_find_imports_in_file(imperfect): found_imports = list(api.find_imports_in_file(imperfect)) assert "b" in [found_import.module for found_import in found_imports] def test_find_imports_in_file_error(tmpdir): test_path = tmpdir.join("test_path.py") test_path.mkdir() with pytest.warns(UserWarning): assert not list(api.find_imports_in_file(test_path)) def test_find_imports_in_code(): code = """ from x.y import z as a from x.y import z as a from x.y import z import x.y import x """ assert len(list(api.find_imports_in_code(code))) == 5 assert len(list(api.find_imports_in_code(code, unique=True))) == 4 assert len(list(api.find_imports_in_code(code, unique=ImportKey.ATTRIBUTE))) == 3 assert len(list(api.find_imports_in_code(code, unique=ImportKey.MODULE))) == 2 assert len(list(api.find_imports_in_code(code, unique=ImportKey.PACKAGE))) == 1 isort-9.0.1/tests/unit/test_comments.py0000644000000000000000000000156013615410400015162 0ustar00from hypothesis import given from hypothesis import strategies as st import isort.comments def test_add_to_line(): assert ( isort.comments.add_to_line([], "import os # comment", removed=True).strip() == "import os" ) # These tests were written by the `hypothesis.extra.ghostwriter` module # and is provided under the Creative Commons Zero public domain dedication. @given( comments=st.one_of(st.none(), st.lists(st.text())), original_string=st.text(), removed=st.booleans(), comment_prefix=st.text(), ) def test_fuzz_add_to_line(comments, original_string, removed, comment_prefix): isort.comments.add_to_line( comments=comments, original_string=original_string, removed=removed, comment_prefix=comment_prefix, ) @given(line=st.text()) def test_fuzz_parse(line): isort.comments.parse(line=line) isort-9.0.1/tests/unit/test_exceptions.py0000644000000000000000000000757513615410400015532 0ustar00import pickle from isort import exceptions class TestISortError: def setup_class(self): self.instance = exceptions.ISortError() def test_init(self): assert isinstance(self.instance, exceptions.ISortError) def test_pickleable(self): assert isinstance(pickle.loads(pickle.dumps(self.instance)), exceptions.ISortError) class TestExistingSyntaxErrors(TestISortError): def setup_class(self): self.instance: exceptions.ExistingSyntaxErrors = exceptions.ExistingSyntaxErrors( "file_path" ) def test_variables(self): assert self.instance.file_path == "file_path" class TestIntroducedSyntaxErrors(TestISortError): def setup_class(self): self.instance: exceptions.IntroducedSyntaxErrors = exceptions.IntroducedSyntaxErrors( "file_path" ) def test_variables(self): assert self.instance.file_path == "file_path" class TestFileSkipped(TestISortError): def setup_class(self): self.instance: exceptions.FileSkipped = exceptions.FileSkipped("message", "file_path") def test_variables(self): assert self.instance.file_path == "file_path" assert str(self.instance) == "message" class TestFileSkipComment(TestISortError): def setup_class(self): self.instance: exceptions.FileSkipComment = exceptions.FileSkipComment("file_path") def test_variables(self): assert self.instance.file_path == "file_path" class TestFileSkipSetting(TestISortError): def setup_class(self): self.instance: exceptions.FileSkipSetting = exceptions.FileSkipSetting("file_path") def test_variables(self): assert self.instance.file_path == "file_path" class TestProfileDoesNotExist(TestISortError): def setup_class(self): self.instance: exceptions.ProfileDoesNotExist = exceptions.ProfileDoesNotExist("profile") def test_variables(self): assert self.instance.profile == "profile" class TestSortingFunctionDoesNotExist(TestISortError): def setup_class(self): self.instance: exceptions.SortingFunctionDoesNotExist = ( exceptions.SortingFunctionDoesNotExist("round", ["square", "peg"]) ) def test_variables(self): assert self.instance.sort_order == "round" assert self.instance.available_sort_orders == ["square", "peg"] class TestLiteralParsingFailure(TestISortError): def setup_class(self): self.instance: exceptions.LiteralParsingFailure = exceptions.LiteralParsingFailure( "x = [", SyntaxError ) def test_variables(self): assert self.instance.code == "x = [" assert self.instance.original_error is SyntaxError class TestLiteralSortTypeMismatch(TestISortError): def setup_class(self): self.instance: exceptions.LiteralSortTypeMismatch = exceptions.LiteralSortTypeMismatch( tuple, list ) def test_variables(self): assert self.instance.kind is tuple assert self.instance.expected_kind is list class TestAssignmentsFormatMismatch(TestISortError): def setup_class(self): self.instance: exceptions.AssignmentsFormatMismatch = exceptions.AssignmentsFormatMismatch( "print x" ) def test_variables(self): assert self.instance.code == "print x" class TestUnsupportedSettings(TestISortError): def setup_class(self): self.instance: exceptions.UnsupportedSettings = exceptions.UnsupportedSettings( {"apply": {"value": "true", "source": "/"}} ) def test_variables(self): assert self.instance.unsupported_settings == {"apply": {"value": "true", "source": "/"}} class TestUnsupportedEncoding(TestISortError): def setup_class(self): self.instance: exceptions.UnsupportedEncoding = exceptions.UnsupportedEncoding("file.py") def test_variables(self): assert self.instance.filename == "file.py" isort-9.0.1/tests/unit/test_files.py0000644000000000000000000000037113615410400014436 0ustar00from isort import files from isort.settings import DEFAULT_CONFIG def test_find(tmpdir): tmp_file = tmpdir.join("file.py") tmp_file.write("import os, sys\n") assert tuple(files.find((tmp_file,), DEFAULT_CONFIG, [], [])) == (tmp_file,) isort-9.0.1/tests/unit/test_format.py0000644000000000000000000001052413615410400014625 0ustar00from io import StringIO from pathlib import Path from unittest.mock import MagicMock, patch import colorama import pytest from hypothesis import given, reject from hypothesis import strategies as st import isort.format def test_ask_whether_to_apply_changes_to_file(): with patch("builtins.input", MagicMock(return_value="y")): assert isort.format.ask_whether_to_apply_changes_to_file("") with patch("builtins.input", MagicMock(return_value="n")): assert not isort.format.ask_whether_to_apply_changes_to_file("") with patch("builtins.input", MagicMock(return_value="q")): with pytest.raises(SystemExit): assert isort.format.ask_whether_to_apply_changes_to_file("") def test_basic_printer(capsys): printer = isort.format.create_terminal_printer( color=False, success="{success}: {message}", error="{error}: {message}" ) printer.success("All good!") out, _ = capsys.readouterr() assert out == "SUCCESS: All good!\n" printer.error("Some error") _, err = capsys.readouterr() assert err == "ERROR: Some error\n" printer = isort.format.create_terminal_printer( color=False, success="success: {message}: {success}", error="error: {message}: {error}" ) printer.success("All good!") out, _ = capsys.readouterr() assert out == "success: All good!: SUCCESS\n" printer.error("Some error") _, err = capsys.readouterr() assert err == "error: Some error: ERROR\n" def test_basic_printer_diff(capsys): printer = isort.format.create_terminal_printer(color=False) printer.diff_line("+ added line\n") printer.diff_line("- removed line\n") out, _ = capsys.readouterr() assert out == "+ added line\n- removed line\n" def test_colored_printer_success(capsys): printer = isort.format.create_terminal_printer(color=True, success="{success}: {message}") printer.success("All good!") out, _ = capsys.readouterr() assert "SUCCESS" in out assert "All good!" in out assert colorama.Fore.GREEN in out def test_colored_printer_error(capsys): printer = isort.format.create_terminal_printer(color=True, error="{error}: {message}") printer.error("Some error") _, err = capsys.readouterr() assert "ERROR" in err assert "Some error" in err assert colorama.Fore.RED in err def test_colored_printer_diff(capsys): printer = isort.format.create_terminal_printer(color=True) printer.diff_line("+++ file1\n") printer.diff_line("--- file2\n") printer.diff_line("+ added line\n") printer.diff_line("normal line\n") printer.diff_line("- removed line\n") printer.diff_line("normal line\n") out, _ = capsys.readouterr() # No color added to lines with multiple + and -'s assert out.startswith("+++ file1\n--- file2\n") # Added lines are green assert colorama.Fore.GREEN + "+ added line" in out # Removed lines are red assert colorama.Fore.RED + "- removed line" in out # Normal lines are reset back assert colorama.Style.RESET_ALL + "normal line" in out def test_colored_printer_diff_output(capsys): output = StringIO() printer = isort.format.create_terminal_printer(color=True, output=output) printer.diff_line("a line\n") out, _ = capsys.readouterr() assert out == "" output.seek(0) assert output.read().startswith("a line\n") @patch("isort.format.colorama_unavailable", True) def test_colorama_not_available_handled_gracefully(capsys): with pytest.raises(SystemExit) as system_exit: _ = isort.format.create_terminal_printer(color=True) assert system_exit.value.code assert int(system_exit.value.code) > 0 _, err = capsys.readouterr() assert "colorama" in err assert "colors extra" in err # This test code was written by the `hypothesis.extra.ghostwriter` module # and is provided under the Creative Commons Zero public domain dedication. @given( file_input=st.text(), file_output=st.text(), file_path=st.one_of(st.none(), st.builds(Path)), output=st.one_of(st.none(), st.builds(StringIO, st.text())), ) def test_fuzz_show_unified_diff(file_input, file_output, file_path, output): try: isort.format.show_unified_diff( file_input=file_input, file_output=file_output, file_path=file_path, output=output, ) except UnicodeEncodeError: reject() isort-9.0.1/tests/unit/test_hooks.py0000644000000000000000000001231713615410400014462 0ustar00import os from pathlib import Path from subprocess import CompletedProcess from unittest.mock import MagicMock, patch import pytest from isort import exceptions, hooks from isort._version import _IS_COMPILED def test_git_hook(src_dir): """Simple smoke level testing of git hooks""" # Ensure correct subprocess command is called with patch( "subprocess.run", return_value=CompletedProcess("command", returncode=0, stdout=b"") ) as run_mock: hooks.git_hook() run_mock.assert_called_once() assert run_mock.call_args[0][0] == [ "git", "diff-index", "--cached", "--name-only", "--diff-filter=ACMRTUXB", "HEAD", ] with patch( "subprocess.run", return_value=CompletedProcess("command", returncode=0, stdout=b"") ) as run_mock: hooks.git_hook(lazy=True) run_mock.assert_called_once() assert run_mock.call_args[0][0] == [ "git", "diff-index", "--name-only", "--diff-filter=ACMRTUXB", "HEAD", ] # Test that non python files aren't processed with patch( "subprocess.run", return_value=CompletedProcess( "command", returncode=0, stdout=b"README.md\nsetup.cfg\nLICDENSE\nmkdocs.yml\ntest" ), ) as run_mock: hooks.git_hook(modify=True) run_mock.assert_called_once() # Test with incorrectly sorted file returned from git file_name = os.path.join(src_dir, "main.py") with patch( "subprocess.run", side_effect=[ CompletedProcess("command", returncode=0, stdout=file_name.encode()), CompletedProcess("command", returncode=0, stdout=b"import b,a"), ], ) as run_mock: errors = hooks.git_hook(modify=True, strict=True) assert run_mock.call_count == 2 assert errors == 1 @pytest.mark.skipif(reason="Can't use these mocks in mypyc-compiled code.", condition=_IS_COMPILED) def test_git_hook_with_mocks(src_dir: str) -> None: """ Additional tests for the git hook, split off to allow running some when compiled. """ mock_main_py = MagicMock(return_value=[os.path.join(src_dir, "main.py")]) mock_imperfect = MagicMock() mock_imperfect.return_value.stdout = b"import b\nimport a" # Test with sorted file returned from git and modify=False with patch("isort.hooks.get_lines", mock_main_py): with patch("subprocess.run", mock_imperfect): with patch("isort.api.sort_file", MagicMock(return_value=False)) as api_mock: hooks.git_hook(modify=False) api_mock.assert_not_called() # Test with skipped file returned from git with patch("isort.hooks.get_lines", MagicMock(return_value=[os.path.join(src_dir, "main.py")])): class FakeProcessResponse: stdout = b"# isort: skip-file\nimport b\nimport a\n" with patch("subprocess.run", MagicMock(return_value=FakeProcessResponse())): with patch("isort.api", MagicMock(side_effect=exceptions.FileSkipped("", ""))): hooks.git_hook(modify=True) def test_git_hook_lazy(tmpdir): # Write an actual unsorted file to disk & check that `lazy=True` spots it has_problems = tmpdir.join("test_has_problems.py") has_problems.write_text("import b\nimport a\n", "utf8") with patch( "subprocess.run", side_effect=[ CompletedProcess("command", returncode=0, stdout=str(has_problems).encode()), # Second call to subprocess.run should not be made when lazy=True RuntimeError("donotcall"), ], ) as run_mock: found_errors = hooks.git_hook(lazy=True, strict=True) # subprocess.run (via get_output) should not be called when lazy=True # because we're not asking git for the staged content assert run_mock.call_count == 1 assert found_errors == 1 @pytest.mark.skipif(reason="Can't use these mocks in mypyc-compiled code.", condition=_IS_COMPILED) def test_git_hook_uses_the_configuration_file_specified_in_settings_path(tmp_path: Path) -> None: subdirectory_path = tmp_path / "subdirectory" configuration_file_path = subdirectory_path / ".isort.cfg" # Inserting the modified file in the parent directory of the configuration file ensures that it # will not be found by the normal search routine modified_file_path = configuration_file_path.parent.parent / "somefile.py" # This section will be used to check that the configuration file was indeed loaded section = "testsection" os.mkdir(subdirectory_path) with open(configuration_file_path, "w") as fd: fd.write("[isort]\n") fd.write(f"sections={section}") with open(modified_file_path, "w") as fd: pass files_modified = [str(modified_file_path.absolute())] with patch("isort.hooks.get_lines", MagicMock(return_value=files_modified)): with patch("isort.hooks.get_output", MagicMock(return_value="")): with patch("isort.api.check_code_string", MagicMock()) as run_mock: hooks.git_hook(settings_file=str(configuration_file_path)) assert run_mock.call_args[1]["config"].sections == (section,) isort-9.0.1/tests/unit/test_identify.py0000644000000000000000000001056413615410400015154 0ustar00from io import StringIO from isort import Config, identify from isort.identify import Import def imports_in_code(code: str, **kwargs) -> list[identify.Import]: return list(identify.imports(StringIO(code), **kwargs)) def test_top_only(): imports_in_function = """ import abc def xyz(): import defg """ assert len(imports_in_code(imports_in_function)) == 2 assert len(imports_in_code(imports_in_function, top_only=True)) == 1 imports_after_class = """ import abc class MyObject: pass import defg """ assert len(imports_in_code(imports_after_class)) == 2 assert len(imports_in_code(imports_after_class, top_only=True)) == 1 def test_top_doc_string(): assert ( len( imports_in_code( ''' #! /bin/bash import x """import abc from y import z """ import abc ''' ) ) == 1 ) def test_yield_and_raise_edge_cases(): assert not imports_in_code( """ raise SomeException("Blah") \\ from exceptionsInfo.popitem()[1] """ ) assert not imports_in_code( """ def generator_function(): yield \\ from other_function()[1] """ ) assert ( len( imports_in_code( """ # one # two def function(): # three \\ import b import a """ ) ) == 2 ) assert ( len( imports_in_code( """ # one # two def function(): raise \\ import b import a """ ) ) == 1 ) assert not imports_in_code( """ def generator_function(): ( yield from other_function()[1] ) """ ) assert not imports_in_code( """ def generator_function(): ( ( (((( ((((( (( ((( yield from other_function()[1] ))))))))))))) ))) """ ) assert ( len( imports_in_code( """ def generator_function(): import os yield \\ from other_function()[1] """ ) ) == 1 ) assert not imports_in_code( """ def generator_function(): ( ( (((( ((((( (( ((( yield """ ) assert not imports_in_code( """ def generator_function(): ( ( (((( ((((( (( ((( raise ( """ ) assert not imports_in_code( """ def generator_function(): ( ( (((( ((((( (( ((( raise \\ from \\ """ ) assert ( len( imports_in_code( """ def generator_function(): ( ( (((( ((((( (( ((( raise \\ from \\ import c import abc import xyz """ ) ) == 2 ) def test_complex_examples(): assert ( len( imports_in_code( """ import a, b, c; import n x = ( 1, 2, 3 ) import x from os \\ import path from os ( import path ) from os import \\ path from os \\ import ( path ) from os import ( \\""" ) ) == 9 ) assert not imports_in_code("from os import \\") assert imports_in_code( """ from os \\ import ( system""" ) == [ Import( line_number=2, indented=False, module="os", attribute="system", alias=None, cimport=False, file_path=None, ) ] assert imports_in_code( """ from os \\ cimport ( system""" ) == [ Import( line_number=2, indented=False, module="os", attribute="system", alias=None, cimport=True, file_path=None, ) ] def test_aliases(): assert imports_in_code("import os as os")[0].alias == "os" assert not imports_in_code( "import os as os", config=Config( remove_redundant_aliases=True, ), )[0].alias assert imports_in_code("from os import path as path")[0].alias == "path" assert not imports_in_code( "from os import path as path", config=Config(remove_redundant_aliases=True) )[0].alias def test_indented(): assert not imports_in_code("import os")[0].indented assert imports_in_code(" import os")[0].indented assert imports_in_code("\timport os")[0].indented isort-9.0.1/tests/unit/test_importable.py0000644000000000000000000000342613615410400015476 0ustar00"""Basic set of tests to ensure entire code base is importable""" # ruff: noqa: PLC0415 import subprocess import sys from contextlib import suppress from pathlib import Path def test_importable(): """Simple smoketest to ensure all isort modules are importable""" import isort import isort._version import isort.api import isort.comments import isort.exceptions import isort.format import isort.hooks import isort.logo import isort.main import isort.output import isort.parse import isort.place import isort.profiles import isort.sections import isort.settings import isort.sorting import isort.stdlibs import isort.stdlibs.all import isort.stdlibs.py2 import isort.stdlibs.py3 import isort.stdlibs.py27 import isort.stdlibs.py36 import isort.stdlibs.py37 import isort.stdlibs.py38 import isort.stdlibs.py39 import isort.stdlibs.py310 import isort.stdlibs.py311 import isort.stdlibs.py312 import isort.stdlibs.py313 import isort.stdlibs.py314 import isort.stdlibs.py315 import isort.utils import isort.wrap import isort.wrap_modes # Ensure predictable test failure regardless of the ambient pytest argv causing a `SystemExit` # in isort.__main__ when it tries to parse the pytest argv. with suppress(SystemExit): import isort.__main__ # noqa: F401 def test_module_cli_invocation_works(tmp_path: Path) -> None: file_path = tmp_path / "sample.py" file_path.write_text("import os\nimport sys\n", encoding="utf-8") result = subprocess.run( [sys.executable, "-m", "isort", str(file_path), "--check-only"], check=False, capture_output=True, text=True, ) assert result.returncode == 0, result.stderr isort-9.0.1/tests/unit/test_io.py0000644000000000000000000000274713615410400013754 0ustar00import sys from unittest.mock import patch import pytest from isort import io from isort.exceptions import UnsupportedEncoding class TestFile: @pytest.mark.skipif(sys.platform == "win32", reason="Can't run file encoding test in AppVeyor") def test_read(self, tmpdir): test_file_content = """# -*- encoding: ascii -*- import Ὡ """ test_file = tmpdir.join("file.py") test_file.write(test_file_content) with pytest.raises(UnicodeDecodeError): with io.File.read(str(test_file)) as file_handler: file_handler.stream.read() def test_from_content(self, tmpdir): test_file = tmpdir.join("file.py") test_file.write_text("import os", "utf8") file_obj = io.File.from_contents("import os", filename=str(test_file)) assert file_obj assert file_obj.extension == "py" def test_open(self, tmpdir): with pytest.raises(FileNotFoundError): io.File._open("THISCANTBEAREALFILEὩὩὩὩὩὩὩὩὩὩὩὩ.ὩὩὩὩὩ") def raise_arbitrary_exception(*args, **kwargs): raise RuntimeError("test") test_file = tmpdir.join("file.py") test_file.write("import os") assert io.File._open(str(test_file)) # correctly responds to error determining encoding with patch("tokenize.detect_encoding", raise_arbitrary_exception): with pytest.raises(UnsupportedEncoding): io.File._open(str(test_file)) isort-9.0.1/tests/unit/test_isort.py0000644000000000000000000056704313615410400014512 0ustar00"""Tests all major functionality of the isort library Should be ran using py.test by simply running py.test in the isort project directory """ import os import os.path import subprocess import sys from io import StringIO from pathlib import Path from tempfile import NamedTemporaryFile from typing import TYPE_CHECKING, Any from collections.abc import Iterator import pytest import isort from isort import api, files, sections from isort.exceptions import ExistingSyntaxErrors, FileSkipped, MissingSection from isort.settings import Config from isort.utils import exists_case_sensitive from isort.main import parse_args from isort.main import main from .utils import UnreadableStream, as_stream if TYPE_CHECKING: WrapModes: Any else: from isort.wrap_modes import WrapModes TEST_DEFAULT_CONFIG = """ [*.{py,pyi}] max_line_length = 120 indent_style = space indent_size = 4 known_first_party = isort known_third_party = kate known_something_else = something_entirely_different sections = FUTURE, STDLIB, THIRDPARTY, FIRSTPARTY, LOCALFOLDER, SOMETHING_ELSE ignore_frosted_errors = E103 skip = build,.tox,venv balanced_wrapping = true """ SHORT_IMPORT = "from third_party import lib1, lib2, lib3, lib4" SINGLE_FROM_IMPORT = "from third_party import lib1" SINGLE_LINE_LONG_IMPORT = "from third_party import lib1, lib2, lib3, lib4, lib5, lib5ab" REALLY_LONG_IMPORT = ( "from third_party import lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11," "lib12, lib13, lib14, lib15, lib16, lib17, lib18, lib20, lib21, lib22" ) REALLY_LONG_IMPORT_WITH_COMMENT = ( "from third_party import lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, " "lib10, lib11, lib12, lib13, lib14, lib15, lib16, lib17, lib18, lib20, lib21, lib22" " # comment" ) @pytest.fixture(scope="session", autouse=True) def default_settings_path(tmpdir_factory) -> Iterator[str]: config_dir = tmpdir_factory.mktemp("config") config_file = config_dir.join(".editorconfig").strpath with open(config_file, "w") as editorconfig: editorconfig.write(TEST_DEFAULT_CONFIG) assert Config(config_file).known_other with config_dir.as_cwd(): yield config_dir.strpath def test_happy_path() -> None: """Test the most basic use case, straight imports no code, simply not organized by category.""" test_input = "import sys\nimport os\nimport myproject.test\nimport django.settings" test_output = isort.code(test_input, known_first_party=["myproject"]) assert test_output == ( "import os\nimport sys\n\nimport django.settings\n\nimport myproject.test\n" ) def test_code_intermixed() -> None: """Defines what should happen when isort encounters imports intermixed with code. (it should pull them all to the top) """ test_input = ( "import sys\n" "print('yo')\n" "print('I like to put code between imports cause I want stuff to break')\n" "import myproject.test\n" ) test_output = isort.code(test_input) assert test_output == ( "import sys\n" "\n" "print('yo')\n" "print('I like to put code between imports cause I want stuff to break')\n" "import myproject.test\n" ) def test_correct_space_between_imports() -> None: """Ensure after imports a correct amount of space (in newlines) is enforced. (2 for method, class, or decorator definitions 1 for anything else) """ test_input_method = "import sys\ndef my_method():\n print('hello world')\n" test_output_method = isort.code(test_input_method) assert test_output_method == ("import sys\n\n\ndef my_method():\n print('hello world')\n") test_input_decorator = "import sys\n@my_decorator\ndef my_method():\n print('hello world')\n" test_output_decorator = isort.code(test_input_decorator) assert test_output_decorator == ( "import sys\n\n\n@my_decorator\ndef my_method():\n print('hello world')\n" ) test_input_class = "import sys\nclass MyClass(object):\n pass\n" test_output_class = isort.code(test_input_class) assert test_output_class == "import sys\n\n\nclass MyClass(object):\n pass\n" test_input_other = "import sys\nprint('yo')\n" test_output_other = isort.code(test_input_other) assert test_output_other == "import sys\n\nprint('yo')\n" test_input_inquotes = ( "import sys\n" "@my_decorator('''hello\nworld''')\n" "def my_method():\n" " print('hello world')\n" ) test_output_inquotes = api.sort_code_string(test_input_inquotes) assert ( test_output_inquotes == "import sys\n" "\n\n" "@my_decorator('''hello\nworld''')\n" "def my_method():\n" " print('hello world')\n" ) test_input_assign = "import sys\nVAR = 1\n" test_output_assign = api.sort_code_string(test_input_assign) assert test_output_assign == "import sys\n\nVAR = 1\n" test_input_assign = "import sys\nVAR = 1\ndef y():\n" test_output_assign = api.sort_code_string(test_input_assign) assert test_output_assign == "import sys\n\nVAR = 1\ndef y():\n" test_input = """ import os x = "hi" def x(): pass """ assert isort.code(test_input) == test_input def test_sort_on_number() -> None: """Ensure numbers get sorted logically (10 > 9 not the other way around)""" test_input = "import lib10\nimport lib9\n" test_output = isort.code(test_input) assert test_output == "import lib9\nimport lib10\n" def test_line_length() -> None: """Ensure isort enforces the set line_length.""" assert len(isort.code(REALLY_LONG_IMPORT, line_length=80).split("\n")[0]) <= 80 assert len(isort.code(REALLY_LONG_IMPORT, line_length=120).split("\n")[0]) <= 120 test_output = isort.code(REALLY_LONG_IMPORT, line_length=42) assert test_output == ( "from third_party import (lib1, lib2, lib3,\n" " lib4, lib5, lib6,\n" " lib7, lib8, lib9,\n" " lib10, lib11,\n" " lib12, lib13,\n" " lib14, lib15,\n" " lib16, lib17,\n" " lib18, lib20,\n" " lib21, lib22)\n" ) test_input = ( "from django.contrib.gis.gdal.field import (\n" " OFTDate, OFTDateTime, OFTInteger, OFTInteger64, OFTReal, OFTString,\n" " OFTTime,\n" ")\n" ) # Test case described in issue #654 assert ( isort.code( code=test_input, include_trailing_comma=True, line_length=79, multi_line_output=WrapModes.VERTICAL_GRID_GROUPED, balanced_wrapping=False, ) == test_input ) test_output = isort.code(code=REALLY_LONG_IMPORT, line_length=42, wrap_length=32) assert test_output == ( "from third_party import (lib1,\n" " lib2,\n" " lib3,\n" " lib4,\n" " lib5,\n" " lib6,\n" " lib7,\n" " lib8,\n" " lib9,\n" " lib10,\n" " lib11,\n" " lib12,\n" " lib13,\n" " lib14,\n" " lib15,\n" " lib16,\n" " lib17,\n" " lib18,\n" " lib20,\n" " lib21,\n" " lib22)\n" ) test_input = ( "from .test import a_very_long_function_name_that_exceeds_the_normal_pep8_line_length\n" ) with pytest.raises(ValueError, match="wrap_length must be set lower than or equal to line_le"): test_output = isort.code(code=REALLY_LONG_IMPORT, line_length=80, wrap_length=99) assert ( isort.code(REALLY_LONG_IMPORT, line_length=100, wrap_length=99) == """ from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14, lib15, lib16, lib17, lib18, lib20, lib21, lib22) """.lstrip() ) # Test Case described in issue #1015 test_output = isort.code( REALLY_LONG_IMPORT, line_length=25, multi_line_output=WrapModes.HANGING_INDENT ) assert test_output == ( "from third_party import \\\n" " lib1, lib2, lib3, \\\n" " lib4, lib5, lib6, \\\n" " lib7, lib8, lib9, \\\n" " lib10, lib11, \\\n" " lib12, lib13, \\\n" " lib14, lib15, \\\n" " lib16, lib17, \\\n" " lib18, lib20, \\\n" " lib21, lib22\n" ) def test_output_modes() -> None: """Test setting isort to use various output modes works as expected""" test_output_grid = isort.code( code=REALLY_LONG_IMPORT, multi_line_output=WrapModes.GRID, line_length=40 ) assert test_output_grid == ( "from third_party import (lib1, lib2,\n" " lib3, lib4,\n" " lib5, lib6,\n" " lib7, lib8,\n" " lib9, lib10,\n" " lib11, lib12,\n" " lib13, lib14,\n" " lib15, lib16,\n" " lib17, lib18,\n" " lib20, lib21,\n" " lib22)\n" ) test_output_vertical = isort.code( code=REALLY_LONG_IMPORT, multi_line_output=WrapModes.VERTICAL, line_length=40 ) assert test_output_vertical == ( "from third_party import (lib1,\n" " lib2,\n" " lib3,\n" " lib4,\n" " lib5,\n" " lib6,\n" " lib7,\n" " lib8,\n" " lib9,\n" " lib10,\n" " lib11,\n" " lib12,\n" " lib13,\n" " lib14,\n" " lib15,\n" " lib16,\n" " lib17,\n" " lib18,\n" " lib20,\n" " lib21,\n" " lib22)\n" ) comment_output_vertical = isort.code( code=REALLY_LONG_IMPORT_WITH_COMMENT, multi_line_output=WrapModes.VERTICAL, line_length=40 ) assert comment_output_vertical == ( "from third_party import (lib1, # comment\n" " lib2,\n" " lib3,\n" " lib4,\n" " lib5,\n" " lib6,\n" " lib7,\n" " lib8,\n" " lib9,\n" " lib10,\n" " lib11,\n" " lib12,\n" " lib13,\n" " lib14,\n" " lib15,\n" " lib16,\n" " lib17,\n" " lib18,\n" " lib20,\n" " lib21,\n" " lib22)\n" ) test_output_hanging_indent = isort.code( code=REALLY_LONG_IMPORT, multi_line_output=WrapModes.HANGING_INDENT, line_length=40, indent=" ", ) assert test_output_hanging_indent == ( "from third_party import lib1, lib2, \\\n" " lib3, lib4, lib5, lib6, lib7, \\\n" " lib8, lib9, lib10, lib11, lib12, \\\n" " lib13, lib14, lib15, lib16, lib17, \\\n" " lib18, lib20, lib21, lib22\n" ) comment_output_hanging_indent = isort.code( code=REALLY_LONG_IMPORT_WITH_COMMENT, multi_line_output=WrapModes.HANGING_INDENT, line_length=40, indent=" ", ) assert comment_output_hanging_indent == ( "from third_party import lib1, lib2, \\\n" " lib3, lib4, lib5, lib6, lib7, \\\n" " lib8, lib9, lib10, lib11, lib12, \\\n" " lib13, lib14, lib15, lib16, lib17, \\\n" " lib18, lib20, lib21, lib22 \\\n" " # comment\n" ) test_output_vertical_indent = isort.code( code=REALLY_LONG_IMPORT, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=40, indent=" ", ) assert test_output_vertical_indent == ( "from third_party import (\n" " lib1,\n" " lib2,\n" " lib3,\n" " lib4,\n" " lib5,\n" " lib6,\n" " lib7,\n" " lib8,\n" " lib9,\n" " lib10,\n" " lib11,\n" " lib12,\n" " lib13,\n" " lib14,\n" " lib15,\n" " lib16,\n" " lib17,\n" " lib18,\n" " lib20,\n" " lib21,\n" " lib22\n" ")\n" ) comment_output_vertical_indent = isort.code( code=REALLY_LONG_IMPORT_WITH_COMMENT, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=40, indent=" ", ) assert comment_output_vertical_indent == ( "from third_party import ( # comment\n" " lib1,\n" " lib2,\n" " lib3,\n" " lib4,\n" " lib5,\n" " lib6,\n" " lib7,\n" " lib8,\n" " lib9,\n" " lib10,\n" " lib11,\n" " lib12,\n" " lib13,\n" " lib14,\n" " lib15,\n" " lib16,\n" " lib17,\n" " lib18,\n" " lib20,\n" " lib21,\n" " lib22\n" ")\n" ) test_output_vertical_grid = isort.code( code=REALLY_LONG_IMPORT, multi_line_output=WrapModes.VERTICAL_GRID, line_length=40, indent=" ", ) assert test_output_vertical_grid == ( "from third_party import (\n" " lib1, lib2, lib3, lib4, lib5, lib6,\n" " lib7, lib8, lib9, lib10, lib11,\n" " lib12, lib13, lib14, lib15, lib16,\n" " lib17, lib18, lib20, lib21, lib22)\n" ) comment_output_vertical_grid = isort.code( code=REALLY_LONG_IMPORT_WITH_COMMENT, multi_line_output=WrapModes.VERTICAL_GRID, line_length=40, indent=" ", ) assert comment_output_vertical_grid == ( "from third_party import ( # comment\n" " lib1, lib2, lib3, lib4, lib5, lib6,\n" " lib7, lib8, lib9, lib10, lib11,\n" " lib12, lib13, lib14, lib15, lib16,\n" " lib17, lib18, lib20, lib21, lib22)\n" ) test_output_vertical_grid_grouped = isort.code( code=REALLY_LONG_IMPORT, multi_line_output=WrapModes.VERTICAL_GRID_GROUPED, line_length=40, indent=" ", ) assert test_output_vertical_grid_grouped == ( "from third_party import (\n" " lib1, lib2, lib3, lib4, lib5, lib6,\n" " lib7, lib8, lib9, lib10, lib11,\n" " lib12, lib13, lib14, lib15, lib16,\n" " lib17, lib18, lib20, lib21, lib22\n" ")\n" ) comment_output_vertical_grid_grouped = isort.code( code=REALLY_LONG_IMPORT_WITH_COMMENT, multi_line_output=WrapModes.VERTICAL_GRID_GROUPED, line_length=40, indent=" ", ) assert comment_output_vertical_grid_grouped == ( "from third_party import ( # comment\n" " lib1, lib2, lib3, lib4, lib5, lib6,\n" " lib7, lib8, lib9, lib10, lib11,\n" " lib12, lib13, lib14, lib15, lib16,\n" " lib17, lib18, lib20, lib21, lib22\n" ")\n" ) output_noqa = isort.code(code=REALLY_LONG_IMPORT_WITH_COMMENT, multi_line_output=WrapModes.NOQA) assert output_noqa == ( "from third_party import lib1, lib2, lib3, lib4, lib5, lib6, lib7," " lib8, lib9, lib10, lib11," " lib12, lib13, lib14, lib15, lib16, lib17, lib18, lib20, lib21, lib22 " "# NOQA comment\n" ) test_case = isort.code( code=SINGLE_LINE_LONG_IMPORT, multi_line_output=WrapModes.VERTICAL_GRID_GROUPED, line_length=40, indent=" ", ) test_output_vertical_grid_grouped_doesnt_wrap_early = test_case assert test_output_vertical_grid_grouped_doesnt_wrap_early == ( "from third_party import (\n lib1, lib2, lib3, lib4, lib5, lib5ab\n)\n" ) test_output_prefix_from_module = isort.code( code=REALLY_LONG_IMPORT, multi_line_output=WrapModes.VERTICAL_PREFIX_FROM_MODULE_IMPORT, line_length=40, ) assert test_output_prefix_from_module == ( "from third_party import lib1, lib2\n" "from third_party import lib3, lib4\n" "from third_party import lib5, lib6\n" "from third_party import lib7, lib8\n" "from third_party import lib9, lib10\n" "from third_party import lib11, lib12\n" "from third_party import lib13, lib14\n" "from third_party import lib15, lib16\n" "from third_party import lib17, lib18\n" "from third_party import lib20, lib21\n" "from third_party import lib22\n" ) test_output_prefix_from_module_with_comment = isort.code( code=REALLY_LONG_IMPORT_WITH_COMMENT, multi_line_output=WrapModes.VERTICAL_PREFIX_FROM_MODULE_IMPORT, line_length=40, indent=" ", ) assert test_output_prefix_from_module_with_comment == ( "from third_party import lib1 # comment\n" "from third_party import lib2, lib3\n" "from third_party import lib4, lib5\n" "from third_party import lib6, lib7\n" "from third_party import lib8, lib9\n" "from third_party import lib10, lib11\n" "from third_party import lib12, lib13\n" "from third_party import lib14, lib15\n" "from third_party import lib16, lib17\n" "from third_party import lib18, lib20\n" "from third_party import lib21, lib22\n" ) test_output_hanging_indent_with_parentheses = isort.code( code=REALLY_LONG_IMPORT, multi_line_output=WrapModes.HANGING_INDENT_WITH_PARENTHESES, line_length=40, indent=" ", ) assert test_output_hanging_indent_with_parentheses == ( "from third_party import (lib1, lib2,\n" " lib3, lib4, lib5, lib6, lib7, lib8,\n" " lib9, lib10, lib11, lib12, lib13,\n" " lib14, lib15, lib16, lib17, lib18,\n" " lib20, lib21, lib22)\n" ) comment_output_hanging_indent_with_parentheses = isort.code( code=REALLY_LONG_IMPORT_WITH_COMMENT, multi_line_output=WrapModes.HANGING_INDENT_WITH_PARENTHESES, line_length=40, indent=" ", ) assert comment_output_hanging_indent_with_parentheses == ( "from third_party import (lib1, # comment\n" " lib2, lib3, lib4, lib5, lib6, lib7,\n" " lib8, lib9, lib10, lib11, lib12,\n" " lib13, lib14, lib15, lib16, lib17,\n" " lib18, lib20, lib21, lib22)\n" ) test_input = ( "def a():\n" " from allennlp.modules.text_field_embedders.basic_text_field_embedder" " import BasicTextFieldEmbedder" ) test_output = isort.code(test_input, line_length=100) assert test_output == ( "def a():\n" " from allennlp.modules.text_field_embedders.basic_text_field_embedder import \\\n" " BasicTextFieldEmbedder" ) test_input = ( "class A:\n" " def a():\n" " from allennlp.common.registrable import Registrable" " # import here to avoid circular imports\n" "\n\n" "class B:\n" " def b():\n" " from allennlp.common.registrable import Registrable" " # import here to avoid circular imports\n" ) test_output = isort.code(test_input, line_length=100) assert test_output == test_input def test_qa_comment_case() -> None: test_input = "from veryveryveryveryveryveryveryveryveryveryvery import X # NOQA" test_output = isort.code(code=test_input, line_length=40, multi_line_output=WrapModes.NOQA) assert test_output == "from veryveryveryveryveryveryveryveryveryveryvery import X # NOQA\n" test_input = "import veryveryveryveryveryveryveryveryveryveryvery # NOQA" test_output = isort.code(code=test_input, line_length=40, multi_line_output=WrapModes.NOQA) assert test_output == "import veryveryveryveryveryveryveryveryveryveryvery # NOQA\n" def test_length_sort() -> None: """Test setting isort to sort on length instead of alphabetically.""" test_input = ( "import medium_sizeeeeeeeeeeeeee\n" "import shortie\n" "import looooooooooooooooooooooooooooooooooooooong\n" "import medium_sizeeeeeeeeeeeeea\n" ) test_output = isort.code(test_input, length_sort=True) assert test_output == ( "import shortie\n" "import medium_sizeeeeeeeeeeeeea\n" "import medium_sizeeeeeeeeeeeeee\n" "import looooooooooooooooooooooooooooooooooooooong\n" ) def test_length_sort_straight() -> None: """Test setting isort to sort straight imports on length instead of alphabetically.""" test_input = ( "import medium_sizeeeeeeeeeeeeee\n" "import shortie\n" "import looooooooooooooooooooooooooooooooooooooong\n" "from medium_sizeeeeeeeeeeeeee import b\n" "from shortie import c\n" "from looooooooooooooooooooooooooooooooooooooong import a\n" ) test_output = isort.code(test_input, length_sort_straight=True) assert test_output == ( "import shortie\n" "import medium_sizeeeeeeeeeeeeee\n" "import looooooooooooooooooooooooooooooooooooooong\n" "from looooooooooooooooooooooooooooooooooooooong import a\n" "from medium_sizeeeeeeeeeeeeee import b\n" "from shortie import c\n" ) def test_length_sort_section() -> None: """Test setting isort to sort on length instead of alphabetically for a specific section.""" test_input = ( "import medium_sizeeeeeeeeeeeeee\n" "import shortie\n" "import datetime\n" "import sys\n" "import os\n" "import looooooooooooooooooooooooooooooooooooooong\n" "import medium_sizeeeeeeeeeeeeea\n" ) test_output = isort.code(test_input, length_sort_sections=("stdlib",)) assert test_output == ( "import os\n" "import sys\n" "import datetime\n" "\n" "import looooooooooooooooooooooooooooooooooooooong\n" "import medium_sizeeeeeeeeeeeeea\n" "import medium_sizeeeeeeeeeeeeee\n" "import shortie\n" ) def test_convert_hanging() -> None: """Ensure that isort will convert hanging indents to correct indent method.""" test_input = ( "from third_party import lib1, lib2, \\\n" " lib3, lib4, lib5, lib6, lib7, \\\n" " lib8, lib9, lib10, lib11, lib12, \\\n" " lib13, lib14, lib15, lib16, lib17, \\\n" " lib18, lib20, lib21, lib22\n" ) test_output = isort.code(code=test_input, multi_line_output=WrapModes.GRID, line_length=40) assert test_output == ( "from third_party import (lib1, lib2,\n" " lib3, lib4,\n" " lib5, lib6,\n" " lib7, lib8,\n" " lib9, lib10,\n" " lib11, lib12,\n" " lib13, lib14,\n" " lib15, lib16,\n" " lib17, lib18,\n" " lib20, lib21,\n" " lib22)\n" ) def test_custom_indent() -> None: """Ensure setting a custom indent will work as expected.""" test_output = isort.code( code=REALLY_LONG_IMPORT, multi_line_output=WrapModes.HANGING_INDENT, line_length=40, indent=" ", balanced_wrapping=False, ) assert test_output == ( "from third_party import lib1, lib2, \\\n" " lib3, lib4, lib5, lib6, lib7, lib8, \\\n" " lib9, lib10, lib11, lib12, lib13, \\\n" " lib14, lib15, lib16, lib17, lib18, \\\n" " lib20, lib21, lib22\n" ) test_output = isort.code( code=REALLY_LONG_IMPORT, multi_line_output=WrapModes.HANGING_INDENT, line_length=40, indent="' '", balanced_wrapping=False, ) assert test_output == ( "from third_party import lib1, lib2, \\\n" " lib3, lib4, lib5, lib6, lib7, lib8, \\\n" " lib9, lib10, lib11, lib12, lib13, \\\n" " lib14, lib15, lib16, lib17, lib18, \\\n" " lib20, lib21, lib22\n" ) test_output = isort.code( code=REALLY_LONG_IMPORT, multi_line_output=WrapModes.HANGING_INDENT, line_length=40, indent="tab", balanced_wrapping=False, ) assert test_output == ( "from third_party import lib1, lib2, \\\n" "\tlib3, lib4, lib5, lib6, lib7, lib8, \\\n" "\tlib9, lib10, lib11, lib12, lib13, \\\n" "\tlib14, lib15, lib16, lib17, lib18, \\\n" "\tlib20, lib21, lib22\n" ) test_output = isort.code( code=REALLY_LONG_IMPORT, multi_line_output=WrapModes.HANGING_INDENT, line_length=40, indent=2, balanced_wrapping=False, ) assert test_output == ( "from third_party import lib1, lib2, \\\n" " lib3, lib4, lib5, lib6, lib7, lib8, \\\n" " lib9, lib10, lib11, lib12, lib13, \\\n" " lib14, lib15, lib16, lib17, lib18, \\\n" " lib20, lib21, lib22\n" ) def test_use_parentheses() -> None: test_input = ( "from fooooooooooooooooooooooooo.baaaaaaaaaaaaaaaaaaarrrrrrr import " " my_custom_function as my_special_function" ) test_output = isort.code(test_input, line_length=79, use_parentheses=True) assert test_output == ( "from fooooooooooooooooooooooooo.baaaaaaaaaaaaaaaaaaarrrrrrr import (\n" " my_custom_function as my_special_function)\n" ) test_output = isort.code( code=test_input, line_length=79, use_parentheses=True, include_trailing_comma=True ) assert test_output == ( "from fooooooooooooooooooooooooo.baaaaaaaaaaaaaaaaaaarrrrrrr import (\n" " my_custom_function as my_special_function,)\n" ) test_output = isort.code( code=test_input, line_length=79, use_parentheses=True, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, ) assert test_output == ( "from fooooooooooooooooooooooooo.baaaaaaaaaaaaaaaaaaarrrrrrr import (\n" " my_custom_function as my_special_function\n)\n" ) test_output = isort.code( code=test_input, line_length=79, use_parentheses=True, multi_line_output=WrapModes.VERTICAL_GRID_GROUPED, include_trailing_comma=True, ) assert test_output == ( "from fooooooooooooooooooooooooo.baaaaaaaaaaaaaaaaaaarrrrrrr import (\n" " my_custom_function as my_special_function,\n)\n" ) def test_skip() -> None: """Ensure skipping a single import will work as expected.""" test_input = ( "import myproject\n" "import django\n" "print('hey')\n" "import sys # isort: skip this import needs to be placed here\n\n\n\n\n\n\n" ) test_output = isort.code(test_input, known_first_party=["myproject"]) assert test_output == ( "import django\n" "\n" "import myproject\n" "\n" "print('hey')\n" "import sys # isort: skip this import needs to be placed here\n" ) def test_skip_with_file_name() -> None: """Ensure skipping a file works even when file_contents is provided.""" test_input = "import django\nimport myproject\n" with pytest.raises(FileSkipped): isort.code( file_path=Path("/baz.py"), code=test_input, settings_path=os.getcwd(), skip=["baz.py"] ) def test_skip_within_file() -> None: """Ensure skipping a whole file works.""" test_input = "# isort: skip_file\nimport django\nimport myproject\n" with pytest.raises(FileSkipped): isort.code(test_input, known_third_party=["django"]) def test_force_to_top() -> None: """Ensure forcing a single import to the top of its category works as expected.""" test_input = "import lib6\nimport lib2\nimport lib5\nimport lib1\n" test_output = isort.code(test_input, force_to_top=["lib5"]) assert test_output == "import lib5\nimport lib1\nimport lib2\nimport lib6\n" def test_add_imports() -> None: """Ensures adding imports works as expected.""" test_input = "import lib6\nimport lib2\nimport lib5\nimport lib1\n\n" test_output = isort.code(code=test_input, add_imports=["import lib4", "import lib7"]) assert test_output == ( "import lib1\nimport lib2\nimport lib4\nimport lib5\nimport lib6\nimport lib7\n" ) # Using simplified syntax test_input = "import lib6\nimport lib2\nimport lib5\nimport lib1\n\n" test_output = isort.code(code=test_input, add_imports=["lib4", "lib7", "lib8.a"]) assert test_output == ( "import lib1\n" "import lib2\n" "import lib4\n" "import lib5\n" "import lib6\n" "import lib7\n" "from lib8 import a\n" ) # On a file that has no pre-existing imports test_input = '"""Module docstring"""\nclass MyClass(object):\n pass\n' test_output = isort.code(code=test_input, add_imports=["from __future__ import print_function"]) assert test_output == ( '"""Module docstring"""\n' "from __future__ import print_function\n" "\n" "\n" "class MyClass(object):\n" " pass\n" ) # On a file that has no pre-existing imports and a multiline docstring test_input = ( '"""Module docstring\n\nWith a second line\n"""\nclass MyClass(object):\n pass\n' ) test_output = isort.code(code=test_input, add_imports=["from __future__ import print_function"]) assert test_output == ( '"""Module docstring\n' "\n" "With a second line\n" '"""\n' "from __future__ import print_function\n" "\n" "\n" "class MyClass(object):\n" " pass\n" ) # On a file that has no pre-existing imports and a multiline docstring. # In this example, the closing quotes for the docstring are on the final # line rather than a separate one. test_input = '"""Module docstring\n\nWith a second line"""\nclass MyClass(object):\n pass\n' test_output = isort.code(code=test_input, add_imports=["from __future__ import print_function"]) assert test_output == ( '"""Module docstring\n' "\n" 'With a second line"""\n' "from __future__ import print_function\n" "\n" "\n" "class MyClass(object):\n" " pass\n" ) # On a file that has no pre-existing imports, and no doc-string test_input = "class MyClass(object):\n pass\n" test_output = isort.code(code=test_input, add_imports=["from __future__ import print_function"]) assert test_output == ( "from __future__ import print_function\n\n\nclass MyClass(object):\n pass\n" ) # On a file with no content what so ever test_input = "" test_output = isort.code(test_input, add_imports=["lib4"]) assert test_output == ("") # On a file with no content what so ever, after force_adds is set to True test_input = "" test_output = isort.code(code=test_input, add_imports=["lib4"], force_adds=True) assert test_output == ("import lib4\n") def test_remove_imports() -> None: """Ensures removing imports works as expected.""" test_input = "import lib6\nimport lib2\nimport lib5\nimport lib1" test_output = isort.code(test_input, remove_imports=["lib2", "lib6"]) assert test_output == "import lib1\nimport lib5\n" # Using natural syntax test_input = "import lib6\nimport lib2\nimport lib5\nimport lib1\nfrom lib8 import a" test_output = isort.code( code=test_input, remove_imports=["import lib2", "import lib6", "from lib8 import a"] ) assert test_output == "import lib1\nimport lib5\n" # From imports test_input = "from x import y" test_output = isort.code(test_input, remove_imports=["x"]) assert test_output == "" test_input = "from x import y" test_output = isort.code(test_input, remove_imports=["x.y"]) assert test_output == "" def test_comments_above(): """Test to ensure comments above an import will stay in place""" test_input = "import os\n\nfrom x import y\n\n# comment\nfrom z import __version__, api\n" assert isort.code(test_input, ensure_newline_before_comments=True) == test_input def test_explicitly_local_import() -> None: """Ensure that explicitly local imports are separated.""" test_input = "import lib1\nimport lib2\nimport .lib6\nfrom . import lib7" assert isort.code(test_input) == ( "import lib1\nimport lib2\n\nimport .lib6\nfrom . import lib7\n" ) def test_quotes_in_file() -> None: """Ensure imports within triple quotes don't get imported.""" test_input = 'import os\n\n"""\nLet us\nimport foo\nokay?\n"""\n' assert isort.code(test_input) == test_input test_input = 'import os\n\n\'"""\'\nimport foo\n' assert isort.code(test_input) == test_input test_input = 'import os\n\n"""Let us"""\nimport foo\n\n"""okay?"""\n' assert isort.code(test_input) == test_input test_input = 'import os\n\n#"""\nimport foo\n#"""' assert isort.code(test_input) == ('import os\n\nimport foo\n\n#"""\n#"""\n') test_input = "import os\n\n'\\\nimport foo'\n" assert isort.code(test_input) == test_input test_input = "import os\n\n'''\n\\'''\nimport junk\n'''\n" assert isort.code(test_input) == test_input def test_check_newline_in_imports(capsys) -> None: """Ensure tests works correctly when new lines are in imports.""" test_input = "from lib1 import (\n sub1,\n sub2,\n sub3\n)\n" assert api.check_code_string( code=test_input, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=20, verbose=True, ) out, _ = capsys.readouterr() assert "SUCCESS" in out # if the verbose is only on modified outputs no output will be given assert api.check_code_string( code=test_input, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=20, verbose=True, only_modified=True, ) out, _ = capsys.readouterr() assert not out # we can make the input invalid to again see output test_input = "from lib1 import (\n sub2,\n sub1,\n sub3\n)\n" assert not api.check_code_string( code=test_input, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=20, verbose=True, only_modified=True, ) out, _ = capsys.readouterr() assert out def test_forced_separate() -> None: """Ensure that forcing certain sub modules to show separately works as expected.""" test_input = ( "import sys\n" "import warnings\n" "from collections import OrderedDict\n" "\n" "from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation\n" "from django.core.paginator import InvalidPage\n" "from django.core.urlresolvers import reverse\n" "from django.db import models\n" "from django.db.models.fields import FieldDoesNotExist\n" "from django.utils import six\n" "\n" "from django.utils.deprecation import RenameMethodsBase\n" "from django.utils.encoding import force_str, force_text\n" "from django.utils.http import urlencode\n" "from django.utils.translation import ugettext, ugettext_lazy\n" "\n" "from django.contrib.admin import FieldListFilter\n" "from django.contrib.admin.exceptions import DisallowedModelAdminLookup\n" "from django.contrib.admin.options import IncorrectLookupParameters, IS_POPUP_VAR, " "TO_FIELD_VAR\n" ) assert ( isort.code( code=test_input, forced_separate=["django.utils.*", "django.contrib"], known_third_party=["django"], line_length=120, order_by_type=False, ) == test_input ) assert ( isort.code( code=test_input, forced_separate=["django.utils.*", "django.contrib"], known_third_party=["django"], line_length=120, order_by_type=False, ) == test_input ) test_input = "from .foo import bar\n\nfrom .y import ca\n" assert ( isort.code(code=test_input, forced_separate=[".y"], line_length=120, order_by_type=False) == test_input ) def test_default_section() -> None: """Test to ensure changing the default section works as expected.""" test_input = "import sys\nimport os\nimport myproject.test\nimport django.settings" test_output = isort.code( code=test_input, known_third_party=["django"], default_section="FIRSTPARTY" ) assert test_output == ( "import os\nimport sys\n\nimport django.settings\n\nimport myproject.test\n" ) test_output_custom = isort.code( code=test_input, known_third_party=["django"], default_section="STDLIB" ) assert test_output_custom == ( "import myproject.test\nimport os\nimport sys\n\nimport django.settings\n" ) def test_first_party_overrides_standard_section() -> None: """Test to ensure changing the default section works as expected.""" test_input = ( "from HTMLParser import HTMLParseError, HTMLParser\n" "import sys\n" "import os\n" "import profile.test\n" ) test_output = isort.code(code=test_input, known_first_party=["profile"], py_version="27") assert test_output == ( "import os\n" "import sys\n" "from HTMLParser import HTMLParseError, HTMLParser\n" "\n" "import profile.test\n" ) def test_thirdy_party_overrides_standard_section() -> None: """Test to ensure changing the default section works as expected.""" test_input = "import sys\nimport os\nimport profile.test\n" test_output = isort.code(test_input, known_third_party=["profile"]) assert test_output == "import os\nimport sys\n\nimport profile.test\n" def test_known_pattern_path_expansion(tmpdir) -> None: """Test to ensure patterns ending with path sep gets expanded and nested packages treated as known patterns. """ src_dir = tmpdir.mkdir("src") src_dir.mkdir("foo") src_dir.mkdir("bar") test_input = ( "from kate_plugin import isort_plugin\n" "import sys\n" "from foo import settings\n" "import bar\n" "import this\n" "import os\n" ) test_output = isort.code( code=test_input, default_section="THIRDPARTY", known_first_party=["src/", "this", "kate_plugin"], directory=str(tmpdir), ) assert test_output == ( "import os\n" "import sys\n" "\n" "import bar\n" "import this\n" "from foo import settings\n" "from kate_plugin import isort_plugin\n" ) def test_force_single_line_imports() -> None: """Test to ensure forcing imports to each have their own line works as expected.""" test_input = ( "from third_party import lib1, lib2, \\\n" " lib3, lib4, lib5, lib6, lib7, \\\n" " lib8, lib9, lib10, lib11, lib12, \\\n" " lib13, lib14, lib15, lib16, lib17, \\\n" " lib18, lib20, lib21, lib22\n" ) test_output = isort.code( code=test_input, multi_line_output=WrapModes.GRID, line_length=40, force_single_line=True ) assert test_output == ( "from third_party import lib1\n" "from third_party import lib2\n" "from third_party import lib3\n" "from third_party import lib4\n" "from third_party import lib5\n" "from third_party import lib6\n" "from third_party import lib7\n" "from third_party import lib8\n" "from third_party import lib9\n" "from third_party import lib10\n" "from third_party import lib11\n" "from third_party import lib12\n" "from third_party import lib13\n" "from third_party import lib14\n" "from third_party import lib15\n" "from third_party import lib16\n" "from third_party import lib17\n" "from third_party import lib18\n" "from third_party import lib20\n" "from third_party import lib21\n" "from third_party import lib22\n" ) test_input = "from third_party import lib_a, lib_b, lib_d\nfrom third_party.lib_c import lib1\n" test_output = isort.code( code=test_input, multi_line_output=WrapModes.GRID, line_length=40, force_single_line=True ) assert test_output == ( "from third_party import lib_a\n" "from third_party import lib_b\n" "from third_party import lib_d\n" "from third_party.lib_c import lib1\n" ) def test_force_single_line_long_imports() -> None: test_input = "from veryveryveryveryveryvery import small, big\n" test_output = isort.code( code=test_input, multi_line_output=WrapModes.NOQA, line_length=40, force_single_line=True ) assert test_output == ( "from veryveryveryveryveryvery import big\n" "from veryveryveryveryveryvery import small # NOQA\n" ) def test_force_single_line_imports_and_sort_within_sections() -> None: test_input = "from third_party import lib_a, lib_b, lib_d\nfrom third_party.lib_c import lib1\n" test_output = isort.code( code=test_input, multi_line_output=WrapModes.GRID, line_length=40, force_single_line=True, force_sort_within_sections=True, ) assert test_output == ( "from third_party import lib_a\n" "from third_party import lib_b\n" "from third_party import lib_d\n" "from third_party.lib_c import lib1\n" ) test_output = isort.code( code=test_input, multi_line_output=WrapModes.GRID, line_length=40, force_single_line=True, force_sort_within_sections=True, lexicographical=True, ) assert test_output == ( "from third_party import lib_a\n" "from third_party import lib_b\n" "from third_party.lib_c import lib1\n" "from third_party import lib_d\n" ) test_input = """import sympy import numpy as np import pandas as pd from matplotlib import pyplot as plt """ assert ( isort.code(code=test_input, force_sort_within_sections=True, length_sort=True) == test_input ) def test_titled_imports() -> None: """Tests setting custom titled/commented import sections.""" test_input = ( "import sys\n" "import unicodedata\n" "import statistics\n" "import os\n" "import myproject.test\n" "import django.settings" ) test_output = isort.code( code=test_input, known_first_party=["myproject"], import_heading_stdlib="Standard Library", import_heading_firstparty="My Stuff", ) assert test_output == ( "# Standard Library\n" "import os\n" "import statistics\n" "import sys\n" "import unicodedata\n" "\n" "import django.settings\n" "\n" "# My Stuff\n" "import myproject.test\n" ) test_second_run = isort.code( code=test_output, known_first_party=["myproject"], import_heading_stdlib="Standard Library", import_heading_firstparty="My Stuff", ) assert test_second_run == test_output test_input_lines_down = ( "# comment 1\n" "import django.settings\n" "\n" "# Standard Library\n" "import sys\n" "import unicodedata\n" "import statistics\n" "import os\n" "import myproject.test\n" ) test_output_lines_down = isort.code( code=test_input_lines_down, known_first_party=["myproject"], import_heading_stdlib="Standard Library", import_heading_firstparty="My Stuff", ) assert test_output_lines_down == ( "# comment 1\n" "# Standard Library\n" "import os\n" "import statistics\n" "import sys\n" "import unicodedata\n" "\n" "import django.settings\n" "\n" "# My Stuff\n" "import myproject.test\n" ) def test_footered_imports() -> None: """Tests setting both custom titles and footers to import sections.""" test_input = ( "import sys\n" "import unicodedata\n" "import statistics\n" "import os\n" "import myproject.test\n" "import django.settings" ) test_output = isort.code( code=test_input, known_first_party=["myproject"], import_footer_stdlib="Standard Library End", import_footer_firstparty="My Stuff End", ) assert test_output == ( "import os\n" "import statistics\n" "import sys\n" "import unicodedata\n" "\n" "# Standard Library End\n" "\n" "import django.settings\n" "\n" "import myproject.test\n" "\n" "# My Stuff End\n" ) test_second_run = isort.code( code=test_output, known_first_party=["myproject"], import_footer_stdlib="Standard Library End", import_footer_firstparty="My Stuff End", ) assert test_second_run == test_output test_input_lines_down = ( "# comment 1\n" "import django.settings\n" "\n" "import sys\n" "import unicodedata\n" "import statistics\n" "import os\n" "import myproject.test\n" "\n" "# Standard Library End\n" ) test_output_lines_down = isort.code( code=test_input_lines_down, known_first_party=["myproject"], import_footer_stdlib="Standard Library End", import_footer_firstparty="My Stuff End", ) assert test_output_lines_down == ( "# comment 1\n" "import os\n" "import statistics\n" "import sys\n" "import unicodedata\n" "\n" "# Standard Library End\n" "\n" "import django.settings\n" "\n" "import myproject.test\n" "\n" "# My Stuff End\n" ) def test_titled_and_footered_imports() -> None: """Tests setting custom footers to import sections.""" test_input = ( "import sys\n" "import unicodedata\n" "import statistics\n" "import os\n" "import myproject.test\n" "import django.settings" ) test_output = isort.code( code=test_input, known_first_party=["myproject"], import_heading_stdlib="Standard Library", import_heading_firstparty="My Stuff", import_footer_stdlib="Standard Library End", import_footer_firstparty="My Stuff End", ) assert test_output == ( "# Standard Library\n" "import os\n" "import statistics\n" "import sys\n" "import unicodedata\n" "\n" "# Standard Library End\n" "\n" "import django.settings\n" "\n" "# My Stuff\n" "import myproject.test\n" "\n" "# My Stuff End\n" ) test_second_run = isort.code( code=test_output, known_first_party=["myproject"], import_heading_stdlib="Standard Library", import_heading_firstparty="My Stuff", import_footer_stdlib="Standard Library End", import_footer_firstparty="My Stuff End", ) assert test_second_run == test_output test_input_lines_down = ( "# comment 1\n" "import django.settings\n" "\n" "# Standard Library\n" "import sys\n" "import unicodedata\n" "import statistics\n" "import os\n" "import myproject.test\n" "\n" "# Standard Library End\n" ) test_output_lines_down = isort.code( code=test_input_lines_down, known_first_party=["myproject"], import_heading_stdlib="Standard Library", import_heading_firstparty="My Stuff", import_footer_stdlib="Standard Library End", import_footer_firstparty="My Stuff End", ) assert test_output_lines_down == ( "# comment 1\n" "# Standard Library\n" "import os\n" "import statistics\n" "import sys\n" "import unicodedata\n" "\n" "# Standard Library End\n" "\n" "import django.settings\n" "\n" "# My Stuff\n" "import myproject.test\n" "\n" "# My Stuff End\n" ) test_input_lines_down = ( "# comment 1\n" "import django.settings\n" "\n" "# Standard Library\n" "import sys\n" "import unicodedata\n" "import statistics\n" "import os\n" "import myproject.test\n" "\n" "# Standard Library End\n" "# Standard Library End\n" ) test_output_lines_down = isort.code( code=test_input_lines_down, known_first_party=["myproject"], import_heading_stdlib="Standard Library", import_heading_firstparty="My Stuff", import_footer_stdlib="Standard Library End", import_footer_firstparty="My Stuff End", dedup_headings=True, ) assert test_output_lines_down == ( "# comment 1\n" "# Standard Library\n" "import os\n" "import statistics\n" "import sys\n" "import unicodedata\n" "\n" "# Standard Library End\n" "\n" "import django.settings\n" "\n" "# My Stuff\n" "import myproject.test\n" "\n" "# My Stuff End\n" ) test_input_lines_down = ( "# comment 1\n" "# Standard Library\n" "import os\n" "import statistics\n" "import sys\n" "import unicodedata\n" "\n" "# Standard Library End\n" "\n" "import django.settings\n" "\n" "# My Stuff\n" "import myproject.test\n" ) test_output_lines_down = isort.code( code=test_input_lines_down, known_first_party=["myproject"], import_heading_stdlib="Standard Library", import_heading_firstparty="My Stuff", import_footer_stdlib="Standard Library End", import_footer_firstparty="My Stuff End", dedup_headings=True, ) assert test_output_lines_down == ( "# comment 1\n" "# Standard Library\n" "import os\n" "import statistics\n" "import sys\n" "import unicodedata\n" "\n" "# Standard Library End\n" "\n" "import django.settings\n" "\n" "# My Stuff\n" "import myproject.test\n" "\n" "# My Stuff End\n" ) def test_balanced_wrapping() -> None: """Tests balanced wrapping mode, where the length of individual lines maintain width.""" test_input = ( "from __future__ import (absolute_import, division, print_function,\n" " unicode_literals)" ) test_output = isort.code(code=test_input, line_length=70, balanced_wrapping=True) assert test_output == ( "from __future__ import (absolute_import, division,\n" " print_function, unicode_literals)\n" ) def test_relative_import_with_space() -> None: """Tests the case where the relation and the module that is being imported from is separated with a space. """ test_input = "from ... fields.sproqet import SproqetCollection" assert isort.code(test_input) == ("from ...fields.sproqet import SproqetCollection\n") test_input = "from .import foo" test_output = "from . import foo\n" assert isort.code(test_input) == test_output test_input = "from.import foo" test_output = "from . import foo\n" assert isort.code(test_input) == test_output def test_multiline_import() -> None: """Test the case where import spawns multiple lines with inconsistent indentation.""" test_input = "from pkg \\\n import stuff, other_suff \\\n more_stuff" assert isort.code(test_input) == ("from pkg import more_stuff, other_suff, stuff\n") # test again with a custom configuration custom_configuration: dict[str, Any] = { "force_single_line": True, "line_length": 120, "known_first_party": ["asdf", "qwer"], "default_section": "THIRDPARTY", "forced_separate": "asdf", } expected_output = ( "from pkg import more_stuff\nfrom pkg import other_suff\nfrom pkg import stuff\n" ) assert isort.code(test_input, **custom_configuration) == expected_output def test_single_multiline() -> None: """Test the case where a single import spawns multiple lines.""" test_input = "from os import\\\n getuid\n\nprint getuid()\n" output = isort.code(test_input) assert output == ("from os import getuid\n\nprint getuid()\n") def test_atomic_mode() -> None: """With atomic mode isort should be able to automatically detect and stop syntax errors""" # without syntax error, everything works OK test_input = "from b import d, c\nfrom a import f, e\n" assert isort.code(test_input, atomic=True) == ("from a import e, f\nfrom b import c, d\n") # with syntax error content is not changed test_input += "while True print 'Hello world'" # blatant syntax error with pytest.raises(ExistingSyntaxErrors): isort.code(test_input, atomic=True) # unless file is for Cython which doesn't yet provide a public AST parsing API assert ( isort.code(test_input, extension="pyx", atomic=True, verbose=True) == isort.code(test_input, extension="pyx", atomic=True) == """from a import e, f from b import c, d while True print 'Hello world' """ ) # ensure atomic works with streams test_stream_input = as_stream("from b import d, c\nfrom a import f, e\n") test_output = UnreadableStream() isort.stream(test_stream_input, test_output, atomic=True) test_output.seek(0) assert test_output.read() == "from a import e, f\nfrom b import c, d\n" def test_order_by_type() -> None: test_input = "from module import Class, CONSTANT, function" assert isort.code(test_input, order_by_type=True) == ( "from module import CONSTANT, Class, function\n" ) # More complex sample data test_input = "from module import Class, CONSTANT, function, BASIC, Apple" assert isort.code(test_input, order_by_type=True) == ( "from module import BASIC, CONSTANT, Apple, Class, function\n" ) # Really complex sample data, to verify we don't mess with top level imports, only nested ones test_input = ( "import StringIO\n" "import glob\n" "import os\n" "import shutil\n" "import tempfile\n" "import time\n" "from subprocess import PIPE, Popen, STDOUT\n" ) assert isort.code(test_input, order_by_type=True, py_version="27") == ( "import glob\n" "import os\n" "import shutil\n" "import StringIO\n" "import tempfile\n" "import time\n" "from subprocess import PIPE, STDOUT, Popen\n" ) @pytest.mark.parametrize("has_body", [True, False]) def test_custom_lines_before_import_section(has_body: bool) -> None: """Test the case where the number of lines to output before imports has been explicitly set.""" test_input = "from a import b\n" if has_body: test_input += "\nfoo = 'bar'\n" ln = "\n" # default case is no line added before the import assert isort.code(test_input) == (test_input) # test again with a custom number of lines before the import section assert isort.code(test_input, lines_before_imports=2) == 2 * ln + test_input comment = "# Comment\n" # test with a comment above assert isort.code(comment + ln + test_input, lines_before_imports=0) == comment + test_input # test with comments with empty lines assert ( isort.code(comment + ln + comment + 3 * ln + test_input, lines_before_imports=1) == comment + ln + comment + 1 * ln + test_input ) def test_check_code_detects_lines_before_import_changes() -> None: """check_code must return False when lines_before_imports requires adding or removing blank lines before the import section, even if the imports themselves are already sorted. Regression test for https://github.com/PyCQA/isort/issues/2242 """ code = """from a import b, x foo = 'bar' """ # Adding a blank line before imports (0 present, 1 required) assert isort.code(code, lines_before_imports=1) == "\n" + code assert not isort.check_code(code, lines_before_imports=1) # Removing a blank line before imports (1 present, 0 required) assert isort.code("\n" + code, lines_before_imports=0) == code assert not isort.check_code("\n" + code, lines_before_imports=0) # Already correct: no change needed assert isort.check_code(code, lines_before_imports=0) assert isort.check_code("\n" + code, lines_before_imports=1) def test_custom_lines_after_import_section() -> None: """Test the case where the number of lines to output after imports has been explicitly set.""" test_input = "from a import b\nfoo = 'bar'\n" # default case is one space if not method or class after imports assert isort.code(test_input) == ("from a import b\n\nfoo = 'bar'\n") # test again with a custom number of lines after the import section assert isort.code(test_input, lines_after_imports=2) == ("from a import b\n\n\nfoo = 'bar'\n") def test_smart_lines_after_import_section() -> None: """Tests the default 'smart' behavior for dealing with lines after the import section""" # one space if not method or class after imports test_input = "from a import b\nfoo = 'bar'\n" assert isort.code(test_input) == ("from a import b\n\nfoo = 'bar'\n") # two spaces if a method or class after imports test_input = "from a import b\ndef my_function():\n pass\n" assert isort.code(test_input) == ("from a import b\n\n\ndef my_function():\n pass\n") # two spaces if an async method after imports test_input = "from a import b\nasync def my_function():\n pass\n" assert isort.code(test_input) == ("from a import b\n\n\nasync def my_function():\n pass\n") # two spaces if a method or class after imports - even if comment before function test_input = "from a import b\n# comment should be ignored\ndef my_function():\n pass\n" assert isort.code(test_input) == ( "from a import b\n\n\n# comment should be ignored\ndef my_function():\n pass\n" ) # the same logic does not apply to doc strings test_input = ( 'from a import b\n"""\n comment should be ignored\n"""\ndef my_function():\n pass\n' ) assert isort.code(test_input) == ( 'from a import b\n\n"""\n comment should be ignored\n"""\ndef my_function():\n pass\n' ) # Ensure logic doesn't incorrectly skip over assignments to multi-line strings test_input = 'from a import b\nX = """test\n"""\ndef my_function():\n pass\n' assert isort.code(test_input) == ( 'from a import b\n\nX = """test\n"""\ndef my_function():\n pass\n' ) def test_settings_overwrite() -> None: """Test to ensure settings overwrite instead of trying to combine.""" assert Config(known_standard_library=["not_std_library"]).known_standard_library == frozenset( {"not_std_library"} ) assert Config(known_first_party=["thread"]).known_first_party == frozenset({"thread"}) def test_combined_from_and_as_imports() -> None: """Test to ensure it's possible to combine from and as imports.""" test_input = ( "from translate.misc.multistring import multistring\n" "from translate.storage import base, factory\n" "from translate.storage.placeables import general, parse as rich_parse\n" ) assert isort.code(test_input, combine_as_imports=True) == test_input assert isort.code(test_input, combine_as_imports=True, only_sections=True) == test_input test_input = "import os \nimport os as _os" test_output = "import os\nimport os as _os\n" assert isort.code(test_input) == test_output def test_as_imports_with_line_length() -> None: """Test to ensure it's possible to combine from and as imports.""" test_input = ( "from translate.storage import base as storage_base\n" "from translate.storage.placeables import general, parse as rich_parse\n" ) assert isort.code(code=test_input, combine_as_imports=False, line_length=40) == ( "from translate.storage import \\\n base as storage_base\n" "from translate.storage.placeables import \\\n general\n" "from translate.storage.placeables import \\\n parse as rich_parse\n" ) def test_keep_comments() -> None: """Test to ensure isort properly keeps comments in tact after sorting.""" # Straight Import test_input = "import foo # bar\n" assert isort.code(test_input) == test_input # Star import test_input_star = "from foo import * # bar\n" assert isort.code(test_input_star) == test_input_star # Force Single Line From Import test_input = "from foo import bar # comment\n" assert isort.code(test_input, force_single_line=True) == test_input # From import test_input = "from foo import bar # My Comment\n" assert isort.code(test_input) == test_input # More complicated case test_input = "from a import b # My Comment1\nfrom a import c # My Comment2\n" assert isort.code(test_input) == ( "from a import b # My Comment1\nfrom a import c # My Comment2\n" ) # Test case where imports comments make imports extend pass the line length test_input = "from a import b # My Comment1\nfrom a import c # My Comment2\nfrom a import d\n" assert isort.code(test_input, line_length=45) == ( "from a import b # My Comment1\nfrom a import c # My Comment2\nfrom a import d\n" ) # Test case where imports with comments will be beyond line length limit test_input = ( "from a import b, c # My Comment1\n" "from a import c, d # My Comment2 is really really really really long\n" ) assert isort.code(test_input, line_length=45) == ( "from a import ( # My Comment1; My Comment2 is really really really really long\n" " b, c, d)\n" ) # Test that comments are not stripped from 'import ... as ...' by default test_input = "from a import b as bb # b comment\nfrom a import c as cc # c comment\n" assert isort.code(test_input) == test_input # Test that 'import ... as ...' comments are not collected inappropriately test_input = ( "from a import b as bb # b comment\nfrom a import c as cc # c comment\nfrom a import d\n" ) assert isort.code(test_input) == test_input assert isort.code(test_input, combine_as_imports=True) == ( "from a import b as bb, c as cc, d # b comment; c comment\n" ) def test_multiline_split_on_dot() -> None: """Test to ensure isort correctly handles multiline imports, even when split right after a '.' """ test_input = ( "from my_lib.my_package.test.level_1.level_2.level_3.level_4.level_5.\\\n" " my_module import my_function" ) assert isort.code(test_input, line_length=70) == ( "from my_lib.my_package.test.level_1.level_2.level_3.level_4.level_5.my_module import \\\n" " my_function\n" ) def test_import_star() -> None: """Test to ensure isort handles star imports correctly""" test_input = "from blah import *\nfrom blah import _potato\n" assert isort.code(test_input) == ("from blah import *\nfrom blah import _potato\n") assert isort.code(test_input, combine_star=True) == ("from blah import *\n") def test_include_trailing_comma() -> None: """Test for the include_trailing_comma option""" test_output_grid = isort.code( code=SHORT_IMPORT, multi_line_output=WrapModes.GRID, line_length=40, include_trailing_comma=True, ) assert test_output_grid == ( "from third_party import (lib1, lib2,\n lib3, lib4,)\n" ) test_output_vertical = isort.code( code=SHORT_IMPORT, multi_line_output=WrapModes.VERTICAL, line_length=40, include_trailing_comma=True, ) assert test_output_vertical == ( "from third_party import (lib1,\n" " lib2,\n" " lib3,\n" " lib4,)\n" ) test_output_vertical_indent = isort.code( code=SHORT_IMPORT, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=40, include_trailing_comma=True, ) assert test_output_vertical_indent == ( "from third_party import (\n lib1,\n lib2,\n lib3,\n lib4,\n)\n" ) test_output_vertical_grid = isort.code( code=SHORT_IMPORT, multi_line_output=WrapModes.VERTICAL_GRID, line_length=40, include_trailing_comma=True, ) assert test_output_vertical_grid == ( "from third_party import (\n lib1, lib2, lib3, lib4,)\n" ) test_output_vertical_grid_grouped = isort.code( code=SHORT_IMPORT, multi_line_output=WrapModes.VERTICAL_GRID_GROUPED, line_length=40, include_trailing_comma=True, ) assert test_output_vertical_grid_grouped == ( "from third_party import (\n lib1, lib2, lib3, lib4,\n)\n" ) test_output_wrap_single_import_with_use_parentheses = isort.code( code=SINGLE_FROM_IMPORT, line_length=25, include_trailing_comma=True, use_parentheses=True ) assert test_output_wrap_single_import_with_use_parentheses == ( "from third_party import (\n lib1,)\n" ) test_output_wrap_single_import_vertical_indent = isort.code( code=SINGLE_FROM_IMPORT, line_length=25, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, include_trailing_comma=True, use_parentheses=True, ) assert test_output_wrap_single_import_vertical_indent == ( "from third_party import (\n lib1,\n)\n" ) trailing_comma_with_comment = ( "from six.moves.urllib.parse import urlencode " "# pylint: disable=no-name-in-module,import-error" ) expected_trailing_comma_with_comment = ( "from six.moves.urllib.parse import (\n" " urlencode, # pylint: disable=no-n" "ame-in-module,import-error\n)\n" ) trailing_comma_with_comment = isort.code( code=trailing_comma_with_comment, line_length=80, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, include_trailing_comma=True, use_parentheses=True, ) assert trailing_comma_with_comment == expected_trailing_comma_with_comment # The next time around, it should be equal trailing_comma_with_comment = isort.code( code=trailing_comma_with_comment, line_length=80, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, include_trailing_comma=True, use_parentheses=True, ) assert trailing_comma_with_comment == expected_trailing_comma_with_comment def test_similar_to_std_library() -> None: """Test to ensure modules that are named similarly to a standard library import don't end up clobbered """ test_input = "import datetime\n\nimport requests\nimport times\n" assert isort.code(test_input, known_third_party=["requests", "times"]) == test_input def test_correctly_placed_imports() -> None: """Test to ensure comments stay on correct placement after being sorted""" test_input = "from a import b # comment for b\nfrom a import c # comment for c\n" assert isort.code(test_input, force_single_line=True) == ( "from a import b # comment for b\nfrom a import c # comment for c\n" ) assert isort.code(test_input) == ( "from a import b # comment for b\nfrom a import c # comment for c\n" ) # Full example test from issue #143 test_input = ( "from itertools import chain\n" "\n" "from django.test import TestCase\n" "from model_mommy import mommy\n" "\n" "from apps.clientman.commands.download_usage_rights import " "associate_right_for_item_product\n" "from apps.clientman.commands.download_usage_rights import " "associate_right_for_item_product_d" "efinition\n" "from apps.clientman.commands.download_usage_rights import " "associate_right_for_item_product_d" "efinition_platform\n" "from apps.clientman.commands.download_usage_rights import " "associate_right_for_item_product_p" "latform\n" "from apps.clientman.commands.download_usage_rights import " "associate_right_for_territory_reta" "il_model\n" "from apps.clientman.commands.download_usage_rights import " "associate_right_for_territory_reta" "il_model_definition_platform_provider # noqa\n" "from apps.clientman.commands.download_usage_rights import " "clear_right_for_item_product\n" "from apps.clientman.commands.download_usage_rights import " "clear_right_for_item_product_defini" "tion\n" "from apps.clientman.commands.download_usage_rights import " "clear_right_for_item_product_defini" "tion_platform\n" "from apps.clientman.commands.download_usage_rights import " "clear_right_for_item_product_platfo" "rm\n" "from apps.clientman.commands.download_usage_rights import " "clear_right_for_territory_retail_mo" "del\n" "from apps.clientman.commands.download_usage_rights import " "clear_right_for_territory_retail_mo" "del_definition_platform_provider # noqa\n" "from apps.clientman.commands.download_usage_rights import " "create_download_usage_right\n" "from apps.clientman.commands.download_usage_rights import " "delete_download_usage_right\n" "from apps.clientman.commands.download_usage_rights import " "disable_download_for_item_product\n" "from apps.clientman.commands.download_usage_rights import " "disable_download_for_item_product_d" "efinition\n" "from apps.clientman.commands.download_usage_rights import " "disable_download_for_item_product_d" "efinition_platform\n" "from apps.clientman.commands.download_usage_rights import " "disable_download_for_item_product_p" "latform\n" "from apps.clientman.commands.download_usage_rights import " "disable_download_for_territory_reta" "il_model\n" "from apps.clientman.commands.download_usage_rights import " "disable_download_for_territory_reta" "il_model_definition_platform_provider # noqa\n" "from apps.clientman.commands.download_usage_rights import " "get_download_rights_for_item\n" "from apps.clientman.commands.download_usage_rights import " "get_right\n" ) assert ( isort.code( code=test_input, force_single_line=True, line_length=140, known_third_party=["django", "model_mommy"], default_section=sections.FIRSTPARTY, ) == test_input ) def test_auto_detection() -> None: """Initial test to ensure isort auto-detection works correctly - will grow over time as new issues are raised. """ # Issue 157 test_input = "import binascii\nimport os\n\nimport cv2\nimport requests\n" assert isort.code(test_input, known_third_party=["cv2", "requests"]) == test_input # alternative solution assert isort.code(test_input, default_section="THIRDPARTY") == test_input def test_same_line_statements() -> None: """Ensure isort correctly handles the case where a single line contains multiple statements including an import """ test_input = "import pdb; import nose\n" assert isort.code(test_input) == ("import pdb\n\nimport nose\n") test_input = "import pdb; pdb.set_trace()\nimport nose; nose.run()\n" assert isort.code(test_input) == test_input def test_long_line_comments() -> None: """Ensure isort correctly handles comments at the end of extremely long lines""" test_input = ( "from foo.utils.fabric_stuff.live import check_clean_live, deploy_live, " "sync_live_envdir, " "update_live_app, update_live_cron # noqa\n" "from foo.utils.fabric_stuff.stage import check_clean_stage, deploy_stage, " "sync_stage_envdir, " "update_stage_app, update_stage_cron # noqa\n" ) assert isort.code(code=test_input, line_length=100, balanced_wrapping=True) == ( "from foo.utils.fabric_stuff.live import (check_clean_live, deploy_live, # noqa\n" " sync_live_envdir, update_live_app, " "update_live_cron)\n" "from foo.utils.fabric_stuff.stage import (check_clean_stage, deploy_stage, # noqa\n" " sync_stage_envdir, update_stage_app, " "update_stage_cron)\n" ) def test_tab_character_in_import() -> None: """Ensure isort correctly handles import statements that contain a tab character""" test_input = "from __future__ import print_function\nfrom __future__ import\tprint_function\n" assert isort.code(test_input) == "from __future__ import print_function\n" def test_split_position() -> None: """Ensure isort splits on import instead of . when possible""" test_input = ( "from p24.shared.exceptions.master.host_state_flag_unchanged " "import HostStateUnchangedException\n" ) assert isort.code(test_input, line_length=80) == ( "from p24.shared.exceptions.master.host_state_flag_unchanged import \\\n" " HostStateUnchangedException\n" ) def test_place_comments() -> None: """Ensure manually placing imports works as expected""" test_input = ( "import sys\n" "import os\n" "import myproject.test\n" "import django.settings\n" "\n" "# isort: imports-thirdparty\n" "# isort: imports-firstparty\n" "# isort:imports-stdlib\n" "\n" ) expected_output = ( "\n# isort: imports-thirdparty\n" "import django.settings\n" "\n" "# isort: imports-firstparty\n" "import myproject.test\n" "\n" "# isort:imports-stdlib\n" "import os\n" "import sys\n" ) test_output = isort.code(test_input, known_first_party=["myproject"]) assert test_output == expected_output test_output = isort.code(test_output, known_first_party=["myproject"]) assert test_output == expected_output def test_placement_control() -> None: """Ensure that most specific placement control match wins""" test_input = ( "import os\n" "import sys\n" "from bottle import Bottle, redirect, response, run\n" "import p24.imports._argparse as argparse\n" "import p24.imports._subprocess as subprocess\n" "import p24.imports._VERSION as VERSION\n" "import p24.shared.media_wiki_syntax as syntax\n" ) test_output = isort.code( code=test_input, known_first_party=["p24", "p24.imports._VERSION"], known_standard_library=["p24.imports", "os", "sys"], known_third_party=["bottle"], default_section="THIRDPARTY", ) assert test_output == ( "import os\n" "import p24.imports._argparse as argparse\n" "import p24.imports._subprocess as subprocess\n" "import sys\n" "\n" "from bottle import Bottle, redirect, response, run\n" "\n" "import p24.imports._VERSION as VERSION\n" "import p24.shared.media_wiki_syntax as syntax\n" ) def test_custom_sections() -> None: """Ensure that most specific placement control match wins""" test_input = ( "import os\n" "import sys\n" "from django.conf import settings\n" "from bottle import Bottle, redirect, response, run\n" "import p24.imports._argparse as argparse\n" "from django.db import models\n" "import p24.imports._subprocess as subprocess\n" "import pandas as pd\n" "import p24.imports._VERSION as VERSION\n" "import numpy as np\n" "import p24.shared.media_wiki_syntax as syntax\n" ) test_output = isort.code( code=test_input, known_first_party=["p24", "p24.imports._VERSION"], import_heading_stdlib="Standard Library", import_heading_thirdparty="Third Party", import_heading_firstparty="First Party", import_heading_django="Django", import_heading_pandas="Pandas", known_standard_library=["p24.imports", "os", "sys"], known_third_party=["bottle"], known_django=["django"], known_pandas=["pandas", "numpy"], default_section="THIRDPARTY", sections=[ "FUTURE", "STDLIB", "DJANGO", "THIRDPARTY", "PANDAS", "FIRSTPARTY", "LOCALFOLDER", ], ) assert test_output == ( "# Standard Library\n" "import os\n" "import p24.imports._argparse as argparse\n" "import p24.imports._subprocess as subprocess\n" "import sys\n" "\n" "# Django\n" "from django.conf import settings\n" "from django.db import models\n" "\n" "# Third Party\n" "from bottle import Bottle, redirect, response, run\n" "\n" "# Pandas\n" "import numpy as np\n" "import pandas as pd\n" "\n" "# First Party\n" "import p24.imports._VERSION as VERSION\n" "import p24.shared.media_wiki_syntax as syntax\n" ) def test_custom_sections_exception_handling() -> None: """Ensure that appropriate exception is raised for missing sections""" test_input = "import requests\n" with pytest.raises(MissingSection): isort.code( code=test_input, default_section="THIRDPARTY", sections=[ "FUTURE", "STDLIB", "DJANGO", "PANDAS", "FIRSTPARTY", "LOCALFOLDER", ], ) test_input = "from requests import get, post\n" with pytest.raises(MissingSection): isort.code( code=test_input, default_section="THIRDPARTY", sections=[ "FUTURE", "STDLIB", "DJANGO", "PANDAS", "FIRSTPARTY", "LOCALFOLDER", ], ) def test_glob_known() -> None: """Ensure that most specific placement control match wins""" test_input = ( "import os\n" "from django_whatever import whatever\n" "import sys\n" "from django.conf import settings\n" "from . import another\n" ) test_output = isort.code( code=test_input, import_heading_stdlib="Standard Library", import_heading_thirdparty="Third Party", import_heading_firstparty="First Party", import_heading_django="Django", import_heading_djangoplugins="Django Plugins", import_heading_localfolder="Local", known_django=["django"], known_djangoplugins=["django_*"], default_section="THIRDPARTY", sections=[ "FUTURE", "STDLIB", "DJANGO", "DJANGOPLUGINS", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER", ], ) assert test_output == ( "# Standard Library\n" "import os\n" "import sys\n" "\n" "# Django\n" "from django.conf import settings\n" "\n" "# Django Plugins\n" "from django_whatever import whatever\n" "\n" "# Local\n" "from . import another\n" ) def test_sticky_comments() -> None: """Test to ensure it is possible to make comments 'stick' above imports""" test_input = ( "import os\n" "\n" "# Used for type-hinting (ref: https://github.com/davidhalter/jedi/issues/414).\n" "from selenium.webdriver.remote.webdriver import WebDriver # noqa\n" ) assert isort.code(test_input) == test_input test_input = ( "from django import forms\n" "# While this couples the geographic forms to the GEOS library,\n" "# it decouples from database (by not importing SpatialBackend).\n" "from django.contrib.gis.geos import GEOSException, GEOSGeometry\n" "from django.utils.translation import ugettext_lazy as _\n" ) assert isort.code(test_input) == test_input def test_zipimport() -> None: """Imports ending in "import" shouldn't be clobbered""" test_input = "from zipimport import zipimport\n" assert isort.code(test_input) == test_input def test_from_ending() -> None: """Imports ending in "from" shouldn't be clobbered.""" test_input = "from foo import get_foo_from, get_foo\n" expected_output = "from foo import get_foo, get_foo_from\n" assert isort.code(test_input) == expected_output def test_from_first() -> None: """Tests the setting from_first works correctly""" test_input = "from os import path\nimport os\n" assert isort.code(test_input, from_first=True) == test_input def test_top_comments() -> None: """Ensure correct behavior with top comments""" test_input = ( "# -*- encoding: utf-8 -*-\n# Test comment\n#\nfrom __future__ import unicode_literals\n" ) assert isort.code(test_input) == test_input test_input = ( "# -*- coding: utf-8 -*-\n" "from django.db import models\n" "from django.utils.encoding import python_2_unicode_compatible\n" ) assert isort.code(test_input) == test_input test_input = "# Comment\nimport sys\n" assert isort.code(test_input) == test_input test_input = "# -*- coding\nimport sys\n" assert isort.code(test_input) == test_input def test_consistency() -> None: """Ensures consistency of handling even when dealing with non ordered-by-type imports""" test_input = "from sqlalchemy.dialects.postgresql import ARRAY, array\n" assert isort.code(test_input, order_by_type=True) == test_input def test_force_grid_wrap() -> None: """Ensures removing imports works as expected.""" test_input = "from bar import lib2\nfrom foo import lib6, lib7\n" test_output = isort.code( code=test_input, force_grid_wrap=2, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT ) assert ( test_output == """from bar import lib2 from foo import ( lib6, lib7 ) """ ) test_output = isort.code( code=test_input, force_grid_wrap=3, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT ) assert test_output == test_input def test_force_grid_wrap_long() -> None: """Ensure that force grid wrap still happens with long line length""" test_input = ( "from foo import lib6, lib7\n" "from bar import lib2\n" "from babar import something_that_is_kind_of_long" ) test_output = isort.code( code=test_input, force_grid_wrap=2, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=9999, ) assert ( test_output == """from babar import something_that_is_kind_of_long from bar import lib2 from foo import ( lib6, lib7 ) """ ) def test_uses_jinja_variables() -> None: """Test a basic set of imports that use jinja variables""" test_input = "import sys\nimport os\nimport myproject.{ test }\nimport django.{ settings }" test_output = isort.code( code=test_input, known_third_party=["django"], known_first_party=["myproject"] ) assert test_output == ( "import os\nimport sys\n\nimport django.{ settings }\n\nimport myproject.{ test }\n" ) test_input = "import {{ cookiecutter.repo_name }}\nfrom foo import {{ cookiecutter.bar }}\n" assert isort.code(test_input) == test_input def test_fcntl() -> None: """Test to ensure fcntl gets correctly recognized as stdlib import""" test_input = "import fcntl\nimport os\nimport sys\n" assert isort.code(test_input) == test_input def test_import_split_is_word_boundary_aware() -> None: """Test to ensure that isort splits words in a boundary aware manner""" test_input = ( "from mycompany.model.size_value_array_import_func import \\\n" " get_size_value_array_import_func_jobs" ) test_output = isort.code( code=test_input, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=79 ) assert test_output == ( "from mycompany.model.size_value_array_import_func import (\n" " get_size_value_array_import_func_jobs\n" ")\n" ) def test_other_file_encodings(tmpdir) -> None: """Test to ensure file encoding is respected""" for encoding in ("latin1", "utf8"): tmp_fname = tmpdir.join(f"test_{encoding}.py") file_contents = f"# coding: {encoding}\n\ns = u'ã'\n" tmp_fname.write_binary(file_contents.encode(encoding)) api.sort_file(Path(tmp_fname), file_path=Path(tmp_fname), settings_path=os.getcwd()) assert tmp_fname.read_text(encoding) == file_contents def test_other_file_encodings_in_place(tmpdir) -> None: """Test to ensure file encoding is respected when overwritten in place.""" for encoding in ("latin1", "utf8"): tmp_fname = tmpdir.join(f"test_{encoding}.py") file_contents = f"# coding: {encoding}\n\ns = u'ã'\n" tmp_fname.write_binary(file_contents.encode(encoding)) api.sort_file( Path(tmp_fname), file_path=Path(tmp_fname), settings_path=os.getcwd(), overwrite_in_place=True, ) assert tmp_fname.read_text(encoding) == file_contents def test_encoding_not_in_comment(tmpdir) -> None: """Test that 'encoding' not in a comment is ignored""" tmp_fname = tmpdir.join("test_encoding.py") file_contents = "class Foo\n coding: latin1\n\ns = u'ã'\n" tmp_fname.write_binary(file_contents.encode("utf8")) assert ( isort.code( Path(tmp_fname).read_text("utf8"), file_path=Path(tmp_fname), settings_path=os.getcwd() ) == file_contents ) def test_encoding_not_in_first_two_lines(tmpdir) -> None: """Test that 'encoding' not in the first two lines is ignored""" tmp_fname = tmpdir.join("test_encoding.py") file_contents = "\n\n# -*- coding: latin1\n\ns = u'ã'\n" tmp_fname.write_binary(file_contents.encode("utf8")) assert ( isort.code( Path(tmp_fname).read_text("utf8"), file_path=Path(tmp_fname), settings_path=os.getcwd() ) == file_contents ) def test_comment_at_top_of_file() -> None: """Test to ensure isort correctly handles top of file comments""" test_input = ( "# Comment one\n" "from django import forms\n" "# Comment two\n" "from django.contrib.gis.geos import GEOSException\n" ) assert isort.code(test_input) == test_input test_input = "# -*- coding: utf-8 -*-\nfrom django.db import models\n" assert isort.code(test_input) == test_input def test_alphabetic_sorting() -> None: """Test to ensure isort correctly handles single line imports""" test_input = ( "import unittest\n" "\n" "import ABC\n" "import Zope\n" "from django.contrib.gis.geos import GEOSException\n" "from plone.app.testing import getRoles\n" "from plone.app.testing import ManageRoles\n" "from plone.app.testing import setRoles\n" "from Products.CMFPlone import utils\n" ) options = { "force_single_line": True, "force_alphabetical_sort_within_sections": True, } # type: dict[str, Any] output = isort.code(test_input, **options) assert output == test_input test_input = "# -*- coding: utf-8 -*-\nfrom django.db import models\n" assert isort.code(test_input) == test_input def test_alphabetic_sorting_multi_line() -> None: """Test to ensure isort correctly handles multiline import see: issue 364""" test_input = ( "from a import (CONSTANT_A, cONSTANT_B, CONSTANT_C, CONSTANT_D, CONSTANT_E,\n" " CONSTANT_F, CONSTANT_G, CONSTANT_H, CONSTANT_I, CONSTANT_J)\n" ) options = {"force_alphabetical_sort_within_sections": True} # type: dict[str, Any] assert isort.code(test_input, **options) == test_input def test_comments_not_duplicated() -> None: """Test to ensure comments aren't duplicated: issue 303""" test_input = ( "from flask import url_for\n" "# Whole line comment\n" "from service import demo # inline comment\n" "from service import settings\n" ) output = isort.code(test_input) assert output.count("# Whole line comment\n") == 1 assert output.count("# inline comment\n") == 1 def test_top_of_line_comments() -> None: """Test to ensure top of line comments stay where they should: issue 260""" test_input = ( "# -*- coding: utf-8 -*-\n" "from django.db import models\n" "#import json as simplejson\n" "from myproject.models import Servidor\n" "\n" "import reversion\n" "\n" "import logging\n" ) output = isort.code(test_input) print(output) assert output.startswith("# -*- coding: utf-8 -*-\n") def test_basic_comment() -> None: """Test to ensure a basic comment won't crash isort""" test_input = "import logging\n# Foo\nimport os\n" assert isort.code(test_input) == test_input def test_shouldnt_add_lines() -> None: """Ensure that isort doesn't add a blank line when a top of import comment is present, See: issue #316 """ test_input = '"""Text"""\n# This is a comment\nimport pkg_resources\n' assert isort.code(test_input) == test_input def test_sections_parsed_correct(tmpdir) -> None: """Ensure that modules for custom sections parsed as list from config file and isort result is correct """ conf_file_data = ( "[settings]\n" "sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER,COMMON\n" "known_common=nose\n" "import_heading_common=Common Library\n" "import_heading_stdlib=Standard Library\n" ) test_input = "import os\nfrom nose import *\nimport nose\nfrom os import path" correct_output = ( "# Standard Library\n" "import os\n" "from os import path\n" "\n" "# Common Library\n" "import nose\n" "from nose import *\n" ) tmpdir.join(".isort.cfg").write(conf_file_data) assert isort.code(test_input, settings_path=str(tmpdir)) == correct_output def test_pyproject_conf_file(tmpdir) -> None: """Ensure that modules for custom sections parsed as list from config file and isort result is correct """ conf_file_data = ( "[build-system]\n" 'requires = ["setuptools", "wheel"]\n' "[project]\n" 'name = "isort"\n' 'version = "0.1.0"\n' 'license = "MIT"\n' "[tool.isort]\n" "lines_between_types=1\n" 'known_common="nose"\n' 'known_first_party="foo"\n' 'import_heading_common="Common Library"\n' 'import_heading_stdlib="Standard Library"\n' 'sections="FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER,COMMON"\n' "include_trailing_comma = true\n" ) test_input = "import os\nfrom nose import *\nimport nose\nfrom os import path\nimport foo" correct_output = ( "# Standard Library\n" "import os\n" "\n" "from os import path\n" "\n" "import foo\n" "\n" "# Common Library\n" "import nose\n" "\n" "from nose import *\n" ) tmpdir.join("pyproject.toml").write(conf_file_data) assert isort.code(test_input, settings_path=str(tmpdir)) == correct_output def test_alphabetic_sorting_no_newlines() -> None: """Test to ensure that alphabetical sort does not erroneously introduce new lines (issue #328) """ test_input = "import os\n" test_output = isort.code(code=test_input, force_alphabetical_sort_within_sections=True) assert test_input == test_output test_input = "import os\nimport unittest\n\nfrom a import b\n\n\nprint(1)\n" test_output = isort.code( code=test_input, force_alphabetical_sort_within_sections=True, lines_after_imports=2 ) assert test_input == test_output def test_sort_within_section() -> None: """Test to ensure its possible to force isort to sort within sections""" test_input = ( "from Foob import ar\nimport foo\nfrom foo import bar\nfrom foo.bar import Quux, baz\n" ) test_output = isort.code(test_input, force_sort_within_sections=True) assert test_output == test_input test_input = ( "import foo\n" "from foo import bar\n" "from foo.bar import baz\n" "from foo.bar import Quux\n" "from Foob import ar\n" ) test_output = isort.code( code=test_input, force_sort_within_sections=True, order_by_type=False, force_single_line=True, ) assert test_output == test_input test_input = ( "import foo\n" "from foo import bar\n" "from foo.bar import baz\n" "from foo.bar import Quux\n" "from Foob import ar\n" ) test_output = isort.code( code=test_input, case_sensitive=True, force_sort_within_sections=True, order_by_type=False, force_single_line=True, ) assert test_output == test_input test_input = "from Foob import ar\nimport foo\nfrom foo import Quux\nfrom foo import baz\n" test_output = isort.code( code=test_input, case_sensitive=True, force_sort_within_sections=True, order_by_type=True, force_single_line=True, ) assert test_output == test_input def test_sort_within_section_case_honored() -> None: """Ensure isort can do partial case-sensitive sorting in force-sorted sections""" test_input = ( "import foo\nfrom foo import bar\nfrom foo.bar import Quux, baz\nfrom Foob import ar\n" ) test_output = isort.code( test_input, force_sort_within_sections=True, honor_case_in_force_sorted_sections=True ) assert test_output == test_input test_input = ( "import foo\n" "from foo import bar\n" "from foo.bar import baz\n" "from foo.bar import Quux\n" "from Foob import ar\n" ) test_output = isort.code( code=test_input, force_sort_within_sections=True, honor_case_in_force_sorted_sections=True, order_by_type=False, force_single_line=True, ) assert test_output == test_input test_input = ( "from Foob import ar\n" "import foo\n" "from foo import bar\n" "from foo.bar import baz\n" "from foo.bar import Quux\n" ) test_output = isort.code( code=test_input, case_sensitive=True, force_sort_within_sections=True, honor_case_in_force_sorted_sections=True, order_by_type=False, force_single_line=True, ) assert test_output == test_input test_input = "from Foob import ar\nimport foo\nfrom foo import Quux\nfrom foo import baz\n" test_output = isort.code( code=test_input, case_sensitive=True, force_sort_within_sections=True, honor_case_in_force_sorted_sections=True, order_by_type=True, force_single_line=True, ) assert test_output == test_input def test_sorting_with_two_top_comments() -> None: """Test to ensure isort will sort files that contain 2 top comments""" test_input = "#! comment1\n''' comment2\n'''\nimport b\nimport a\n" assert isort.code(test_input) == ("#! comment1\n''' comment2\n'''\nimport a\nimport b\n") def test_lines_between_sections() -> None: """Test to ensure lines_between_sections works""" test_input = "from bar import baz\nimport os\n" assert isort.code(test_input, lines_between_sections=0) == ("import os\nfrom bar import baz\n") assert isort.code(test_input, lines_between_sections=2) == ( "import os\n\n\nfrom bar import baz\n" ) def test_forced_sepatate_globs() -> None: """Test to ensure that forced_separate glob matches lines""" test_input = ( "import os\n" "\n" "from myproject.foo.models import Foo\n" "\n" "from myproject.utils import util_method\n" "\n" "from myproject.bar.models import Bar\n" "\n" "import sys\n" ) test_output = isort.code(code=test_input, forced_separate=["*.models"], line_length=120) assert test_output == ( "import os\n" "import sys\n" "\n" "from myproject.utils import util_method\n" "\n" "from myproject.bar.models import Bar\n" "from myproject.foo.models import Foo\n" ) def test_no_additional_lines_issue_358() -> None: """Test to ensure issue 358 is resolved and running isort multiple times does not add extra newlines """ test_input = ( '"""This is a docstring"""\n' "# This is a comment\n" "from __future__ import (\n" " absolute_import,\n" " division,\n" " print_function,\n" " unicode_literals\n" ")\n" ) expected_output = ( '"""This is a docstring"""\n' "# This is a comment\n" "from __future__ import (\n" " absolute_import,\n" " division,\n" " print_function,\n" " unicode_literals\n" ")\n" ) test_output = isort.code( code=test_input, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=20 ) assert test_output == expected_output test_output = isort.code( code=test_output, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=20 ) assert test_output == expected_output for _attempt in range(5): test_output = isort.code( code=test_output, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=20 ) assert test_output == expected_output test_input = ( '"""This is a docstring"""\n' "\n" "# This is a comment\n" "from __future__ import (\n" " absolute_import,\n" " division,\n" " print_function,\n" " unicode_literals\n" ")\n" ) expected_output = ( '"""This is a docstring"""\n' "\n" "# This is a comment\n" "from __future__ import (\n" " absolute_import,\n" " division,\n" " print_function,\n" " unicode_literals\n" ")\n" ) test_output = isort.code( code=test_input, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=20 ) assert test_output == expected_output test_output = isort.code( code=test_output, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=20 ) assert test_output == expected_output for _attempt in range(5): test_output = isort.code( code=test_output, multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, line_length=20 ) assert test_output == expected_output def test_import_by_paren_issue_375() -> None: """Test to ensure isort can correctly handle sorting imports where the paren is directly by the import body """ test_input = "from .models import(\n Foo,\n Bar,\n)\n" assert isort.code(test_input) == "from .models import Bar, Foo\n" def test_import_by_paren_issue_460() -> None: """Test to ensure isort can doesn't move comments around""" test_input = """ # First comment # Second comment # third comment import io import os """ assert isort.code(test_input) == test_input def test_function_with_docstring() -> None: """Test to ensure isort can correctly sort imports when the first found content is a function with a docstring """ add_imports = ["from __future__ import unicode_literals"] test_input = 'def foo():\n """ Single line triple quoted doctring """\n pass\n' expected_output = ( "from __future__ import unicode_literals\n" "\n" "\n" "def foo():\n" ' """ Single line triple quoted doctring """\n' " pass\n" ) assert isort.code(test_input, add_imports=add_imports) == expected_output def test_plone_style() -> None: """Test to ensure isort correctly plone style imports""" test_input = ( "from django.contrib.gis.geos import GEOSException\n" "from plone.app.testing import getRoles\n" "from plone.app.testing import ManageRoles\n" "from plone.app.testing import setRoles\n" "from Products.CMFPlone import utils\n" "\n" "import ABC\n" "import unittest\n" "import Zope\n" ) options = {"force_single_line": True, "force_alphabetical_sort": True} # type: dict[str, Any] assert isort.code(test_input, **options) == test_input def test_third_party_case_sensitive() -> None: """Modules which match builtins by name but not on case should not be picked up on Windows.""" test_input = "import thirdparty\nimport os\nimport ABC\n" expected_output = "import os\n\nimport ABC\nimport thirdparty\n" assert isort.code(test_input) == expected_output def test_exists_case_sensitive_file(tmpdir) -> None: """Test exists_case_sensitive function for a file.""" exists_case_sensitive.cache_clear() tmpdir.join("module.py").ensure(file=1) assert exists_case_sensitive(str(tmpdir.join("module.py"))) assert not exists_case_sensitive(str(tmpdir.join("MODULE.py"))) def test_exists_case_sensitive_directory(tmpdir) -> None: """Test exists_case_sensitive function for a directory.""" exists_case_sensitive.cache_clear() tmpdir.join("pkg").ensure(dir=1) assert exists_case_sensitive(str(tmpdir.join("pkg"))) assert not exists_case_sensitive(str(tmpdir.join("PKG"))) def test_sys_path_mutation(tmpdir) -> None: """Test to ensure sys.path is not modified""" tmpdir.mkdir("src").mkdir("a") test_input = "from myproject import test" options = {"virtual_env": str(tmpdir)} # type: dict[str, Any] expected_length = len(sys.path) isort.code(test_input, **options) assert len(sys.path) == expected_length def test_long_single_line() -> None: """Test to ensure long single lines get handled correctly""" output = isort.code( code="from ..views import (" " _a," "_xxxxxx_xxxxxxx_xxxxxxxx_xxx_xxxxxxx as xxxxxx_xxxxxxx_xxxxxxxx_xxx_xxxxxxx)", line_length=79, ) for line in output.split("\n"): assert len(line) <= 79 output = isort.code( code="from ..views import (" " _a," "_xxxxxx_xxxxxxx_xxxxxxxx_xxx_xxxxxxx as xxxxxx_xxxxxxx_xxxxxxxx_xxx_xxxxxxx)", line_length=79, combine_as_imports=True, ) for line in output.split("\n"): assert len(line) <= 79 def test_import_inside_class_issue_432() -> None: """Test to ensure issue 432 is resolved and isort doesn't insert imports in the middle of classes """ test_input = "# coding=utf-8\nclass Foo:\n def bar(self):\n pass\n" expected_output = ( "# coding=utf-8\nimport baz\n\n\nclass Foo:\n def bar(self):\n pass\n" ) assert isort.code(test_input, add_imports=["import baz"]) == expected_output def test_wildcard_import_without_space_issue_496() -> None: """Test to ensure issue #496: wildcard without space, is resolved""" test_input = "from findorserver.coupon.models import*" expected_output = "from findorserver.coupon.models import *\n" assert isort.code(test_input) == expected_output def test_import_line_mangles_issues_491() -> None: """Test to ensure comment on import with parens doesn't cause issues""" test_input = 'import os # ([\n\nprint("hi")\n' assert isort.code(test_input) == test_input def test_import_line_mangles_issues_505() -> None: """Test to ensure comment on import with parens doesn't cause issues""" test_input = 'from sys import * # (\n\n\ndef test():\n print("Test print")\n' assert isort.code(test_input) == test_input def test_import_line_mangles_issues_439() -> None: """Test to ensure comment on import with parens doesn't cause issues""" test_input = "import a # () import\nfrom b import b\n" assert isort.code(test_input) == test_input def test_alias_using_paren_issue_466() -> None: """Test to ensure issue #466: Alias causes slash incorrectly is resolved""" test_input = ( "from django.db.backends.mysql.base import DatabaseWrapper as MySQLDatabaseWrapper\n" ) expected_output = ( "from django.db.backends.mysql.base import (\n" " DatabaseWrapper as MySQLDatabaseWrapper)\n" ) assert isort.code(test_input, line_length=50, use_parentheses=True) == expected_output test_input = ( "from django.db.backends.mysql.base import DatabaseWrapper as MySQLDatabaseWrapper\n" ) expected_output = ( "from django.db.backends.mysql.base import (\n" " DatabaseWrapper as MySQLDatabaseWrapper\n" ")\n" ) assert ( isort.code( code=test_input, line_length=50, multi_line_output=WrapModes.VERTICAL_GRID_GROUPED, use_parentheses=True, ) == expected_output ) def test_long_alias_using_paren_issue_957() -> None: test_input = ( "from package import module as very_very_very_very_very_very_very" "_very_very_very_long_alias\n" ) expected_output = ( "from package import (\n" " module as very_very_very_very_very_very_very_very_very_very_long_alias\n" ")\n" ) out = isort.code( code=test_input, line_length=50, use_parentheses=True, multi_line_output=WrapModes.VERTICAL_GRID_GROUPED, ) assert out == expected_output test_input = ( "from deep.deep.deep.deep.deep.deep.deep.deep.deep.package import module as " "very_very_very_very_very_very_very_very_very_very_long_alias\n" ) expected_output = ( "from deep.deep.deep.deep.deep.deep.deep.deep.deep.package import (\n" " module as very_very_very_very_very_very_very_very_very_very_long_alias\n" ")\n" ) out = isort.code( code=test_input, line_length=50, use_parentheses=True, multi_line_output=WrapModes.VERTICAL_GRID_GROUPED, ) assert out == expected_output test_input = ( "from deep.deep.deep.deep.deep.deep.deep.deep.deep.package " "import very_very_very_very_very_very_very_very_very_very_long_module as very_very_very_" "very_very_very_very_very_very_very_long_alias\n" ) expected_output = ( "from deep.deep.deep.deep.deep.deep.deep.deep.deep.package import (\n" " very_very_very_very_very_very_very_very_very_very_long_module as very_very_very_very" "_very_very_very_very_very_very_long_alias\n" ")\n" ) out = isort.code( code=test_input, line_length=50, use_parentheses=True, multi_line_output=WrapModes.VERTICAL_GRID_GROUPED, ) assert out == expected_output def test_strict_whitespace_by_default(capsys) -> None: test_input = "import os\nfrom django.conf import settings\n" assert not api.check_code_string(test_input) _, err = capsys.readouterr() assert "ERROR" in err assert err.endswith("Imports are incorrectly sorted and/or formatted.\n") def test_strict_whitespace_no_closing_newline_issue_676(capsys) -> None: test_input = "import os\n\nfrom django.conf import settings\n\nprint(1)" assert api.check_code_string(test_input) out, _ = capsys.readouterr() assert out == "" def test_ignore_whitespace(capsys) -> None: test_input = "import os\nfrom django.conf import settings\n" assert api.check_code_string(test_input, ignore_whitespace=True) out, _ = capsys.readouterr() assert out == "" def test_import_wraps_with_comment_issue_471() -> None: """Test to ensure issue #471 is resolved""" test_input = ( "from very_long_module_name import SuperLongClassName #@UnusedImport" " -- long string of comments which wrap over" ) expected_output = ( "from very_long_module_name import (\n" " SuperLongClassName) # @UnusedImport -- long string of comments which wrap over\n" ) assert ( isort.code(code=test_input, line_length=50, multi_line_output=1, use_parentheses=True) == expected_output ) def test_import_case_produces_inconsistent_results_issue_472() -> None: """Test to ensure sorting imports with same name but different case produces the same result across platforms """ test_input = ( "from sqlalchemy.dialects.postgresql import ARRAY\n" "from sqlalchemy.dialects.postgresql import array\n" ) assert isort.code(test_input, force_single_line=True) == test_input test_input = ( "from scrapy.core.downloader.handlers.http import " "HttpDownloadHandler, HTTPDownloadHandler\n" ) assert isort.code(test_input, line_length=100) == test_input def test_inconsistent_behavior_in_python_2_and_3_issue_479() -> None: """Test to ensure Python 2 and 3 have the same behavior""" test_input = ( "from workalendar.europe import UnitedKingdom\n" "\n" "from future.standard_library import hooks\n" ) assert isort.code(test_input, known_first_party=["future"]) == test_input def test_sort_within_section_comments_issue_436() -> None: """Test to ensure sort within sections leaves comments untouched""" test_input = ( "import os.path\n" "import re\n" "\n" "# report.py exists in ... comment line 1\n" "# this file needs to ... comment line 2\n" "# it must not be ... comment line 3\n" "import report\n" ) assert isort.code(test_input, force_sort_within_sections=True) == test_input def test_sort_within_sections_with_force_to_top_issue_473() -> None: """Test to ensure it's possible to sort within sections with items forced to top""" test_input = "import z\nimport foo\nfrom foo import bar\n" assert ( isort.code(code=test_input, force_sort_within_sections=True, force_to_top=["z"]) == test_input ) def test_force_sort_within_sections_with_relative_imports() -> None: """Test sorting of relative imports with force_sort_within_sections=True""" assert isort.check_code( """import . from . import foo from .. import a from ..alpha.beta import b from ..omega import c import .apple as bar from .mango import baz """, show_diff=True, force_sort_within_sections=True, ) def test_force_sort_within_sections_with_reverse_relative_imports() -> None: """Test reverse sorting of relative imports with force_sort_within_sections=True""" assert isort.check_code( """import . from . import foo from .mango import baz from ..alpha.beta import b from .. import a from ..omega import c import .apple as bar """, show_diff=True, force_sort_within_sections=True, reverse_relative=True, ) def test_sort_relative_in_force_sorted_sections_issue_1659() -> None: """Ensure relative imports are sorted within sections""" assert isort.check_code( """from .. import a from ..alpha.beta import b from ..omega import c import . from . import foo import .apple as bar from .mango import baz """, show_diff=True, force_sort_within_sections=True, sort_relative_in_force_sorted_sections=True, ) def test_reverse_sort_relative_in_force_sorted_sections_issue_1659() -> None: """Ensure reverse ordered relative imports are sorted within sections""" assert isort.check_code( """import . from . import foo import .apple as bar from .mango import baz from .. import a from ..alpha.beta import b from ..omega import c """, show_diff=True, force_sort_within_sections=True, sort_relative_in_force_sorted_sections=True, reverse_relative=True, ) def test_correct_number_of_new_lines_with_comment_issue_435() -> None: """Test to ensure that injecting a comment in-between imports doesn't mess up the new line spacing """ test_input = "import foo\n\n# comment\n\n\ndef baz():\n pass\n" assert isort.code(test_input) == test_input def test_future_below_encoding_issue_545() -> None: """Test to ensure future is always below comment""" test_input = ( "#!/usr/bin/env python\n" "from __future__ import print_function\n" "import logging\n" "\n" 'print("hello")\n' ) expected_output = ( "#!/usr/bin/env python\n" "from __future__ import print_function\n" "\n" "import logging\n" "\n" 'print("hello")\n' ) assert isort.code(test_input) == expected_output def test_no_extra_lines_issue_557() -> None: """Test to ensure no extra lines are prepended""" test_input = ( "import os\n" "\n" "from scrapy.core.downloader.handlers.http import " "HttpDownloadHandler, HTTPDownloadHandler\n" ) expected_output = ( "import os\n" "from scrapy.core.downloader.handlers.http import HttpDownloadHandler, " "HTTPDownloadHandler\n" ) assert ( isort.code( code=test_input, force_alphabetical_sort=True, force_sort_within_sections=True, line_length=100, ) == expected_output ) def test_long_import_wrap_support_with_mode_2() -> None: """Test to ensure mode 2 still allows wrapped imports with slash""" test_input = ( "from foobar.foobar.foobar.foobar import \\\n" " an_even_longer_function_name_over_80_characters\n" ) assert ( isort.code(code=test_input, multi_line_output=WrapModes.HANGING_INDENT, line_length=80) == test_input ) def test_pylint_comments_incorrectly_wrapped_issue_571() -> None: """Test to ensure pylint comments don't get wrapped""" test_input = ( "from PyQt5.QtCore import QRegExp # @UnresolvedImport pylint: disable=import-error," "useless-suppression\n" ) expected_output = ( "from PyQt5.QtCore import \\\n" " QRegExp # @UnresolvedImport pylint: disable=import-error,useless-suppression\n" ) assert isort.code(test_input, line_length=60) == expected_output def test_ensure_async_methods_work_issue_537() -> None: """Test to ensure async methods are correctly identified""" test_input = ( "from myapp import myfunction\n" "\n" "\n" "async def test_myfunction(test_client, app):\n" " a = await myfunction(test_client, app)\n" ) assert isort.code(test_input) == test_input def test_ensure_as_imports_sort_correctly_within_from_imports_issue_590() -> None: """Test to ensure combination from and as import statements are sorted correct""" test_input = "from os import defpath\nfrom os import pathsep as separator\n" assert isort.code(test_input, force_sort_within_sections=True) == test_input test_input = "from os import defpath\nfrom os import pathsep as separator\n" assert isort.code(test_input) == test_input test_input = "from os import defpath\nfrom os import pathsep as separator\n" assert isort.code(test_input, force_single_line=True) == test_input def test_ensure_line_endings_are_preserved_issue_493() -> None: """Test to ensure line endings are not converted""" test_input = "from os import defpath\r\nfrom os import pathsep as separator\r\n" assert isort.code(test_input) == test_input test_input = "from os import defpath\rfrom os import pathsep as separator\r" assert isort.code(test_input) == test_input test_input = "from os import defpath\nfrom os import pathsep as separator\n" assert isort.code(test_input) == test_input @pytest.mark.parametrize("ws", [" ", "\t", "\f"]) def test_line_endings_are_detected_ignoring_whitespace(ws: str) -> None: """Test to ensure line endings are not converted""" test_input = f"# foo{ws}\r\nimport a\r\n\r\ncimport z\r\n" assert isort.code(test_input) == test_input test_input = f"# foo{ws}\rimport a\r\rcimport z\r" assert isort.code(test_input) == test_input test_input = f"# foo{ws}\nimport a\n\ncimport z\n" assert isort.code(test_input) == test_input def test_not_splitted_sections() -> None: whiteline = "\n" stdlib_section = "import unittest\n" firstparty_section = "from app.pkg1 import mdl1\n" local_section = "from .pkg2 import mdl2\n" statement = "foo = bar\n" test_input = ( stdlib_section + whiteline + firstparty_section + whiteline + local_section + whiteline + statement ) assert isort.code(test_input, known_first_party=["app"]) == test_input assert isort.code(test_input, no_lines_before=["LOCALFOLDER"], known_first_party=["app"]) == ( stdlib_section + whiteline + firstparty_section + local_section + whiteline + statement ) # by default STDLIB and FIRSTPARTY sections are split by THIRDPARTY section, # so don't merge them if THIRDPARTY imports aren't exist assert ( isort.code(test_input, no_lines_before=["FIRSTPARTY"], known_first_party=["app"]) == test_input ) # in case when THIRDPARTY section is excluded from sections list, # it's ok to merge STDLIB and FIRSTPARTY assert isort.code( code=test_input, sections=["STDLIB", "FIRSTPARTY", "LOCALFOLDER"], no_lines_before=["FIRSTPARTY"], known_first_party=["app"], ) == (stdlib_section + firstparty_section + whiteline + local_section + whiteline + statement) # it doesn't change output, because stdlib packages don't have any whitelines before them assert ( isort.code(test_input, no_lines_before=["STDLIB"], known_first_party=["app"]) == test_input ) def test_no_lines_before_empty_section() -> None: test_input = "import first\nimport custom\n" assert ( isort.code( code=test_input, known_third_party=["first"], known_custom=["custom"], sections=["THIRDPARTY", "LOCALFOLDER", "CUSTOM"], no_lines_before=["THIRDPARTY", "LOCALFOLDER", "CUSTOM"], ) == test_input ) def test_no_inline_sort() -> None: """Test to ensure multiple `from` imports in one line are not sorted if `--no-inline-sort` flag is enabled. If `--force-single-line-imports` flag is enabled, then `--no-inline-sort` is ignored. """ test_input = "from foo import a, c, b\n" assert isort.code(test_input, no_inline_sort=True, force_single_line=False) == test_input assert ( isort.code(test_input, no_inline_sort=False, force_single_line=False) == "from foo import a, b, c\n" ) expected = "from foo import a\nfrom foo import b\nfrom foo import c\n" assert isort.code(test_input, no_inline_sort=False, force_single_line=True) == expected assert isort.code(test_input, no_inline_sort=True, force_single_line=True) == expected def test_relative_import_of_a_module() -> None: """Imports can be dynamically created (PEP302) and is used by modules such as six. This test ensures that these types of imports are still sorted to the correct type instead of being categorized as local. """ test_input = ( "from __future__ import absolute_import\n" "\n" "import itertools\n" "\n" "from six import add_metaclass\n" "\n" "from six.moves import asd\n" ) expected_results = ( "from __future__ import absolute_import\n" "\n" "import itertools\n" "\n" "from six import add_metaclass\n" "from six.moves import asd\n" ) sorted_result = isort.code(test_input, force_single_line=True) assert sorted_result == expected_results def test_escaped_parens_sort() -> None: test_input = "from foo import \\ \n(a,\nb,\nc)\n" expected = "from foo import a, b, c\n" assert isort.code(test_input) == expected def test_escaped_parens_sort_with_comment() -> None: test_input = "from foo import \\ \n(a,\nb,# comment\nc)\n" expected = "from foo import b # comment\nfrom foo import a, c\n" assert isort.code(test_input) == expected def test_escaped_parens_sort_with_first_comment() -> None: test_input = "from foo import \\ \n(a,# comment\nb,\nc)\n" expected = "from foo import a # comment\nfrom foo import b, c\n" assert isort.code(test_input) == expected def test_escaped_no_parens_sort_with_first_comment() -> None: test_input = "from foo import a, \\\nb, \\\nc # comment\n" expected = "from foo import c # comment\nfrom foo import a, b\n" assert isort.code(test_input) == expected @pytest.mark.skip(reason="TODO: Duplicates currently not handled.") def test_to_ensure_imports_are_brought_to_top_issue_651() -> None: test_input = ( "from __future__ import absolute_import, unicode_literals\n" "\n" 'VAR = """\n' "multiline text\n" '"""\n' "\n" "from __future__ import unicode_literals\n" "from __future__ import absolute_import\n" ) expected_output = ( "from __future__ import absolute_import, unicode_literals\n" "\n" 'VAR = """\n' "multiline text\n" '"""\n' ) assert isort.code(test_input) == expected_output def test_to_ensure_importing_from_imports_module_works_issue_662() -> None: test_input = ( "@wraps(fun)\n" "def __inner(*args, **kwargs):\n" " from .imports import qualname\n" "\n" " warn(description=description or qualname(fun), deprecation=deprecation, " "removal=removal)\n" ) assert isort.code(test_input) == test_input def test_to_ensure_no_unexpected_changes_issue_666() -> None: test_input = ( "from django.conf import settings\n" "from django.core.management import call_command\n" "from django.core.management.base import BaseCommand\n" "from django.utils.translation import ugettext_lazy as _\n" "\n" 'TEMPLATE = """\n' "# This file is generated automatically with the management command\n" "#\n" "# manage.py bis_compile_i18n\n" "#\n" "# please dont change it manually.\n" "from django.utils.translation import ugettext_lazy as _\n" '"""\n' ) assert isort.code(test_input) == test_input def test_to_ensure_tabs_dont_become_space_issue_665() -> None: test_input = "import os\n\n\ndef my_method():\n\tpass\n" assert isort.code(test_input) == test_input def test_new_lines_are_preserved() -> None: with NamedTemporaryFile("w", suffix="py", delete=False) as rn_newline: pass try: with open(rn_newline.name, mode="w", newline="") as rn_newline_input: rn_newline_input.write("import sys\r\nimport os\r\n") api.sort_file(rn_newline.name, settings_path=os.getcwd()) with open(rn_newline.name) as new_line_file: print(new_line_file.read()) with open(rn_newline.name, newline="") as rn_newline_file: rn_newline_contents = rn_newline_file.read() assert rn_newline_contents == "import os\r\nimport sys\r\n" finally: os.remove(rn_newline.name) with NamedTemporaryFile("w", suffix="py", delete=False) as r_newline: pass try: with open(r_newline.name, mode="w", newline="") as r_newline_input: r_newline_input.write("import sys\rimport os\r") api.sort_file(r_newline.name, settings_path=os.getcwd()) with open(r_newline.name, newline="") as r_newline_file: r_newline_contents = r_newline_file.read() assert r_newline_contents == "import os\rimport sys\r" finally: os.remove(r_newline.name) with NamedTemporaryFile("w", suffix="py", delete=False) as n_newline: pass try: with open(n_newline.name, mode="w", newline="") as n_newline_input: n_newline_input.write("import sys\nimport os\n") api.sort_file(n_newline.name, settings_path=os.getcwd()) with open(n_newline.name, newline="") as n_newline_file: n_newline_contents = n_newline_file.read() assert n_newline_contents == "import os\nimport sys\n" finally: os.remove(n_newline.name) def test_forced_separate_is_deterministic_issue_774(tmpdir) -> None: config_file = tmpdir.join("setup.cfg") config_file.write( "[isort]\nforced_separate:\n separate1\n separate2\n separate3\n separate4\n" ) test_input = ( "import time\n" "\n" "from separate1 import foo\n" "\n" "from separate2 import bar\n" "\n" "from separate3 import baz\n" "\n" "from separate4 import quux\n" ) assert isort.code(test_input, settings_file=config_file.strpath) == test_input def test_monkey_patched_urllib() -> None: with pytest.raises(ImportError): # Previous versions of isort monkey patched urllib which caused unusual # importing for other projects. from urllib import quote # type: ignore # noqa: F401, PLC0415 def test_argument_parsing() -> None: args = parse_args(["--dt", "-t", "foo", "--skip=bar", "baz.py", "--os"]) assert args["order_by_type"] is False assert args["force_to_top"] == ["foo"] assert args["skip"] == ["bar"] assert args["files"] == ["baz.py"] assert args["only_sections"] is True @pytest.mark.parametrize("multiprocess", [False, True]) def test_command_line(tmpdir, capfd, multiprocess: bool) -> None: tmpdir.join("file1.py").write("import re\nimport os\n\nimport contextlib\n\n\nimport isort") tmpdir.join("file2.py").write("import collections\nimport time\n\nimport abc\n\n\nimport isort") arguments = [str(tmpdir), "--settings-path", os.getcwd()] if multiprocess: arguments.extend(["--jobs", "2"]) main(arguments) assert ( tmpdir.join("file1.py").read() == "import contextlib\nimport os\nimport re\n\nimport isort\n" ) assert ( tmpdir.join("file2.py").read() == "import abc\nimport collections\nimport time\n\nimport isort\n" ) if not (sys.platform.startswith("win") or sys.platform.startswith("darwin")): out, err = capfd.readouterr() assert not [error for error in err.split("\n") if error and "warning:" not in error] # it informs us about fixing the files: assert str(tmpdir.join("file1.py")) in out assert str(tmpdir.join("file2.py")) in out @pytest.mark.parametrize("quiet", [False, True]) def test_quiet(tmpdir, capfd, quiet: bool) -> None: if sys.platform.startswith("win"): return tmpdir.join("file1.py").write("import re\nimport os") tmpdir.join("file2.py").write("") arguments = [str(tmpdir)] if quiet: arguments.append("-q") main(arguments) out, err = capfd.readouterr() assert not err assert bool(out) != quiet @pytest.mark.parametrize("enabled", [False, True]) def test_safety_skips(tmpdir, enabled: bool) -> None: tmpdir.join("victim.py").write("# ...") toxdir = tmpdir.mkdir(".tox") toxdir.join("verysafe.py").write("# ...") tmpdir.mkdir("_build").mkdir("python3.7").join("importantsystemlibrary.py").write("# ...") tmpdir.mkdir(".pants.d").join("pants.py").write("import os") if enabled: config = Config(directory=str(tmpdir)) else: config = Config(skip=[], directory=str(tmpdir)) skipped: list[str] = [] broken: list[str] = [] codes = [str(tmpdir)] files.find(codes, config, skipped, broken) # if enabled files within nested unsafe directories should be skipped file_names = { os.path.relpath(f, str(tmpdir)) for f in files.find([str(tmpdir)], config, skipped, broken) } if enabled: assert file_names == {"victim.py"} assert len(skipped) == 3 else: assert file_names == { os.sep.join((".tox", "verysafe.py")), os.sep.join(("_build", "python3.7", "importantsystemlibrary.py")), os.sep.join((".pants.d", "pants.py")), "victim.py", } assert not skipped # directly pointing to files within unsafe directories shouldn't skip them either way file_names = { os.path.relpath(f, str(toxdir)) for f in files.find([str(toxdir)], Config(directory=str(toxdir)), skipped, broken) } assert file_names == {"verysafe.py"} @pytest.mark.parametrize( "skip_glob_assert", [ ([], 0, {os.sep.join(("code", "file.py"))}), (["**/*.py"], 1, set()), (["*/code/*.py"], 1, set()), ], ) def test_skip_glob(tmpdir, skip_glob_assert: tuple[list[str], int, set[str]]) -> None: skip_glob, skipped_count, file_names_expected = skip_glob_assert base_dir = tmpdir.mkdir("build") code_dir = base_dir.mkdir("code") code_dir.join("file.py").write("import os") config = Config(skip_glob=skip_glob, directory=str(base_dir)) skipped: list[str] = [] broken: list[str] = [] file_names = { os.path.relpath(f, str(base_dir)) for f in files.find([str(base_dir)], config, skipped, broken) } assert len(skipped) == skipped_count assert file_names == file_names_expected def test_broken(tmpdir) -> None: base_dir = tmpdir.mkdir("broken") config = Config(directory=str(base_dir)) skipped: list[str] = [] broken: list[str] = [] file_names = { os.path.relpath(f, str(base_dir)) for f in files.find(["not-exist"], config, skipped, broken) } assert len(broken) == 1 assert file_names == set() def test_comments_not_removed_issue_576() -> None: test_input = ( "import distutils\n" "# this comment is important and should not be removed\n" "from sys import api_version as api_version\n" ) assert isort.code(test_input) == test_input def test_reverse_relative_imports_issue_417() -> None: test_input = ( "from . import ipsum\n" "from . import lorem\n" "from .dolor import consecteur\n" "from .sit import apidiscing\n" "from .. import donec\n" "from .. import euismod\n" "from ..mi import iaculis\n" "from ..nec import tempor\n" "from ... import diam\n" "from ... import dui\n" "from ...eu import dignissim\n" "from ...ex import metus\n" ) assert isort.code(test_input, force_single_line=True, reverse_relative=True) == test_input def test_inconsistent_relative_imports_issue_577() -> None: test_input = ( "from ... import diam\n" "from ... import dui\n" "from ...eu import dignissim\n" "from ...ex import metus\n" "from .. import donec\n" "from .. import euismod\n" "from ..mi import iaculis\n" "from ..nec import tempor\n" "from . import ipsum\n" "from . import lorem\n" "from .dolor import consecteur\n" "from .sit import apidiscing\n" ) assert isort.code(test_input, force_single_line=True) == test_input def test_unwrap_issue_762() -> None: test_input = "from os.path \\\nimport (join, split)\n" assert isort.code(test_input) == "from os.path import join, split\n" test_input = "from os.\\\n path import (join, split)" assert isort.code(test_input) == "from os.path import join, split\n" def test_multiple_as_imports() -> None: test_input = "from a import b as b\nfrom a import b as bb\nfrom a import b as bb_\n" test_output = isort.code(test_input) assert test_output == test_input test_output = isort.code(test_input, combine_as_imports=True) assert test_output == "from a import b as b, b as bb, b as bb_\n" test_output = isort.code(test_input) assert test_output == test_input test_output = isort.code(code=test_input, combine_as_imports=True) assert test_output == "from a import b as b, b as bb, b as bb_\n" test_input = ( "from a import b\nfrom a import b as b\nfrom a import b as bb\nfrom a import b as bb_\n" ) test_output = isort.code(test_input) assert test_output == test_input test_output = isort.code(code=test_input, combine_as_imports=True) assert test_output == "from a import b, b as b, b as bb, b as bb_\n" test_input = ( "from a import b as e\nfrom a import b as c\nfrom a import b\nfrom a import b as f\n" ) test_output = isort.code(test_input) assert ( test_output == "from a import b\nfrom a import b as c\nfrom a import b as e\nfrom a import b as f\n" ) test_output = isort.code(code=test_input, no_inline_sort=True) assert ( test_output == "from a import b\nfrom a import b as c\nfrom a import b as e\nfrom a import b as f\n" ) test_output = isort.code(code=test_input, combine_as_imports=True) assert test_output == "from a import b, b as c, b as e, b as f\n" test_output = isort.code(code=test_input, combine_as_imports=True, no_inline_sort=True) assert test_output == "from a import b, b as e, b as c, b as f\n" test_input = "import a as a\nimport a as aa\nimport a as aa_\n" test_output = isort.code(code=test_input, combine_as_imports=True) assert test_output == test_input assert test_output == "import a as a\nimport a as aa\nimport a as aa_\n" test_output = isort.code(code=test_input, combine_as_imports=True) assert test_output == test_input def test_all_imports_from_single_module() -> None: test_input = ( "import a\n" "from a import *\n" "from a import b as d\n" "from a import z, x, y\n" "from a import b\n" "from a import w, i as j\n" "from a import b as c, g as h\n" "from a import e as f\n" ) test_output = isort.code( code=test_input, combine_star=False, combine_as_imports=False, force_single_line=False, no_inline_sort=False, ) assert test_output == ( "import a\n" "from a import *\n" "from a import b\n" "from a import b as c\n" "from a import b as d\n" "from a import e as f\n" "from a import g as h\n" "from a import i as j\n" "from a import w, x, y, z\n" ) test_input = ( "import a\nfrom a import *\nfrom a import z, x, y\nfrom a import b\nfrom a import w\n" ) test_output = isort.code( code=test_input, combine_star=True, combine_as_imports=False, force_single_line=False, no_inline_sort=False, ) assert test_output == "import a\nfrom a import *\n" test_input += """ from a import b as c from a import b as d from a import e as f from a import g as h from a import i as j """ test_output = isort.code( code=test_input, combine_star=False, combine_as_imports=True, force_single_line=False, no_inline_sort=False, ) assert test_output == ( "import a\n" "from a import *\n" "from a import b, b as c, b as d, e as f, g as h, i as j, w, x, y, z\n" ) test_output = isort.code( code=test_input, combine_star=False, combine_as_imports=False, force_single_line=True, no_inline_sort=False, ) assert test_output == ( "import a\n" "from a import *\n" "from a import b\n" "from a import b as c\n" "from a import b as d\n" "from a import e as f\n" "from a import g as h\n" "from a import i as j\n" "from a import w\n" "from a import x\n" "from a import y\n" "from a import z\n" ) test_input = ( "import a\n" "from a import *\n" "from a import b\n" "from a import b as d\n" "from a import b as c\n" "from a import z, x, y, w\n" "from a import i as j\n" "from a import g as h\n" "from a import e as f\n" ) test_output = isort.code( code=test_input, combine_star=False, combine_as_imports=False, force_single_line=False, no_inline_sort=True, ) assert test_output == ( "import a\n" "from a import *\n" "from a import b\n" "from a import b as c\n" "from a import b as d\n" "from a import z, x, y, w\n" "from a import i as j\n" "from a import g as h\n" "from a import e as f\n" ) test_input = ( "import a\nfrom a import *\nfrom a import z, x, y\nfrom a import b\nfrom a import w\n" ) test_output = isort.code( code=test_input, combine_star=True, combine_as_imports=True, force_single_line=False, no_inline_sort=False, ) assert test_output == "import a\nfrom a import *\n" test_output = isort.code( code=test_input, combine_star=True, combine_as_imports=False, force_single_line=True, no_inline_sort=False, ) assert test_output == "import a\nfrom a import *\n" test_output = isort.code( code=test_input, combine_star=True, combine_as_imports=False, force_single_line=False, no_inline_sort=True, ) assert test_output == "import a\nfrom a import *\n" test_output = isort.code( code=test_input, combine_star=False, combine_as_imports=True, force_single_line=True, no_inline_sort=False, ) assert test_output == ( "import a\n" "from a import *\n" "from a import b\n" "from a import w\n" "from a import x\n" "from a import y\n" "from a import z\n" ) test_input = ( "import a\n" "from a import *\n" "from a import b\n" "from a import b as d\n" "from a import b as c\n" "from a import z, x, y, w\n" "from a import i as j\n" "from a import g as h\n" "from a import e as f\n" ) test_output = isort.code( code=test_input, combine_star=False, combine_as_imports=True, force_single_line=False, no_inline_sort=True, ) assert test_output == ( "import a\n" "from a import *\n" "from a import b, b as d, b as c, z, x, y, w, i as j, g as h, e as f\n" ) test_output = isort.code( code=test_input, combine_star=False, combine_as_imports=False, force_single_line=True, no_inline_sort=True, ) assert test_output == ( "import a\n" "from a import *\n" "from a import b\n" "from a import b as c\n" "from a import b as d\n" "from a import e as f\n" "from a import g as h\n" "from a import i as j\n" "from a import w\n" "from a import x\n" "from a import y\n" "from a import z\n" ) test_input = ( "import a\nfrom a import *\nfrom a import z, x, y\nfrom a import b\nfrom a import w\n" ) test_output = isort.code( code=test_input, combine_star=True, combine_as_imports=True, force_single_line=True, no_inline_sort=False, ) assert test_output == "import a\nfrom a import *\n" def test_noqa_issue_679() -> None: """Test to ensure that NOQA notation is being observed as expected if honor_noqa is set to `True` """ test_input = """ import os import requestsss import zed # NOQA import ujson # NOQA import foo""" test_output = """ import os import foo import requestsss import ujson # NOQA import zed # NOQA """ test_output_honor_noqa = """ import os import foo import requestsss import zed # NOQA import ujson # NOQA """ assert isort.code(test_input) == test_output assert isort.code(test_input.lower()) == test_output.lower() assert isort.code(test_input, honor_noqa=True) == test_output_honor_noqa assert isort.code(test_input.lower(), honor_noqa=True) == test_output_honor_noqa.lower() def test_extract_multiline_output_wrap_setting_from_a_config_file(tmp_path: Path) -> None: editorconfig_contents = ["root = true", " [*.py]", "multi_line_output = 5"] config_file = tmp_path / ".editorconfig" config_file.write_text("\n".join(editorconfig_contents)) config = Config(settings_path=str(tmp_path)) assert config.multi_line_output == WrapModes.VERTICAL_GRID_GROUPED def test_ensure_support_for_non_typed_but_cased_alphabetic_sort_issue_890() -> None: test_input = ( "from pkg import BALL\n" "from pkg import RC\n" "from pkg import Action\n" "from pkg import Bacoo\n" "from pkg import RCNewCode\n" "from pkg import actual\n" "from pkg import rc\n" "from pkg import recorder\n" ) expected_output = ( "from pkg import Action\n" "from pkg import BALL\n" "from pkg import Bacoo\n" "from pkg import RC\n" "from pkg import RCNewCode\n" "from pkg import actual\n" "from pkg import rc\n" "from pkg import recorder\n" ) assert ( isort.code( code=test_input, case_sensitive=True, order_by_type=False, force_single_line=True ) == expected_output ) def test_to_ensure_empty_line_not_added_to_file_start_issue_889() -> None: test_input = "# comment\nimport os\n# comment2\nimport sys\n" assert isort.code(test_input) == test_input @pytest.mark.parametrize("ws", ["", " ", "\t", "\f", "\n"]) def test_to_ensure_correctly_handling_of_whitespace_only_issue_811( ws: str, capsys: pytest.CaptureFixture[str], ) -> None: test_input = f'import os\nimport sys\n\n{ws}\ndef my_function():\n print("hi")\n' assert isort.check_code(test_input, ignore_whitespace=True) out, err = capsys.readouterr() assert out == "" assert err == "" def test_standard_library_deprecates_user_issue_778() -> None: test_input = "import os\n\nimport user\n" assert isort.code(test_input) == test_input @pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_settings_path_skip_issue_909(tmpdir) -> None: base_dir = tmpdir.mkdir("project") config_dir = base_dir.mkdir("conf") config_dir.join(".isort.cfg").write( "[isort]\nskip =\n file_to_be_skipped.py\nskip_glob =\n *glob_skip*\n" ) base_dir.join("file_glob_skip.py").write( 'import os\n\nprint("Hello World")\n\nimport sys\nimport os\n' ) base_dir.join("file_to_be_skipped.py").write( 'import os\n\nprint("Hello World")\nimport sys\nimport os\n' ) test_run_directory = os.getcwd() os.chdir(str(base_dir)) with pytest.raises(subprocess.CalledProcessError): # without the settings path provided: the command should not skip & identify errors subprocess.run(["isort", ".", "--check-only"], check=True) result = subprocess.run( ["isort", ".", "--check-only", "--settings-path=conf/.isort.cfg"], stdout=subprocess.PIPE, check=True, ) os.chdir(str(test_run_directory)) assert b"skipped 2" in result.stdout.lower() @pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_skip_paths_issue_938(tmpdir) -> None: base_dir = tmpdir.mkdir("project") config_dir = base_dir.mkdir("conf") config_dir.join(".isort.cfg").write( "[isort]\n" "line_length = 88\n" "multi_line_output = 4\n" "lines_after_imports = 2\n" "skip_glob =\n" " migrations/**.py\n" ) base_dir.join("dont_skip.py").write('import os\n\nprint("Hello World")\nimport sys\n') migrations_dir = base_dir.mkdir("migrations") migrations_dir.join("file_glob_skip.py").write( 'import os\n\nprint("Hello World")\n\nimport sys\n' ) test_run_directory = os.getcwd() os.chdir(str(base_dir)) result = subprocess.run( ["isort", "dont_skip.py", "migrations/file_glob_skip.py"], stdout=subprocess.PIPE, check=True, ) os.chdir(str(test_run_directory)) assert b"skipped" not in result.stdout.lower() os.chdir(str(base_dir)) result = subprocess.run( [ "isort", "--filter-files", "--settings-path=conf/.isort.cfg", "dont_skip.py", "migrations/file_glob_skip.py", ], stdout=subprocess.PIPE, check=True, ) os.chdir(str(test_run_directory)) assert b"skipped 1" in result.stdout.lower() def test_failing_file_check_916() -> None: test_input = ( "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n" ) expected_output = ( "#!/usr/bin/env python\n" "# -*- coding: utf-8 -*-\n" "# FUTURE\n" "from __future__ import unicode_literals\n" ) settings = { "import_heading_future": "FUTURE", "sections": ["FUTURE", "STDLIB", "NORDIGEN", "FIRSTPARTY", "THIRDPARTY", "LOCALFOLDER"], "indent": " ", "multi_line_output": 3, "lines_after_imports": 2, } # type: dict[str, Any] assert isort.code(test_input, **settings) == expected_output assert isort.code(expected_output, **settings) == expected_output assert api.check_code_string(expected_output, **settings) def test_import_heading_issue_905() -> None: config = { "import_heading_stdlib": "Standard library imports", "import_heading_thirdparty": "Third party imports", "import_heading_firstparty": "Local imports", "known_third_party": ["numpy"], "known_first_party": ["oklib"], } # type: dict[str, Any] test_input = ( "# Standard library imports\n" "from os import path as osp\n" "\n" "# Third party imports\n" "import numpy as np\n" "\n" "# Local imports\n" "from oklib.plot_ok import imagesc\n" ) assert isort.code(test_input, **config) == test_input def test_isort_keeps_comments_issue_691() -> None: test_input = ( "import os\n" "# This will make sure the app is always imported when\n" "# Django starts so that shared_task will use this app.\n" "from .celery import app as celery_app # noqa\n" "\n" "PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))\n" "\n" "def path(*subdirectories):\n" " return os.path.join(PROJECT_DIR, *subdirectories)\n" ) expected_output = ( "import os\n" "\n" "# This will make sure the app is always imported when\n" "# Django starts so that shared_task will use this app.\n" "from .celery import app as celery_app # noqa\n" "\n" "PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))\n" "\n" "def path(*subdirectories):\n" " return os.path.join(PROJECT_DIR, *subdirectories)\n" ) assert isort.code(test_input) == expected_output def test_isort_multiline_with_tab_issue_1714() -> None: test_input = "from sys \\ \n\timport version\nprint(version)\n" expected_output = "from sys import version\n\nprint(version)\n" assert isort.code(test_input) == expected_output def test_isort_ensures_blank_line_between_import_and_comment() -> None: config = { "ensure_newline_before_comments": True, "lines_between_sections": 0, "known_one": ["one"], "known_two": ["two"], "known_three": ["three"], "known_four": ["four"], "sections": [ "FUTURE", "STDLIB", "FIRSTPARTY", "THIRDPARTY", "LOCALFOLDER", "ONE", "TWO", "THREE", "FOUR", ], } # type: dict[str, Any] test_input = ( "import os\n" "# noinspection PyUnresolvedReferences\n" "import one.a\n" "# noinspection PyUnresolvedReferences\n" "import one.b\n" "# noinspection PyUnresolvedReferences\n" "import two.a as aa\n" "# noinspection PyUnresolvedReferences\n" "import two.b as bb\n" "# noinspection PyUnresolvedReferences\n" "from three.a import a\n" "# noinspection PyUnresolvedReferences\n" "from three.b import b\n" "# noinspection PyUnresolvedReferences\n" "from four.a import a as aa\n" "# noinspection PyUnresolvedReferences\n" "from four.b import b as bb\n" ) expected_output = ( "import os\n" "\n" "# noinspection PyUnresolvedReferences\n" "import one.a\n" "\n" "# noinspection PyUnresolvedReferences\n" "import one.b\n" "\n" "# noinspection PyUnresolvedReferences\n" "import two.a as aa\n" "\n" "# noinspection PyUnresolvedReferences\n" "import two.b as bb\n" "\n" "# noinspection PyUnresolvedReferences\n" "from three.a import a\n" "\n" "# noinspection PyUnresolvedReferences\n" "from three.b import b\n" "\n" "# noinspection PyUnresolvedReferences\n" "from four.a import a as aa\n" "\n" "# noinspection PyUnresolvedReferences\n" "from four.b import b as bb\n" ) assert isort.code(test_input, **config) == expected_output def test_pyi_formatting_issue_942(tmpdir) -> None: test_input = "import os\n\n\ndef my_method():\n" expected_py_output = test_input.splitlines() expected_pyi_output = "import os\n\ndef my_method():\n".splitlines() assert isort.code(test_input).splitlines() == expected_py_output assert isort.code(test_input, extension="pyi").splitlines() == expected_pyi_output source_py = tmpdir.join("source.py") source_py.write(test_input) assert ( isort.code(code=Path(source_py).read_text(), file_path=Path(source_py)).splitlines() == expected_py_output ) source_pyi = tmpdir.join("source.pyi") source_pyi.write(test_input) assert ( isort.code( code=Path(source_pyi).read_text(), extension="pyi", file_path=Path(source_pyi) ).splitlines() == expected_pyi_output ) # Ensure it works for direct file API as well (see: issue #1284) source_pyi = tmpdir.join("source.pyi") source_pyi.write(test_input) api.sort_file(Path(source_pyi)) assert source_pyi.read().splitlines() == expected_pyi_output def test_move_class_issue_751() -> None: test_input = ( "# -*- coding: utf-8 -*-" "\n" "# Define your item pipelines here" "#" "# Don't forget to add your pipeline to the ITEM_PIPELINES setting" "# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html" "from datetime import datetime" "from .items import WeiboMblogItem" "\n" "class WeiboMblogPipeline(object):" " def process_item(self, item, spider):" " if isinstance(item, WeiboMblogItem):" " item = self._process_item(item, spider)" " return item" "\n" " def _process_item(self, item, spider):" " item['inserted_at'] = datetime.now()" " return item" "\n" ) assert isort.code(test_input) == test_input def test_python_version() -> None: # test that the py_version can be added as flag args = parse_args(["--py=27"]) assert args["py_version"] == "27" args = parse_args(["--python-version=3"]) assert args["py_version"] == "3" test_input = "import os\n\nimport user\n" assert isort.code(test_input, py_version="3") == test_input # user is part of the standard library in python 2 output_python_2 = "import os\nimport user\n" assert isort.code(test_input, py_version="27") == output_python_2 test_input = "import os\nimport xml" print(isort.code(test_input, py_version="all")) def test_isort_with_single_character_import() -> None: """Tests to ensure isort handles single capatilized single character imports as class objects by default See Issue #376: https://github.com/pycqa/isort/issues/376 """ test_input = "from django.db.models import CASCADE, SET_NULL, Q\n" assert isort.code(test_input) == test_input def test_isort_nested_imports() -> None: """Ensure imports in a nested block get sorted correctly""" test_input = """ def import_test(): import sys import os # my imports from . import def from . import abc return True """ assert ( isort.code(test_input) == """ def import_test(): import os import sys # my imports from . import abc, def return True """ ) def test_isort_off() -> None: """Test that isort can be turned on and off at will using comments""" test_input = """import os # isort: off import sys import os # isort: on from . import local """ assert isort.code(test_input) == test_input def test_isort_split() -> None: """Test the ability to split isort import sections""" test_input = """import os import sys # isort: split import os import sys """ assert isort.code(test_input) == test_input test_input = """import c import b # isort: split import a import c """ assert isort.code(test_input) == test_input def test_comment_look_alike(): """Test to ensure isort will handle what looks like a single line comment at the end of a multi-line comment. """ test_input = ''' """This is a multi-line comment ending with what appears to be a single line comment # Single Line Comment""" import sys import os ''' assert ( isort.code(test_input) == ''' """This is a multi-line comment ending with what appears to be a single line comment # Single Line Comment""" import os import sys ''' ) def test_cimport_support(): """Test to ensure cimports (Cython style imports) work""" test_input = """ import os import sys import cython import platform import traceback import time import types import re import copy import inspect # used by JavascriptBindings.__SetObjectMethods() import urllib import json import datetime import random if sys.version_info.major == 2: import urlparse else: from urllib import parse as urlparse if sys.version_info.major == 2: from urllib import pathname2url as urllib_pathname2url else: from urllib.request import pathname2url as urllib_pathname2url from cpython.version cimport PY_MAJOR_VERSION import weakref # We should allow multiple string types: str, unicode, bytes. # PyToCefString() can handle them all. # Important: # If you set it to basestring, Cython will accept exactly(!) # str/unicode in Py2 and str in Py3. This won't work in Py3 # as we might want to pass bytes as well. Also it will # reject string subtypes, so using it in publi API functions # would be a bad idea. ctypedef object py_string # You can't use "void" along with cpdef function returning None, it is planned to be # added to Cython in the future, creating this virtual type temporarily. If you # change it later to "void" then don't forget to add "except *". ctypedef object py_void ctypedef long WindowHandle from cpython cimport PyLong_FromVoidPtr from cpython cimport bool as py_bool from libcpp cimport bool as cpp_bool from libcpp.map cimport map as cpp_map from multimap cimport multimap as cpp_multimap from libcpp.pair cimport pair as cpp_pair from libcpp.vector cimport vector as cpp_vector from libcpp.string cimport string as cpp_string from wstring cimport wstring as cpp_wstring from libc.string cimport strlen from libc.string cimport memcpy # preincrement and dereference must be "as" otherwise not seen. from cython.operator cimport preincrement as preinc, dereference as deref # from cython.operator cimport address as addr # Address of an c++ object? from libc.stdlib cimport calloc, malloc, free from libc.stdlib cimport atoi # When pyx file cimports * from a pxd file and that pxd cimports * from another pxd # then these names will be visible in pyx file. # Circular imports are allowed in form "cimport ...", but won't work if you do # "from ... cimport *", this is important to know in pxd files. from libc.stdint cimport uint64_t from libc.stdint cimport uintptr_t cimport ctime IF UNAME_SYSNAME == "Windows": from windows cimport * from dpi_aware_win cimport * ELIF UNAME_SYSNAME == "Linux": from linux cimport * ELIF UNAME_SYSNAME == "Darwin": from mac cimport * from cpp_utils cimport * from task cimport * from cef_string cimport * cdef extern from *: ctypedef CefString ConstCefString "const CefString" from cef_types_wrappers cimport * from cef_task cimport * from cef_runnable cimport * from cef_platform cimport * from cef_ptr cimport * from cef_app cimport * from cef_browser cimport * cimport cef_browser_static from cef_client cimport * from client_handler cimport * from cef_frame cimport * # cannot cimport *, that would cause name conflicts with constants. cimport cef_types ctypedef cef_types.cef_paint_element_type_t PaintElementType ctypedef cef_types.cef_jsdialog_type_t JSDialogType from cef_types cimport CefKeyEvent from cef_types cimport CefMouseEvent from cef_types cimport CefScreenInfo # cannot cimport *, name conflicts IF UNAME_SYSNAME == "Windows": cimport cef_types_win ELIF UNAME_SYSNAME == "Darwin": cimport cef_types_mac ELIF UNAME_SYSNAME == "Linux": cimport cef_types_linux from cef_time cimport * from cef_drag cimport * import os IF CEF_VERSION == 1: from cef_v8 cimport * cimport cef_v8_static cimport cef_v8_stack_trace from v8function_handler cimport * from cef_request_cef1 cimport * from cef_web_urlrequest_cef1 cimport * cimport cef_web_urlrequest_static_cef1 from web_request_client_cef1 cimport * from cef_stream cimport * cimport cef_stream_static from cef_response_cef1 cimport * from cef_stream cimport * from cef_content_filter cimport * from content_filter_handler cimport * from cef_download_handler cimport * from download_handler cimport * from cef_cookie_cef1 cimport * cimport cef_cookie_manager_namespace from cookie_visitor cimport * from cef_render_handler cimport * from cef_drag_data cimport * IF UNAME_SYSNAME == "Windows": IF CEF_VERSION == 1: from http_authentication cimport * IF CEF_VERSION == 3: from cef_values cimport * from cefpython_app cimport * from cef_process_message cimport * from cef_web_plugin_cef3 cimport * from cef_request_handler_cef3 cimport * from cef_request_cef3 cimport * from cef_cookie_cef3 cimport * from cef_string_visitor cimport * cimport cef_cookie_manager_namespace from cookie_visitor cimport * from string_visitor cimport * from cef_callback_cef3 cimport * from cef_response_cef3 cimport * from cef_resource_handler_cef3 cimport * from resource_handler_cef3 cimport * from cef_urlrequest_cef3 cimport * from web_request_client_cef3 cimport * from cef_command_line cimport * from cef_request_context cimport * from cef_request_context_handler cimport * from request_context_handler cimport * from cef_jsdialog_handler cimport * """ expected_output = """ import copy import datetime import inspect # used by JavascriptBindings.__SetObjectMethods() import json import os import platform import random import re import sys import time import traceback import types import urllib import cython if sys.version_info.major == 2: import urlparse else: from urllib import parse as urlparse if sys.version_info.major == 2: from urllib import pathname2url as urllib_pathname2url else: from urllib.request import pathname2url as urllib_pathname2url from cpython.version cimport PY_MAJOR_VERSION import weakref # We should allow multiple string types: str, unicode, bytes. # PyToCefString() can handle them all. # Important: # If you set it to basestring, Cython will accept exactly(!) # str/unicode in Py2 and str in Py3. This won't work in Py3 # as we might want to pass bytes as well. Also it will # reject string subtypes, so using it in publi API functions # would be a bad idea. ctypedef object py_string # You can't use "void" along with cpdef function returning None, it is planned to be # added to Cython in the future, creating this virtual type temporarily. If you # change it later to "void" then don't forget to add "except *". ctypedef object py_void ctypedef long WindowHandle cimport ctime from cpython cimport PyLong_FromVoidPtr from cpython cimport bool as py_bool # preincrement and dereference must be "as" otherwise not seen. from cython.operator cimport dereference as deref from cython.operator cimport preincrement as preinc from libc.stdint cimport uint64_t, uintptr_t from libc.stdlib cimport atoi, calloc, free, malloc from libc.string cimport memcpy, strlen from libcpp cimport bool as cpp_bool from libcpp.map cimport map as cpp_map from libcpp.pair cimport pair as cpp_pair from libcpp.string cimport string as cpp_string from libcpp.vector cimport vector as cpp_vector from multimap cimport multimap as cpp_multimap from wstring cimport wstring as cpp_wstring # from cython.operator cimport address as addr # Address of an c++ object? # When pyx file cimports * from a pxd file and that pxd cimports * from another pxd # then these names will be visible in pyx file. # Circular imports are allowed in form "cimport ...", but won't work if you do # "from ... cimport *", this is important to know in pxd files. IF UNAME_SYSNAME == "Windows": from dpi_aware_win cimport * from windows cimport * ELIF UNAME_SYSNAME == "Linux": from linux cimport * ELIF UNAME_SYSNAME == "Darwin": from mac cimport * from cef_string cimport * from cpp_utils cimport * from task cimport * cdef extern from *: ctypedef CefString ConstCefString "const CefString" cimport cef_browser_static # cannot cimport *, that would cause name conflicts with constants. cimport cef_types from cef_app cimport * from cef_browser cimport * from cef_client cimport * from cef_frame cimport * from cef_platform cimport * from cef_ptr cimport * from cef_runnable cimport * from cef_task cimport * from cef_types_wrappers cimport * from client_handler cimport * ctypedef cef_types.cef_paint_element_type_t PaintElementType ctypedef cef_types.cef_jsdialog_type_t JSDialogType from cef_types cimport CefKeyEvent, CefMouseEvent, CefScreenInfo # cannot cimport *, name conflicts IF UNAME_SYSNAME == "Windows": cimport cef_types_win ELIF UNAME_SYSNAME == "Darwin": cimport cef_types_mac ELIF UNAME_SYSNAME == "Linux": cimport cef_types_linux from cef_drag cimport * from cef_time cimport * import os IF CEF_VERSION == 1: cimport cef_cookie_manager_namespace cimport cef_stream_static cimport cef_v8_stack_trace cimport cef_v8_static cimport cef_web_urlrequest_static_cef1 from cef_content_filter cimport * from cef_cookie_cef1 cimport * from cef_download_handler cimport * from cef_drag_data cimport * from cef_render_handler cimport * from cef_request_cef1 cimport * from cef_response_cef1 cimport * from cef_stream cimport * from cef_v8 cimport * from cef_web_urlrequest_cef1 cimport * from content_filter_handler cimport * from cookie_visitor cimport * from download_handler cimport * from v8function_handler cimport * from web_request_client_cef1 cimport * IF UNAME_SYSNAME == "Windows": IF CEF_VERSION == 1: from http_authentication cimport * IF CEF_VERSION == 3: cimport cef_cookie_manager_namespace from cef_callback_cef3 cimport * from cef_command_line cimport * from cef_cookie_cef3 cimport * from cef_jsdialog_handler cimport * from cef_process_message cimport * from cef_request_cef3 cimport * from cef_request_context cimport * from cef_request_context_handler cimport * from cef_request_handler_cef3 cimport * from cef_resource_handler_cef3 cimport * from cef_response_cef3 cimport * from cef_string_visitor cimport * from cef_urlrequest_cef3 cimport * from cef_values cimport * from cef_web_plugin_cef3 cimport * from cefpython_app cimport * from cookie_visitor cimport * from request_context_handler cimport * from resource_handler_cef3 cimport * from string_visitor cimport * from web_request_client_cef3 cimport * """ assert isort.code(test_input).strip() == expected_output.strip() def test_cdef_support(): assert ( isort.code( code=""" from cpython.version cimport PY_MAJOR_VERSION cdef extern from *: ctypedef CefString ConstCefString "const CefString" """ ) == """ from cpython.version cimport PY_MAJOR_VERSION cdef extern from *: ctypedef CefString ConstCefString "const CefString" """ ) assert ( isort.code( code=""" from cpython.version cimport PY_MAJOR_VERSION cpdef extern from *: ctypedef CefString ConstCefString "const CefString" """ ) == """ from cpython.version cimport PY_MAJOR_VERSION cpdef extern from *: ctypedef CefString ConstCefString "const CefString" """ ) def test_top_level_import_order() -> None: test_input = ( "from rest_framework import throttling, viewsets\n" "from rest_framework.authentication import TokenAuthentication\n" ) assert isort.code(test_input, force_sort_within_sections=True) == test_input def test_noqa_issue_1065() -> None: test_input = """ # # USER SIGNALS # from flask_login import user_logged_in, user_logged_out # noqa from flask_security.signals import ( # noqa password_changed as user_reset_password, # noqa user_confirmed, # noqa user_registered, # noqa ) # noqa from flask_principal import identity_changed as user_identity_changed # noqa """ expected_output = """ # # USER SIGNALS # from flask_login import user_logged_in, user_logged_out # noqa from flask_principal import identity_changed as user_identity_changed # noqa from flask_security.signals import password_changed as user_reset_password # noqa from flask_security.signals import user_confirmed # noqa from flask_security.signals import user_registered # noqa """ assert isort.code(test_input, line_length=100) == expected_output test_input_2 = """ # # USER SIGNALS # from flask_login import user_logged_in, user_logged_out # noqa from flask_security.signals import ( password_changed as user_reset_password, # noqa user_confirmed, # noqa user_registered, # noqa ) from flask_principal import identity_changed as user_identity_changed # noqa """ assert isort.code(test_input_2, line_length=100) == expected_output def test_single_line_exclusions(): test_input = """ # start comment from os import path, system from typing import List, TypeVar """ expected_output = """ # start comment from os import path from os import system from typing import List, TypeVar """ assert ( isort.code(code=test_input, force_single_line=True, single_line_exclusions=("typing",)) == expected_output ) def test_nested_comment_handling(): test_input = """ if True: import foo # comment for bar """ assert isort.code(test_input) == test_input # If comments appear inside import sections at same indentation they can be re-arranged. test_input = """ if True: import sys # os import import os """ expected_output = """ if True: # os import import os import sys """ assert isort.code(test_input) == expected_output # Comments shouldn't be unexpectedly rearranged. See issue #1090. test_input = """ def f(): # comment 1 # comment 2 # comment 3 # comment 4 from a import a from b import b """ assert isort.code(test_input) == test_input # Whitespace shouldn't be adjusted for nested imports. See issue #1090. test_input = """ try: import foo except ImportError: import bar """ assert isort.code(test_input) == test_input def test_comments_top_of_file(): """Test to ensure comments at top of file are correctly handled. See issue #1091.""" test_input = """# comment 1 # comment 2 # comment 3 # comment 4 from foo import * """ assert isort.code(test_input) == test_input test_input = """# -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html from datetime import datetime from .items import WeiboMblogItem class WeiboMblogPipeline(object): def process_item(self, item, spider): if isinstance(item, WeiboMblogItem): item = self._process_item(item, spider) return item def _process_item(self, item, spider): item['inserted_at'] = datetime.now() return item """ assert isort.code(test_input) == test_input def test_multiple_aliases(): """Test to ensure isort will retain multiple aliases. See issue #1037""" test_input = """import datetime import datetime as datetime import datetime as dt import datetime as dt2 """ assert isort.code(code=test_input) == test_input def test_parens_in_comment(): """Test to ensure isort can handle parens placed in comments. See issue #1103""" test_input = """from foo import ( # (some text in brackets) bar, ) """ expected_output = "from foo import bar # (some text in brackets)\n" assert isort.code(test_input) == expected_output def test_as_imports_mixed(): """Test to ensure as imports can be mixed with non as. See issue #908""" test_input = """from datetime import datetime import datetime.datetime as dt """ expected_output = """import datetime.datetime as dt from datetime import datetime """ assert isort.code(test_input) == expected_output def test_no_sections_with_future(): """Test to ensure no_sections works with future. See issue #807""" test_input = """from __future__ import print_function import os """ expected_output = """from __future__ import print_function import os """ assert isort.code(test_input, no_sections=True) == expected_output def test_no_sections_with_as_import(): """Test to ensure no_sections work with as import.""" test_input = """import oumpy as np import sympy """ assert isort.code(test_input, no_sections=True) == test_input def test_no_lines_too_long(): """Test to ensure no lines end up too long. See issue: #1015""" test_input = """from package1 import first_package, \ second_package from package2 import \\ first_package """ expected_output = """from package1 import \\ first_package, \\ second_package from package2 import \\ first_package """ assert isort.code(test_input, line_length=25, multi_line_output=2) == expected_output def test_python_future_category(): """Test to ensure a manual python future category will work as needed to install aliases see: Issue #1005 """ test_input = """from __future__ import absolute_import # my future comment from future import standard_library standard_library.install_aliases() import os import re import time from logging.handlers import SysLogHandler from builtins import len, object, str from katlogger import log_formatter, log_rollover from .query_elastic import QueryElastic """ expected_output = """from __future__ import absolute_import # my future comment from future import standard_library standard_library.install_aliases() # Python Standard Library import os import re import time from builtins import len, object, str from logging.handlers import SysLogHandler # CAM Packages from katlogger import log_formatter, log_rollover # Explicitly Local from .query_elastic import QueryElastic """ assert ( isort.code( code=test_input, force_grid_wrap=False, include_trailing_comma=True, indent=4, line_length=90, multi_line_output=3, lines_between_types=1, sections=[ "FUTURE_LIBRARY", "FUTURE_THIRDPARTY", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER", ], import_heading_stdlib="Python Standard Library", import_heading_thirdparty="Third Library", import_heading_firstparty="CAM Packages", import_heading_localfolder="Explicitly Local", known_first_party=["katlogger"], known_future_thirdparty=["future"], ) == expected_output ) def test_combine_star_comments_above(): input_text = """from __future__ import absolute_import # my future comment from future import *, something """ expected_output = """from __future__ import absolute_import # my future comment from future import * """ assert isort.code(input_text, combine_star=True) == expected_output def test_only_sections() -> None: """Test to ensure that the within sections relative position of imports are maintained""" test_input = ( "import sys\n" "\n" "import numpy as np\n" "\n" "import os\n" "from os import path as ospath\n" "\n" "import pandas as pd\n" "\n" "import math\n" "import .views\n" "from collections import defaultdict\n" ) assert ( isort.code(test_input, only_sections=True) == ( "import sys\n" "import os\n" "import math\n" "from os import path as ospath\n" "from collections import defaultdict\n" "\n" "import numpy as np\n" "import pandas as pd\n" "\n" "import .views\n" ) == isort.code(test_input, only_sections=True, force_single_line=True) ) # test to ensure that from_imports remain intact with only_sections test_input = "from foo import b, a, c\n" assert isort.code(test_input, only_sections=True) == test_input def test_combine_straight_imports() -> None: """Tests to ensure that combine_straight_imports works correctly""" test_input = "import os\nimport sys\n# this is a comment\nimport math # inline comment\n" assert isort.code(test_input, combine_straight_imports=True) == ( "# this is a comment\nimport math, os, sys # inline comment\n" ) # test to ensure that combine_straight_import works with only_sections test_input = "import sys, os\nimport a\nimport math\nimport b\n" assert isort.code(test_input, combine_straight_imports=True, only_sections=True) == ( "import sys, os, math\n\nimport a, b\n" ) def test_find_imports_in_code() -> None: test_input = ( "import first_straight\n" "\n" "import second_straight\n" "from first_from import first_from_function_1, first_from_function_2\n" "import bad_name as good_name\n" "from parent.some_bad_defs import bad_name_1 as ok_name_1, bad_name_2 as ok_name_2\n" "\n" "# isort: list\n" "__all__ = ['b', 'c', 'a']\n" "\n" "def bla():\n" " import needed_in_bla_2\n" "\n" "\n" " import needed_in_bla\n" " pass" "\n" "def bla_bla():\n" " import needed_in_bla_bla\n" "\n" " #import not_really_an_import\n" " pass" "\n" "import needed_in_end\n" ) identified_imports = list(map(str, api.find_imports_in_code(test_input))) assert identified_imports == [ ":1 import first_straight", ":3 import second_straight", ":4 from first_from import first_from_function_1", ":4 from first_from import first_from_function_2", ":5 import bad_name as good_name", ":6 from parent.some_bad_defs import bad_name_1 as ok_name_1", ":6 from parent.some_bad_defs import bad_name_2 as ok_name_2", ":12 indented import needed_in_bla_2", ":15 indented import needed_in_bla", ":18 indented import needed_in_bla_bla", ":22 import needed_in_end", ] def test_find_imports_in_stream() -> None: """Ensure that find_imports_in_stream can work with nonseekable streams like STDOUT""" class NonSeekableTestStream(StringIO): def seek(self, offset: int, whence: int = os.SEEK_SET, /) -> int: raise OSError("Stream is not seekable") def seekable(self): return False test_input = NonSeekableTestStream("import m2\nimport m1\nnot_import = 7") identified_imports = list(map(str, api.find_imports_in_stream(test_input))) assert identified_imports == [":1 import m2", ":2 import m1"] def test_split_on_trailing_comma() -> None: test_input = "from lib import (a, b, c,)" expected_output = """from lib import ( a, b, c, ) """ output = isort.code(test_input, split_on_trailing_comma=True) assert output == expected_output output = isort.code(expected_output, split_on_trailing_comma=True) assert output == expected_output def test_split_on_trailing_comma_wih_as() -> None: test_input = "from lib import (a as b,)" expected_output = """from lib import a as b """ output = isort.code(test_input, split_on_trailing_comma=True) assert output == expected_output output = isort.code(expected_output, split_on_trailing_comma=True) assert output == expected_output def test_split_on_trailing_comma_stable_with_mixed_as_imports_issue_2352() -> None: """Regression test for https://github.com/PyCQA/isort/issues/2352. isort should produce stable output when a module has both aliased and non-aliased imports with `include_trailing_comma` enabled. A single non-aliased import remaining after the aliased imports should not be force-wrapped into multi-line format merely because a trailing comma was recorded for the module. """ # Test case from the original issue report test_input = """from django.db.models.sql.compiler import ( SQLAggregateCompiler, SQLCompiler, SQLDeleteCompiler, ) from django.db.models.sql.compiler import SQLInsertCompiler as BaseSQLInsertCompiler from django.db.models.sql.compiler import SQLUpdateCompiler """ output = isort.code( test_input, include_trailing_comma=True, split_on_trailing_comma=True, line_length=88, ) assert output == test_input expected_output_with_combine = """from django.db.models.sql.compiler import ( SQLAggregateCompiler, SQLCompiler, SQLDeleteCompiler, SQLInsertCompiler as BaseSQLInsertCompiler, SQLUpdateCompiler, ) """ output = isort.code( test_input, include_trailing_comma=True, split_on_trailing_comma=True, line_length=88, combine_as_imports=True, ) assert ( output == expected_output_with_combine == isort.code( expected_output_with_combine, include_trailing_comma=True, split_on_trailing_comma=True, line_length=88, combine_as_imports=True, ) ) def test_split_on_trailing_comma_stable_with_use_parentheses_2352() -> None: """Regression test for https://github.com/PyCQA/isort/issues/2352. isort should produce stable output when a module has both aliased and non-aliased imports with `include_trailing_comma` and `use_parentheses` enabled. A single non-aliased import remaining after the aliased imports should not be force-wrapped into multi-line format merely because a trailing comma was recorded for the module. """ test_input = r""" from another_file_aaaaaaaaaaaaaaaaaa import \ SOME_THING_CONSTANT as SOME_THING_CONSTANT_RENAMED from another_file_aaaaaaaaaaaaaaaaaa import some_func """ expected_output = """ from another_file_aaaaaaaaaaaaaaaaaa import ( SOME_THING_CONSTANT as SOME_THING_CONSTANT_RENAMED, ) from another_file_aaaaaaaaaaaaaaaaaa import some_func """ output = isort.code( test_input, include_trailing_comma=True, use_parentheses=True, multi_line_output=3, ) assert output == expected_output # Run again on actual output assert output == isort.code( output, include_trailing_comma=True, use_parentheses=True, multi_line_output=3, ) def test_infinite_loop_in_unmatched_parenthesis() -> None: test_input = "from os import (" # ensure a syntax error is raised for unmatched parenthesis with pytest.raises(ExistingSyntaxErrors): isort.code(test_input) test_input = """from os import ( path, walk ) """ # ensure other cases are handled correctly assert isort.code(test_input) == "from os import path, walk\n" def test_reexport() -> None: test_input = """__all__ = ('foo', 'bar') """ expd_output = """__all__ = ("bar", "foo") """ assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output def test_reexport_leave_alone_if_not_enabled() -> None: test_input = """__all__ = ('foo', 'bar') """ assert isort.code(test_input) == test_input def test_reexport_multiline() -> None: test_input = """__all__ = ( 'foo', 'bar', ) """ expd_output = """__all__ = ("bar", "foo") """ assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output def test_reexport_list() -> None: test_input = """__all__ = ['foo', 'bar'] """ expd_output = """__all__ = ["bar", "foo"] """ assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output def test_reexport_set() -> None: test_input = """__all__ = {'foo', 'bar'} """ expd_output = """__all__ = {"bar", "foo"} """ assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output def test_reexport_bare() -> None: test_input = """__all__ = 'foo', 'bar' """ expd_output = """__all__ = ("bar", "foo") """ assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output def test_reexport_no_spaces() -> None: test_input = """__all__=('foo', 'bar') """ expd_output = """__all__ = ("bar", "foo") """ assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output def test_reexport_not_first_line() -> None: test_input = """import random __all__ = ('foo', 'bar') """ expd_output = """import random __all__ = ("bar", "foo") """ assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output def test_reexport_not_last_line() -> None: test_input = """__all__ = ('foo', 'bar') meme = "rickroll" """ expd_output = """__all__ = ("bar", "foo") meme = "rickroll" """ assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output def test_reexport_multiline_import() -> None: test_input = """from m import ( bar, foo, ) __all__ = [ "foo", "bar", ] """ expd_output = """from m import bar, foo __all__ = ["bar", "foo"] """ assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output def test_reexport_multiline_in_center() -> None: test_input = """from m import ( bar, foo, ) __all__ = [ "foo", "bar", ] test """ expd_output = """from m import bar, foo __all__ = ["bar", "foo"] test """ assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output def test_reexport_multiline_long_rollback() -> None: test_input = """from m import foo, bar __all__ = [ "foo", "bar", ] test """ expd_output = """from m import bar, foo __all__ = ["bar", "foo"] test """ assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output def test_noqa_multiline_hanging_indent() -> None: test_input = ( "from aaaaaaa import bbbbbbbbbbbbbbbbb, ccccccccccccccccccc, dddddddddddddddd" "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee, \\\n" " fffffffffffffffffffff # noqa: E402\n" "\n" "print(fffffffffffffffffffff)\n" "print(dddddddddddddddd)\n" ) output = isort.code(test_input, line_length=120, multi_line_output=WrapModes.HANGING_INDENT) assert output == test_input @pytest.mark.parametrize( ( "actual_imports", "expected_sort", ), [ ( ("from __future__ import annotations\nimport __future__\n"), ("from __future__ import annotations\nimport __future__\n"), ), ( ( "import __future__\n\n" "from __future__ import generator_stop\n" "from __future__ import annotations\n\n" "from concurrent.futures import ThreadPoolExecutor\n\n" ), ( "from __future__ import annotations, generator_stop\n" "import __future__\n\n" "from concurrent.futures import ThreadPoolExecutor\n" ), ), ( ("from __future__ import annotations\nimport sys\n"), ("from __future__ import annotations\n\nimport sys\n"), ), (("import __future__\nimport sys\n"), ("import __future__\n\nimport sys\n")), ( ("import __future__ as future\nfrom __future__ import annotations\n"), ("from __future__ import annotations\nimport __future__ as future\n"), ), ( ("# Copyright 2026\nimport __future__\nfrom __future__ import annotations\n"), ("# Copyright 2026\nfrom __future__ import annotations\nimport __future__\n"), ), ( ("import __future__\nfrom __future__ import *\n"), ("from __future__ import *\nimport __future__\n"), ), ( ( "from __future__ import annotations\n" "import __future__\n" "from __future__ import annotations\n" ), ("from __future__ import annotations\nimport __future__\n"), ), ( ( "import sys\n" "from __future__ import annotations\n" "import os\n" "import __future__\n" "from pathlib import Path\n" ), ( "from __future__ import annotations\n" "import __future__\n\n" "import os\n" "import sys\n" "from pathlib import Path\n" ), ), ], ) def test_dunder_future_import(actual_imports: str, expected_sort: str) -> None: output = isort.code(actual_imports) assert output == expected_sort def test_builtin_modules() -> None: test_input = ( "from typing import Iterable, Iterator, TypeVar, cast\n" "\n" "from _collections_abc import dict_items, dict_keys, dict_values\n" "from python_none_objects import NoneIterable\n" "from _codecs import encode\n" "from nt import environ\n" ) test_output = ( "from _codecs import encode\n" "from _collections_abc import dict_items, dict_keys, dict_values\n" "from nt import environ\n" "from typing import Iterable, Iterator, TypeVar, cast\n" "\n" "from python_none_objects import NoneIterable\n" ) assert isort.code(test_input) == test_output isort-9.0.1/tests/unit/test_lazy_imports.py0000644000000000000000000001235113615410400016071 0ustar00from isort import Config, parse from .utils import isort_test class TestParsing: """Verify that ``parse.file_contents`` correctly identifies lazy imports.""" def test_lazy_straight_import_is_stored_in_lazy_straight_bucket(self): result = parse.file_contents("lazy import ast\n", Config()) assert "ast" in result.imports["STDLIB"]["lazy_straight"] assert "ast" not in result.imports["STDLIB"]["straight"] def test_lazy_from_import_is_stored_in_lazy_from_bucket(self): result = parse.file_contents("lazy from dataclasses import dataclass\n", Config()) assert "dataclasses" in result.imports["STDLIB"]["lazy_from"] assert "dataclasses" not in result.imports["STDLIB"]["from"] def test_eager_imports_are_stored_in_regular_buckets(self): result = parse.file_contents("import os\nfrom pathlib import Path\n", Config()) assert "os" in result.imports["STDLIB"]["straight"] assert "pathlib" in result.imports["STDLIB"]["from"] def test_lazy_imports_are_placed_in_correct_section(self): """Lazy imports must be placed in the same section as their eager counterparts.""" result = parse.file_contents( "lazy import ast\nlazy import requests\n", Config(known_third_party=["requests"]), ) assert "ast" in result.imports["STDLIB"]["lazy_straight"] assert "requests" in result.imports["THIRDPARTY"]["lazy_straight"] def test_lazy_straight_imports_come_after_eager(): """lazy import lines follow all eager import lines within the section.""" isort_test("lazy import ast\nimport os\n", "import os\nlazy import ast\n") def test_lazy_from_imports_come_after_eager(): """lazy from ... import lines follow all eager import lines within the section.""" isort_test( "lazy from pathlib import Path\nfrom collections import defaultdict\n", "from collections import defaultdict\nlazy from pathlib import Path\n", ) def test_lazy_straight_sorted_alphabetically(): """Multiple lazy straight imports are sorted alphabetically.""" isort_test("lazy import shutil\nlazy import ast\n", "lazy import ast\nlazy import shutil\n") def test_lazy_from_sorted_alphabetically(): """Multiple lazy from imports are sorted alphabetically by module name.""" isort_test( "lazy from pathlib import Path\nlazy from dataclasses import dataclass\n", "lazy from dataclasses import dataclass\nlazy from pathlib import Path\n", ) def test_ruff_reference_example(): """Reproduce the canonical example from the ruff issue tracker. See https://github.com/astral-sh/ruff/issues/21305. """ unsorted = ( "lazy from dataclasses import dataclass\n" "import json\n" "import os\n" "import subprocess\n" "from collections import defaultdict\n" "from pathlib import Path\n" "from typing import Final\n" "lazy import ast\n" "lazy import shutil\n" ) expected = ( "import json\n" "import os\n" "import subprocess\n" "from collections import defaultdict\n" "from pathlib import Path\n" "from typing import Final\n" "lazy import ast\n" "lazy import shutil\n" "lazy from dataclasses import dataclass\n" ) isort_test(unsorted, expected) def test_lazy_imports_appear_after_eager_in_each_section_independently(): """Each section gets its own eager-first / lazy-last grouping.""" unsorted = "lazy import requests\nlazy import ast\nimport os\nimport requests\n" expected = "import os\nlazy import ast\n\nimport requests\nlazy import requests\n" isort_test(unsorted, expected, known_third_party=["requests"]) def test_lazy_import_with_alias(): """``lazy import X as Y`` is supported and sorted correctly.""" isort_test( "import os\nlazy import numpy as np\n", "import os\n\nlazy import numpy as np\n", known_third_party=["numpy"], ) def test_lazy_from_import_multiple_names(): """``lazy from X import a, b`` is supported and names are sorted alphabetically.""" isort_test("lazy from typing import List, Dict\n", "lazy from typing import Dict, List\n") def test_no_sections_mode_with_lazy_imports(): """lazy imports are supported in no_sections mode.""" isort_test( "lazy import ast\nimport os\n", "import os\nlazy import ast\n", no_sections=True, ) def test_from_first_option_respected_for_lazy(): """When from_first=True, lazy from imports precede lazy straight but appear after eager.""" isort_test( "import pathlib\nlazy import ast\nlazy from dataclasses import dataclass\n", "import pathlib\nlazy from dataclasses import dataclass\nlazy import ast\n", from_first=True, ) def test_force_sort_within_sections_applies_to_lazy(): """force_sort_within_sections toggles lazy import ordering behavior.""" isort_test( "lazy import zlib\nlazy from ast import parse\n", "lazy import zlib\nlazy from ast import parse\n", force_sort_within_sections=False, ) isort_test( "lazy import zlib\nlazy from ast import parse\n", "lazy from ast import parse\nlazy import zlib\n", force_sort_within_sections=True, ) isort-9.0.1/tests/unit/test_literal.py0000644000000000000000000001072213615410400014771 0ustar00import pytest import isort import isort.literal from isort import exceptions from isort.settings import Config def test_value_mismatch(): with pytest.raises(exceptions.LiteralSortTypeMismatch): isort.literal.assignment("x = [1, 2, 3]", "set", "py") def test_invalid_syntax(): with pytest.raises(exceptions.LiteralParsingFailure): isort.literal.assignment("x = [1, 2, 3", "list", "py") def test_invalid_sort_type(): with pytest.raises(ValueError, match=r"Trying to sort using an undefined sort_type. Defined"): isort.literal.assignment("x = [1, 2, 3", "tuple-list-not-exist", "py") def test_value_assignment_assignments(): assert isort.literal.assignment("b = 1\na = 2\n", "assignments", "py") == "a = 2\nb = 1\n" def test_assignments_invalid_section(): with pytest.raises(exceptions.AssignmentsFormatMismatch): isort.literal.assignment("\n\nx = 1\nx++", "assignments", "py") def test_list_uses_double_quotes(): assert isort.literal.assignment("x = ['b', 'a']", "list", "py") == 'x = ["a", "b"]' def test_list_preserves_bracket_type_tuple(): assert isort.literal.assignment("x = ('b', 'a')", "tuple", "py") == 'x = ("a", "b")' def test_single_element_tuple_keeps_trailing_comma(): assert isort.literal.assignment("x = ('a',)", "tuple", "py") == 'x = ("a",)' def test_set_bracket_and_quotes(): assert isort.literal.assignment("x = {'b', 'a'}", "set", "py") == 'x = {"a", "b"}' def test_long_list_wraps_vertical_hanging_indent(): code = "__all__ = ['" + "', '".join(f"name_{i:02d}" for i in range(12)) + "']" result = isort.literal.assignment(code, "list", "py", config=Config(profile="black")) expected = "__all__ = [\n" + "".join(f' "name_{i:02d}",\n' for i in range(12)) + "]" assert result == expected def test_wrap_without_trailing_comma(): code = "__all__ = ['" + "', '".join(f"name_{i:02d}" for i in range(12)) + "']" result = isort.literal.assignment( code, "list", "py", config=Config(line_length=20, include_trailing_comma=False) ) assert result.endswith('"name_11"\n]') # no trailing comma before the closing bracket def test_quote_fallback_for_embedded_quote(): # value containing a double quote but no single quote -> single quotes (black rule) assert isort.literal.assignment("x = ['a\"b']", "list", "py") == "x = ['a\"b']" def test_black_quote_prefers_double_quotes(): assert isort.literal._black_quote("foo") == '"foo"' def test_black_quote_uses_single_when_value_has_double_quote_only(): assert isort.literal._black_quote('a"b') == "'a\"b'" def test_black_quote_escapes_double_when_value_has_both_quotes(): assert isort.literal._black_quote("a'b\"c") == '"a\'b\\"c"' def test_black_quote_falls_back_to_repr_for_control_chars(): assert isort.literal._black_quote("a\tb") == repr("a\tb") def test_list_of_non_strings_uses_repr(): assert isort.literal.assignment("x = [3, 1, 2]", "list", "py") == "x = [1, 2, 3]" def test_unique_tuple_dedupes_and_sorts(): assert isort.literal.assignment("x = ('b', 'a', 'a')", "unique-tuple", "py") == 'x = ("a", "b")' def test_single_element_tuple_wraps_and_keeps_comma(): long_name = "z" * 100 result = isort.literal.assignment(f"x = ('{long_name}',)", "tuple", "py") assert result == f'x = (\n "{long_name}",\n)' def test_dict_sorts_by_value_with_double_quotes(): # dict is now formatted config-aware too (no more pprint); sorted by value assert ( isort.literal.assignment("x = {'a': 'z', 'b': 'y'}", "dict", "py") == 'x = {"b": "y", "a": "z"}' ) def test_dict_non_string_values_use_repr(): assert isort.literal.assignment("x = {'b': 2, 'a': 1}", "dict", "py") == 'x = {"a": 1, "b": 2}' def test_long_dict_wraps_vertical_hanging_indent(): pairs = {f"key_{i:02d}": f"value_{i:02d}" for i in range(10)} code = "d = {" + ", ".join(f"'{k}': '{v}'" for k, v in pairs.items()) + "}" result = isort.literal.assignment(code, "dict", "py", config=Config(profile="black")) expected = ( "d = {\n" + "".join(f' "key_{i:02d}": "value_{i:02d}",\n' for i in range(10)) + "}" ) assert result == expected def test_value_assignment_dict(): assert ( isort.literal.assignment("x = {3: 'c', 1: 'a', 2: 'b'}", "dict", "py") == 'x = {1: "a", 2: "b", 3: "c"}' ) def test_value_assignment_unique_tuple(): assert ( isort.literal.assignment("x = ('a', 'b', '1', '1')", "unique-tuple", "py") == 'x = ("1", "a", "b")' ) isort-9.0.1/tests/unit/test_main.py0000644000000000000000000010374213615410400014266 0ustar00import json import os import pathlib import shutil import subprocess from datetime import datetime import unittest.mock import pytest from hypothesis import given from hypothesis import strategies as st from isort import main from isort._version import _VERSION_STRING, _IS_COMPILED from isort.exceptions import InvalidSettingsPath from isort.settings import DEFAULT_CONFIG, Config from .utils import as_stream from io import BytesIO, TextIOWrapper from typing import TYPE_CHECKING, Any if TYPE_CHECKING: WrapModes: Any else: from isort.wrap_modes import WrapModes @given( file_name=st.text(), config=st.builds(Config), check=st.booleans(), ask_to_apply=st.booleans(), write_to_stdout=st.booleans(), ) def test_fuzz_sort_imports(file_name, config, check, ask_to_apply, write_to_stdout): main.sort_imports( file_name=file_name, config=config, check=check, ask_to_apply=ask_to_apply, write_to_stdout=write_to_stdout, ) def test_sort_imports(tmpdir): tmp_file = tmpdir.join("file.py") tmp_file.write("import os, sys\n") assert main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore # noqa main.sort_imports(str(tmp_file), DEFAULT_CONFIG) assert not main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore # noqa skip_config = Config(skip=["file.py"]) assert main.sort_imports( # type: ignore str(tmp_file), config=skip_config, check=True, disregard_skip=False ).skipped assert main.sort_imports(str(tmp_file), config=skip_config, disregard_skip=False).skipped # type: ignore # noqa @pytest.mark.skipif(reason="Can't use these mocks in mypyc-compiled code.", condition=_IS_COMPILED) def test_sort_imports_error_handling(tmpdir, capsys): tmp_file = tmpdir.join("file.py") tmp_file.write("import os, sys\n") with ( unittest.mock.patch( "isort.core.process", side_effect=IndexError("Example unhandled exception") ), pytest.raises(IndexError), ): main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore # noqa _, error = capsys.readouterr() assert "Unrecoverable exception thrown when parsing" in error def test_parse_args(): assert main.parse_args([]) == {} assert main.parse_args(["--multi-line", "1"]) == {"multi_line_output": WrapModes.VERTICAL} assert main.parse_args(["--multi-line", "GRID"]) == {"multi_line_output": WrapModes.GRID} assert main.parse_args(["--dont-order-by-type"]) == {"order_by_type": False} assert main.parse_args(["--dt"]) == {"order_by_type": False} assert main.parse_args(["--only-sections"]) == {"only_sections": True} assert main.parse_args(["--os"]) == {"only_sections": True} assert main.parse_args(["--om"]) == {"only_modified": True} assert main.parse_args(["--only-modified"]) == {"only_modified": True} assert main.parse_args(["--csi"]) == {"combine_straight_imports": True} assert main.parse_args(["--combine-straight-imports"]) == {"combine_straight_imports": True} assert main.parse_args(["--dont-follow-links"]) == {"follow_links": False} assert main.parse_args(["--overwrite-in-place"]) == {"overwrite_in_place": True} assert main.parse_args(["--from-first"]) == {"from_first": True} assert main.parse_args(["--resolve-all-configs"]) == {"resolve_all_configs": True} def test_ascii_art(capsys): main.main(["--version"]) out, error = capsys.readouterr() assert ( out == f""" _ _ (_) ___ ___ _ __| |_ | |/ _/ / _ \\/ '__ _/ | |\\__ \\/\\_\\/| | | |_ |_|\\___/\\___/\\_/ \\_/ isort your imports, so you don't have to. VERSION {_VERSION_STRING} """ ) assert error == "" def test_preconvert(): assert main._preconvert(frozenset([1, 1, 2])) == [1, 2] assert main._preconvert(WrapModes.GRID) == "GRID" assert main._preconvert(main._preconvert) == "_preconvert" with pytest.raises(TypeError): main._preconvert(datetime.now()) def test_show_files(capsys, tmpdir): tmpdir.join("a.py").write("import a") tmpdir.join("b.py").write("import b") # show files should list the files isort would sort main.main([str(tmpdir), "--show-files"]) out, error = capsys.readouterr() assert "a.py" in out assert "b.py" in out assert not error # can not be used for stream with pytest.raises(SystemExit): main.main(["-", "--show-files"]) # can not be used with show-config with pytest.raises(SystemExit): main.main([str(tmpdir), "--show-files", "--show-config"]) def test_missing_default_section(tmpdir): config_file = tmpdir.join(".isort.cfg") config_file.write( """ [settings] sections=MADEUP """ ) python_file = tmpdir.join("file.py") python_file.write("import os") with pytest.raises(SystemExit): main.main([str(python_file)]) def test_ran_against_root(): with pytest.raises(SystemExit): main.main(["/"]) def test_config_root_interaction_with_resolve_all_configs(tmpdir): python_file = tmpdir.join("file.py") python_file.write("import os\n") with pytest.raises(SystemExit) as exc_info: main.main([str(python_file), "--config-root", str(tmpdir)]) assert "--resolve-all-configs" in str(exc_info.value) main.main([str(python_file), "--config-root", str(tmpdir), "--resolve-all-configs"]) def test_main(capsys, tmpdir): base_args = [ "--sp", str(tmpdir), "--virtual-env", str(tmpdir), "--src-path", str(tmpdir), ] tmpdir.mkdir(".git") # If nothing is passed in the quick guide is returned without erroring main.main([]) out, error = capsys.readouterr() assert main.QUICK_GUIDE in out assert not error # If no files are passed in but arguments are the quick guide is returned, alongside an error. with pytest.raises(SystemExit): main.main(base_args) out, error = capsys.readouterr() assert main.QUICK_GUIDE in out # Unless the config is requested, in which case it will be returned alone as JSON main.main([*base_args, "--show-config"]) out, error = capsys.readouterr() returned_config = json.loads(out) assert returned_config assert returned_config["virtual_env"] == str(tmpdir) # This should work even if settings path is not provided main.main([*base_args[2:], "--show-config"]) out, error = capsys.readouterr() assert json.loads(out)["virtual_env"] == str(tmpdir) # This should raise an error if an invalid settings path is provided with pytest.raises(InvalidSettingsPath): main.main( [ *base_args[2:], "--show-config", "--settings-path", "/random-root-folder-that-cant-exist-right?", ] ) # Should be able to set settings path to a file config_file = tmpdir.join(".isort.cfg") config_file.write( """ [settings] profile=hug verbose=true """ ) config_args = ["--settings-path", str(config_file)] main.main( [ *config_args, "--virtual-env", "/random-root-folder-that-cant-exist-right?", "--show-config", ] ) out, error = capsys.readouterr() assert json.loads(out)["profile"] == "hug" # Should be able to stream in content to sort input_content = TextIOWrapper( BytesIO( b""" import b import a """ ) ) main.main([*config_args, "-"], stdin=input_content) out, error = capsys.readouterr() assert ( out == f""" else-type place_module for b returned {DEFAULT_CONFIG.default_section} else-type place_module for a returned {DEFAULT_CONFIG.default_section} import a import b """ ) # Should be able to stream diff input_content = TextIOWrapper( BytesIO( b""" import b import a """ ) ) main.main([*config_args, "-", "--diff"], stdin=input_content) out, error = capsys.readouterr() assert not error assert "+" in out assert "-" in out assert "import a" in out assert "import b" in out # check should work with stdin input_content_check = TextIOWrapper( BytesIO( b""" import b import a """ ) ) with pytest.raises(SystemExit): main.main([*config_args, "-", "--check-only"], stdin=input_content_check) out, error = capsys.readouterr() assert error == "ERROR: Imports are incorrectly sorted and/or formatted.\n" # Should be able to run with just a file python_file = tmpdir.join("has_imports.py") python_file.write( """ import b import a """ ) main.main([str(python_file), "--filter-files", "--verbose"]) assert python_file.read().lstrip() == "import a\nimport b\n" # Add a file to skip should_skip = tmpdir.join("should_skip.py") should_skip.write("import nothing") main.main( [ str(python_file), str(should_skip), "--filter-files", "--verbose", "--skip", str(should_skip), ] ) # Should raise a system exit if check only, with broken file python_file.write( """ import b import a """ ) with pytest.raises(SystemExit): main.main( [ str(python_file), str(should_skip), "--filter-files", "--verbose", "--check-only", "--skip", str(should_skip), ] ) # Should have same behavior if full directory is skipped with pytest.raises(SystemExit): main.main( [str(tmpdir), "--filter-files", "--verbose", "--check-only", "--skip", str(should_skip)] ) # Nested files should be skipped without needing --filter-files nested_file = tmpdir.mkdir("nested_dir").join("skip.py") nested_file.write("import b;import a") python_file.write( """ import a import b """ ) main.main([str(tmpdir), "--extend-skip", "skip.py", "--check"]) # without filter options passed in should successfully sort files main.main([str(python_file), str(should_skip), "--verbose", "--atomic"]) # Should raise a system exit if all passed path is broken with pytest.raises(SystemExit): main.main(["not-exist", "--check-only"]) # Should not raise system exit if any of passed path is not broken main.main([str(python_file), "not-exist", "--verbose", "--check-only"]) out, error = capsys.readouterr() assert "Broken" in out def test_isort_filename_overrides(tmpdir, capsys): """Tests isorts available approaches for overriding filename and extension based behavior""" input_text = """ import b import a def function(): pass """ def build_input_content(): return as_stream(input_text) main.main(["-"], stdin=build_input_content()) out, error = capsys.readouterr() assert not error assert out == ( """ import a import b def function(): pass """ ) # if file is skipped it should output unchanged. main.main( ["-", "--filename", "x.py", "--skip", "x.py", "--filter-files"], stdin=build_input_content(), ) out, error = capsys.readouterr() assert not error assert out == ( """ import b import a def function(): pass """ ) main.main(["-", "--ext-format", "pyi"], stdin=build_input_content()) out, error = capsys.readouterr() assert not error assert out == ( """ import a import b def function(): pass """ ) tmp_file = tmpdir.join("tmp.pyi") tmp_file.write_text(input_text, encoding="utf8") main.main(["-", "--filename", str(tmp_file)], stdin=build_input_content()) out, error = capsys.readouterr() assert not error assert out == ( """ import a import b def function(): pass """ ) # setting a filename override when file is passed in as non-stream is not supported. with pytest.raises(SystemExit): main.main([str(tmp_file), "--filename", str(tmp_file)], stdin=build_input_content()) def test_isort_float_to_top_overrides(tmpdir, capsys): """Tests isorts supports overriding float to top from CLI""" test_input = """ import b def function(): pass import a """ config_file = tmpdir.join(".isort.cfg") config_file.write( """ [settings] float_to_top=True """ ) python_file = tmpdir.join("file.py") python_file.write(test_input) main.main([str(python_file)]) out, error = capsys.readouterr() assert not error assert "Fixing" in out assert python_file.read_text(encoding="utf8") == ( """ import a import b def function(): pass """ ) python_file.write(test_input) main.main([str(python_file), "--dont-float-to-top"]) _, error = capsys.readouterr() assert not error assert python_file.read_text(encoding="utf8") == test_input with pytest.raises(SystemExit): main.main([str(python_file), "--float-to-top", "--dont-float-to-top"]) def test_isort_with_stdin(capsys): # ensures that isort sorts stdin without any flags input_content = as_stream( """ import b import a """ ) main.main(["-"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ import a import b """ ) input_content_from = as_stream( """ import c import b from a import z, y, x """ ) main.main(["-"], stdin=input_content_from) out, error = capsys.readouterr() assert out == ( """ import b import c from a import x, y, z """ ) # ensures that isort correctly sorts stdin with --fas flag input_content = as_stream( """ import sys import pandas from z import abc from a import xyz """ ) main.main(["-", "--fas"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ from a import xyz from z import abc import pandas import sys """ ) # ensures that isort correctly sorts stdin with --fass flag input_content = as_stream( """ from a import Path, abc """ ) main.main(["-", "--fass"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ from a import abc, Path """ ) # ensures that isort correctly sorts stdin with --ff flag input_content = as_stream( """ import b from c import x from a import y """ ) main.main(["-", "--ff"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ from a import y from c import x import b """ ) # ensures that isort correctly sorts stdin with -fss flag input_content = as_stream( """ import b from a import a """ ) main.main(["-", "--fss"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ from a import a import b """ ) input_content = as_stream( """ import a from b import c """ ) main.main(["-", "--fss"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ import a from b import c """ ) # ensures that isort correctly sorts stdin with --ds flag input_content = as_stream( """ import sys import pandas import a """ ) main.main(["-", "--ds"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ import a import pandas import sys """ ) # ensures that isort correctly sorts stdin with --cs flag input_content = as_stream( """ from a import b from a import * """ ) main.main(["-", "--cs"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ from a import * """ ) # ensures that isort correctly sorts stdin with --ca flag input_content = as_stream( """ from a import x as X from a import y as Y """ ) main.main(["-", "--ca"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ from a import x as X, y as Y """ ) # ensures that isort works consistently with check and ws flags input_content = as_stream( """ import os import a import b """ ) main.main(["-", "--check-only", "--ws"], stdin=input_content) out, error = capsys.readouterr() assert not error # ensures that isort works consistently with check and diff flags input_content = as_stream( """ import b import a """ ) with pytest.raises(SystemExit): main.main(["-", "--check", "--diff"], stdin=input_content) out, error = capsys.readouterr() assert error assert "underlying stream is not seekable" not in error assert "underlying stream is not seekable" not in error # ensures that isort correctly sorts stdin with --ls flag input_content = as_stream( """ import abcdef import x """ ) main.main(["-", "--ls"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ import x import abcdef """ ) # ensures that isort correctly sorts stdin with --nis flag input_content = as_stream( """ from z import b, c, a """ ) main.main(["-", "--nis"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ from z import b, c, a """ ) # ensures that isort correctly sorts stdin with --sl flag input_content = as_stream( """ from z import b, c, a """ ) main.main(["-", "--sl"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ from z import a from z import b from z import c """ ) # ensures that isort correctly sorts stdin with --top flag input_content = as_stream( """ import os import sys """ ) main.main(["-", "--top", "sys"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ import sys import os """ ) # ensure that isort correctly sorts stdin with --os flag input_content = as_stream( """ import sys import os import z from a import b, e, c """ ) main.main(["-", "--os"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ import sys import os import z from a import b, e, c """ ) # ensures that only-modified flag works with stdin input_content = as_stream( """ import a import b """ ) main.main(["-", "--verbose", "--only-modified"], stdin=input_content) out, error = capsys.readouterr() assert "else-type place_module for a returned THIRDPARTY" not in out assert "else-type place_module for b returned THIRDPARTY" not in out # ensures that combine-straight-imports flag works with stdin input_content = as_stream( """ import a import b """ ) main.main(["-", "--combine-straight-imports"], stdin=input_content) out, error = capsys.readouterr() assert out == ( """ import a, b """ ) def test_unsupported_encodings(tmpdir, capsys): tmp_file = tmpdir.join("file.py") # fmt: off tmp_file.write_text( ''' # [syntax-error]\ # -*- coding: IBO-8859-1 -*- """ check correct unknown encoding declaration """ __revision__ = 'יייי' ''', encoding="utf8" ) # fmt: on # should throw an error if only unsupported encoding provided with pytest.raises(SystemExit): main.main([str(tmp_file)]) _, error = capsys.readouterr() assert "No valid encodings." in error # should not throw an error if at least one valid encoding found normal_file = tmpdir.join("file1.py") normal_file.write("import os\nimport sys") main.main([str(tmp_file), str(normal_file), "--verbose"]) _, error = capsys.readouterr() def test_stream_skip_file(tmpdir, capsys): input_with_skip = """ # isort: skip_file import b import a """ stream_with_skip = as_stream(input_with_skip) main.main(["-"], stdin=stream_with_skip) out, _ = capsys.readouterr() assert out == input_with_skip input_without_skip = input_with_skip.replace("isort: skip_file", "generic comment") stream_without_skip = as_stream(input_without_skip) main.main(["-"], stdin=stream_without_skip) out, _ = capsys.readouterr() assert ( out == """ # generic comment import a import b """ ) atomic_input_without_skip = input_with_skip.replace("isort: skip_file", "generic comment") stream_without_skip = as_stream(atomic_input_without_skip) main.main(["-", "--atomic"], stdin=stream_without_skip) out, _ = capsys.readouterr() assert ( out == """ # generic comment import a import b """ ) def test_only_modified_flag(tmpdir, capsys): # ensures there is no verbose output for correct files with only-modified flag file1 = tmpdir.join("file1.py") file1.write( """ import a import b """ ) file2 = tmpdir.join("file2.py") file2.write( """ import math import pandas as pd """ ) main.main([str(file1), str(file2), "--verbose", "--only-modified"]) out, error = capsys.readouterr() assert ( out == f""" _ _ (_) ___ ___ _ __| |_ | |/ _/ / _ \\/ '__ _/ | |\\__ \\/\\_\\/| | | |_ |_|\\___/\\___/\\_/ \\_/ isort your imports, so you don't have to. VERSION {_VERSION_STRING} """ ) assert not error # ensures that verbose output is only for modified file(s) with only-modified flag file3 = tmpdir.join("file3.py") file3.write( """ import sys import os """ ) main.main([str(file1), str(file2), str(file3), "--verbose", "--only-modified"]) out, error = capsys.readouterr() assert "else-type place_module for sys returned STDLIB" in out assert "else-type place_module for os returned STDLIB" in out assert "else-type place_module for math returned STDLIB" not in out assert "else-type place_module for pandas returned THIRDPARTY" not in out assert not error # ensures that the behaviour is consistent for check flag with only-modified flag main.main([str(file1), str(file2), "--check-only", "--verbose", "--only-modified"]) out, error = capsys.readouterr() assert ( out == f""" _ _ (_) ___ ___ _ __| |_ | |/ _/ / _ \\/ '__ _/ | |\\__ \\/\\_\\/| | | |_ |_|\\___/\\___/\\_/ \\_/ isort your imports, so you don't have to. VERSION {_VERSION_STRING} """ ) assert not error file4 = tmpdir.join("file4.py") file4.write( """ import sys import os """ ) with pytest.raises(SystemExit): main.main([str(file2), str(file4), "--check-only", "--verbose", "--only-modified"]) out, error = capsys.readouterr() assert "else-type place_module for sys returned STDLIB" in out assert "else-type place_module for os returned STDLIB" in out assert "else-type place_module for math returned STDLIB" not in out assert "else-type place_module for pandas returned THIRDPARTY" not in out def test_identify_imports_main(tmpdir, capsys): file_content = "import mod2\nimport mod2\na = 1\nimport mod1\n" some_file = tmpdir.join("some_file.py") some_file.write(file_content) file_imports = f"{some_file}:1 import mod2\n{some_file}:4 import mod1\n" file_imports_with_dupes = ( f"{some_file}:1 import mod2\n{some_file}:2 import mod2\n{some_file}:4 import mod1\n" ) main.identify_imports_main([str(some_file), "--unique"]) out, error = capsys.readouterr() assert out.replace("\r\n", "\n") == file_imports assert not error main.identify_imports_main([str(some_file)]) out, error = capsys.readouterr() assert out.replace("\r\n", "\n") == file_imports_with_dupes assert not error main.identify_imports_main(["-", "--unique"], stdin=as_stream(file_content)) out, error = capsys.readouterr() assert out.replace("\r\n", "\n") == file_imports.replace(str(some_file), "") main.identify_imports_main(["-"], stdin=as_stream(file_content)) out, error = capsys.readouterr() assert out.replace("\r\n", "\n") == file_imports_with_dupes.replace(str(some_file), "") main.identify_imports_main([str(tmpdir)]) main.identify_imports_main(["-", "--packages"], stdin=as_stream(file_content)) out, error = capsys.readouterr() assert len(out.split("\n")) == 6 main.identify_imports_main(["-", "--modules"], stdin=as_stream(file_content)) out, error = capsys.readouterr() assert len(out.split("\n")) == 3 main.identify_imports_main(["-", "--attributes"], stdin=as_stream(file_content)) out, error = capsys.readouterr() assert len(out.split("\n")) == 3 def test_gitignore(capsys, tmp_path: pathlib.Path): import_content = """ import b import a """ def main_check(args): try: main.main(args) except SystemExit: pass return capsys.readouterr() subprocess.run(["git", "init", str(tmp_path)]) python_file = tmp_path / "has_imports.py" python_file.write_text(import_content) (tmp_path / "no_imports.py").write_text("...") out, error = main_check([str(python_file), "--skip-gitignore", "--filter-files", "--check"]) assert "has_imports.py" in error assert "no_imports.py" not in error (tmp_path / ".gitignore").write_text("has_imports.py") out, error = main_check([str(python_file), "--check"]) assert "has_imports.py" in error assert "no_imports.py" not in error out, error = main_check([str(python_file), "--skip-gitignore", "--filter-files", "--check"]) assert "Skipped" in out # Should work with nested directories (tmp_path / "nested_dir").mkdir() (tmp_path / ".gitignore").write_text("nested_dir/has_imports.py") subfolder_file = tmp_path / "nested_dir/has_imports.py" subfolder_file.write_text(import_content) out, error = main_check([str(tmp_path), "--skip-gitignore", "--filter-files", "--check"]) assert "has_imports.py" in error assert "nested_dir/has_imports.py" not in error # Should work with relative path currentdir = os.getcwd() os.chdir(tmp_path) out, error = main_check([".", "--skip-gitignore", "--filter-files", "--check"]) assert "has_imports.py" in error assert "nested_dir/has_imports.py" not in error (tmp_path / ".gitignore").write_text( """ nested_dir/has_imports.py has_imports.py """ ) out, error = main_check([".", "--skip-gitignore", "--filter-files", "--check"]) assert "Skipped" in out os.chdir(currentdir) # Should work with multiple git projects shutil.rmtree(tmp_path / ".git") (tmp_path / ".gitignore").unlink() # git_project0 # | has_imports_ignored.py ignored # | has_imports.py should check git_project0 = tmp_path / "git_project0" git_project0.mkdir() subprocess.run(["git", "init", str(git_project0)]) (git_project0 / ".gitignore").write_text("has_imports_ignored.py") (git_project0 / "has_imports_ignored.py").write_text(import_content) (git_project0 / "has_imports.py").write_text(import_content) # git_project1 # | has_imports.py should check # | nested_dir # | has_imports_ignored.py ignored # | has_imports.py should check # | nested_dir_ignored ignored # | has_imports.py ignored from folder git_project1 = tmp_path / "git_project1" git_project1.mkdir() subprocess.run(["git", "init", str(git_project1)]) (git_project1 / ".gitignore").write_text( """ nested_dir/has_imports_ignored.py nested_dir_ignored """ ) (git_project1 / "has_imports.py").write_text(import_content) nested_dir = git_project1 / "nested_dir" nested_dir.mkdir() (nested_dir / "has_imports.py").write_text(import_content) (nested_dir / "has_imports_ignored.py").write_text(import_content) nested_ignored_dir = git_project1 / "nested_dir_ignored" nested_ignored_dir.mkdir() (nested_ignored_dir / "has_imports.py").write_text(import_content) should_check = [ "/has_imports.py", "/nested_dir/has_imports.py", "/git_project0/has_imports.py", "/git_project1/has_imports.py", "/git_project1/nested_dir/has_imports.py", ] out, error = main_check([str(tmp_path), "--skip-gitignore", "--filter-files", "--check"]) if os.name == "nt": should_check = [sc.replace("/", "\\") for sc in should_check] assert all(f"{tmp_path}{file}" in error for file in should_check) out, error = main_check([str(tmp_path), "--skip-gitignore", "--filter-files"]) assert all(f"{tmp_path}{file}" in out for file in should_check) # Should work when git project contains symlinks if os.name != "nt": (git_project0 / "has_imports_ignored.py").write_text(import_content) (git_project0 / "has_imports.py").write_text(import_content) (tmp_path / "has_imports.py").write_text(import_content) (tmp_path / "nested_dir" / "has_imports.py").write_text(import_content) (git_project0 / "ignore_link.py").symlink_to(tmp_path / "has_imports.py") (git_project0 / "ignore_link").symlink_to(tmp_path / "nested_dir") gitignore = git_project0 / ".gitignore" gitignore.write_text(gitignore.read_text() + "ignore_link.py\nignore_link") out, error = main_check( [str(git_project0), "--skip-gitignore", "--filter-files", "--check"] ) should_check = ["/git_project0/has_imports.py"] assert all(f"{tmp_path}{file}" in error for file in should_check) out, error = main_check([str(git_project0), "--skip-gitignore", "--filter-files"]) assert all(f"{tmp_path}{file}" in out for file in should_check) def test_multiple_configs(capsys, tmpdir): # Ensure that --resolve-all-configs flag resolves multiple configs correctly # and sorts files corresponding to their nearest config setup_cfg = """ [isort] from_first=True """ pyproject_toml = """ [tool.isort] no_inline_sort = \"True\" """ isort_cfg = """ [settings] force_single_line=True """ broken_isort_cfg = """ [iaort_confg] force_single_line=True """ dir1 = tmpdir / "subdir1" dir2 = tmpdir / "subdir2" dir3 = tmpdir / "subdir3" dir4 = tmpdir / "subdir4" dir1.mkdir() dir2.mkdir() dir3.mkdir() dir4.mkdir() setup_cfg_file = dir1 / "setup.cfg" setup_cfg_file.write_text(setup_cfg, "utf-8") pyproject_toml_file = dir2 / "pyproject.toml" pyproject_toml_file.write_text(pyproject_toml, "utf-8") isort_cfg_file = dir3 / ".isort.cfg" isort_cfg_file.write_text(isort_cfg, "utf-8") broken_isort_cfg_file = dir4 / ".isort.cfg" broken_isort_cfg_file.write_text(broken_isort_cfg, "utf-8") import_section = """ from a import y, z, x import b """ file1 = dir1 / "file1.py" file1.write_text(import_section, "utf-8") file2 = dir2 / "file2.py" file2.write_text(import_section, "utf-8") file3 = dir3 / "file3.py" file3.write_text(import_section, "utf-8") file4 = dir4 / "file4.py" file4.write_text(import_section, "utf-8") file5 = tmpdir / "file5.py" file5.write_text(import_section, "utf-8") main.main([str(tmpdir), "--resolve-all-configs", "--cr", str(tmpdir), "--verbose"]) out, _ = capsys.readouterr() assert f"{setup_cfg_file} used for file {file1}" in out assert f"{pyproject_toml_file} used for file {file2}" in out assert f"{isort_cfg_file} used for file {file3}" in out assert f"default used for file {file4}" in out assert f"default used for file {file5}" in out assert ( file1.read() == """ from a import x, y, z import b """ ) assert ( file2.read() == """ import b from a import y, z, x """ ) assert ( file3.read() == """ import b from a import x from a import y from a import z """ ) assert ( file4.read() == """ import b from a import x, y, z """ ) assert ( file5.read() == """ import b from a import x, y, z """ ) # Ensure that --resolve-all-config flags works with --check file6 = dir1 / "file6.py" file6.write( """ import b from a import x, y, z """ ) with pytest.raises(SystemExit): main.main([str(tmpdir), "--resolve-all-configs", "--cr", str(tmpdir), "--check"]) _, err = capsys.readouterr() assert f"{file6} Imports are incorrectly sorted and/or formatted" in err def test_multiple_src_paths(tmpdir, capsys): """ Ensure that isort has consistent behavior with multiple source paths """ tests_module = tmpdir / "tests" app_module = tmpdir / "app" tests_module.mkdir() app_module.mkdir() pyproject_toml = tmpdir / "pyproject.toml" pyproject_toml.write_text( """ [tool.isort] profile = "black" src_paths = ["app", "tests"] auto_identify_namespace_packages = false """, "utf-8", ) file = tmpdir / "file.py" file.write_text( """ from app.something import something from tests.something import something_else """, "utf-8", ) for _ in range(10): # To ensure isort has consistent results in multiple runs main.main([str(tmpdir), "--verbose"]) out, _ = capsys.readouterr() assert ( file.read() == """ from app.something import something from tests.something import something_else """ ) assert "from-type place_module for tests.something returned FIRSTPARTY" in out def test_cli_src_path_glob_pattern(tmpdir, capsys, monkeypatch): service_a_src = tmpdir.mkdir("service_a").mkdir("src") service_b_src = tmpdir.mkdir("service_b").mkdir("src") (tmpdir / "file.py").write_text("import os\n", "utf-8") monkeypatch.chdir(str(tmpdir)) main.main([".", "--src", "*/src/", "--show-config"]) out, _ = capsys.readouterr() config = json.loads(out) src_paths = set(config["src_paths"]) assert str(service_a_src) in src_paths assert str(service_b_src) in src_paths isort-9.0.1/tests/unit/test_output.py0000644000000000000000000000110213615410400014665 0ustar00from hypothesis import given, reject from hypothesis import strategies as st import isort.comments @given( comments=st.one_of(st.none(), st.lists(st.text())), original_string=st.text(), removed=st.booleans(), comment_prefix=st.text(), ) def test_fuzz_add_to_line(comments, original_string, removed, comment_prefix): try: isort.comments.add_to_line( comments=comments, original_string=original_string, removed=removed, comment_prefix=comment_prefix, ) except ValueError: reject() isort-9.0.1/tests/unit/test_parse.py0000644000000000000000000000616613615410400014456 0ustar00import pytest from hypothesis import given from hypothesis import strategies as st from isort import _parse_utils, parse from isort.settings import Config TEST_CONTENTS = """ import xyz import abc import (\\ # one one as \\ # two three) import \\ zebra as \\ # one not_bacon from x import (\\ # one one as \\ # two three) def function(): pass """ def test_file_contents(): ( in_lines, out_lines, import_index, _, _, _, _, _, change_count, original_line_count, _, _, _, _, ) = parse.file_contents(TEST_CONTENTS, config=Config(default_section="")) assert "\n".join(in_lines) == TEST_CONTENTS assert "import" not in "\n".join(out_lines) assert import_index == 1 assert change_count == -11 assert original_line_count == len(in_lines) def test_file_contents_empty(): parsed = parse.file_contents("", config=Config(default_section="")) assert parsed.in_lines == [] assert parsed.original_line_count == 0 @pytest.mark.parametrize("line_separator", ["\n", "\r\n", "\r"]) def test_file_contents_splits_only_on_newlines(line_separator): contents = line_separator.join(["import b", "import a", "\fpass"]) parsed = parse.file_contents(contents, config=Config(default_section="")) assert parsed.in_lines == ["import b", "import a", "\fpass"] # These tests were written by the `hypothesis.extra.ghostwriter` module # and is provided under the Creative Commons Zero public domain dedication. @given(contents=st.text()) def test_fuzz__infer_line_separator(contents): parse._infer_line_separator(contents=contents) @given(import_string=st.text()) def test_fuzz__strip_syntax(import_string): _parse_utils.strip_syntax(import_string=import_string) @given(line=st.text(), config=st.builds(Config)) def test_fuzz_import_type(line, config): _parse_utils.import_type(line=line, config=config) @given(line=st.text(), in_quote=st.text(), needs_import=st.booleans()) def test_fuzz_skip_line(line, in_quote, needs_import): _parse_utils.skip_line(line=line, in_quote=in_quote, needs_import=needs_import) @pytest.mark.parametrize( ("raw_line", "expected"), [ ("from . cimport a", "from . cimport a"), ("from.cimport a", "from . cimport a"), ("from..cimport a", "from .. cimport a"), ("from . import a", "from . import a"), ("from.import a", "from . import a"), ("from..import a", "from .. import a"), ("import *", "import *"), ("import*", "import *"), ("from . import a", "from . import a"), # noqa: PT014 ("from .import a", "from . import a"), ("from ..import a", "from .. import a"), ("from . cimport a", "from . cimport a"), # noqa: PT014 ("from .cimport a", "from . cimport a"), ("from ..cimport a", "from .. cimport a"), ("from\t.\timport a", "from . import a"), ], ) def test_normalize_line(raw_line, expected): line, returned_raw_line = _parse_utils.normalize_line(raw_line) assert line == expected assert returned_raw_line == raw_line isort-9.0.1/tests/unit/test_place.py0000644000000000000000000000521313615410400014420 0ustar00"""Tests for the isort import placement module""" from functools import partial from isort import place, sections from isort.settings import Config def test_module(src_path): place_tester = partial(place.module, config=Config(src_paths=[src_path])) assert place_tester("isort") == sections.FIRSTPARTY assert place_tester("os") == sections.STDLIB assert place_tester(".deprecated") == sections.LOCALFOLDER assert place_tester("__future__") == sections.FUTURE assert place_tester("hug") == sections.THIRDPARTY def test_extra_standard_library(src_path): place_tester = partial( place.module, config=Config(src_paths=[src_path], extra_standard_library=["hug"]) ) assert place_tester("os") == sections.STDLIB assert place_tester("hug") == sections.STDLIB def test_no_standard_library_placement(): assert place.module_with_reason( "pathlib", config=Config(sections=["THIRDPARTY"], default_section="THIRDPARTY") ) == ("THIRDPARTY", "Default option in Config or universal default.") assert place.module("pathlib") == "STDLIB" def test_namespace_package_placement(examples_path): namespace_examples = examples_path / "namespaces" implicit = namespace_examples / "implicit" pkg_resource = namespace_examples / "pkg_resource" pkgutil = namespace_examples / "pkgutil" for namespace_test in (implicit, pkg_resource, pkgutil): print(namespace_test) config = Config(settings_path=namespace_test) no_namespaces = Config(settings_path=namespace_test, auto_identify_namespace_packages=False) namespace_override = Config(settings_path=namespace_test, known_firstparty=["root.name"]) assert place.module("root.name", config=config) == "THIRDPARTY" assert place.module("root.nested", config=config) == "FIRSTPARTY" assert place.module("root.name", config=no_namespaces) == "FIRSTPARTY" assert place.module("root.name", config=namespace_override) == "FIRSTPARTY" no_namespace = namespace_examples / "none" almost_implicit = namespace_examples / "almost-implicit" weird_encoding = namespace_examples / "weird_encoding" for lacks_namespace in (no_namespace, almost_implicit, weird_encoding): config = Config(settings_path=lacks_namespace) manual_namespace = Config(settings_path=lacks_namespace, namespace_packages=["root"]) assert place.module("root.name", config=config) == "FIRSTPARTY" assert place.module("root.nested", config=config) == "FIRSTPARTY" assert place.module("root.name", config=manual_namespace) == "THIRDPARTY" assert place.module("root.nested", config=config) == "FIRSTPARTY" isort-9.0.1/tests/unit/test_regressions.py0000644000000000000000000020226413615410400015704 0ustar00"""A growing set of tests designed to ensure isort doesn't have regressions in new versions""" from io import BytesIO, StringIO, TextIOWrapper import pytest import isort import isort.sections from isort.core import STRING_PREFIXES from isort.main import main def test_isort_duplicating_comments_issue_1264(): """Ensure isort doesn't duplicate comments when force_sort_within_sections is set to `True` as was the case in issue #1264: https://github.com/pycqa/isort/issues/1264 """ assert ( isort.code( """ from homeassistant.util.logging import catch_log_exception # Loading the config flow... from . import config_flow """, force_sort_within_sections=True, ).count("# Loading the config flow...") == 1 ) def test_moving_comments_issue_726(): test_input = ( "from Blue import models as BlueModels\n" "# comment for PlaidModel\n" "from Plaid.models import PlaidModel\n" ) assert isort.code(test_input, force_sort_within_sections=True) == test_input test_input = ( "# comment for BlueModels\n" "from Blue import models as BlueModels\n" "# comment for PlaidModel\n" "# another comment for PlaidModel\n" "from Plaid.models import PlaidModel\n" ) assert isort.code(test_input, force_sort_within_sections=True) == test_input def test_pylint_disable_next_stays_with_first_import_issue_2054(): test_input = ( "# pylint: disable-next=import-error\n" "from C import D\n" "# pylint: disable-next=no-name-in-module\n" "from A import B\n" ) expected_output = ( "# pylint: disable-next=no-name-in-module\n" "from A import B\n" "# pylint: disable-next=import-error\n" "from C import D\n" ) assert isort.code(test_input) == expected_output def test_blank_lined_removed_issue_1275(): """Ensure isort doesn't accidentally remove blank lines after doc strings and before imports. See: https://github.com/pycqa/isort/issues/1275 """ assert ( isort.code( '''""" My docstring """ from b import thing from a import other_thing ''' ) == '''""" My docstring """ from a import other_thing from b import thing ''' ) assert ( isort.code( '''""" My docstring """ from b import thing from a import other_thing ''', add_imports=["from b import thing"], ) == '''""" My docstring """ from a import other_thing from b import thing ''' ) def test_blank_lined_removed_issue_1283(): """Ensure isort doesn't accidentally remove blank lines after __version__ identifiers. See: https://github.com/pycqa/isort/issues/1283 """ test_input = """__version__ = "0.58.1" from starlette import status """ assert isort.code(test_input) == test_input def test_extra_blank_line_added_nested_imports_issue_1290(): """Ensure isort doesn't add unnecessary blank lines above nested imports. See: https://github.com/pycqa/isort/issues/1290 """ test_input = '''from typing import TYPE_CHECKING # Special imports from special import thing if TYPE_CHECKING: # Special imports from special import another_thing def func(): """Docstring""" # Special imports from special import something_else return ''' assert ( isort.code( test_input, import_heading_special="Special imports", known_special=["special"], sections=["FUTURE", "STDLIB", "THIRDPARTY", "SPECIAL", "FIRSTPARTY", "LOCALFOLDER"], ) == test_input ) def test_add_imports_shouldnt_make_isort_unusable_issue_1297(): """Test to ensure add imports doesn't cause any unexpected behaviour when combined with check See: https://github.com/pycqa/isort/issues/1297 """ assert isort.check_code( """from __future__ import unicode_literals from os import path """, add_imports={"from __future__ import unicode_literals"}, ) def test_no_extra_lines_for_imports_in_functions_issue_1277(): """Test to ensure isort doesn't introduce extra blank lines for imports within function. See: https://github.com/pycqa/isort/issues/1277 """ test_input = """ def main(): import time import sys """ expected_output = """ def main(): import sys import time """ assert isort.code(isort.code(isort.code(test_input))) == expected_output def test_no_extra_blank_lines_in_methods_issue_1293(): """Test to ensure isort isn't introducing extra lines in methods that contain imports See: https://github.com/pycqa/isort/issues/1293 """ test_input = """ class Something(object): def on_email_deleted(self, email): from hyperkitty.tasks import rebuild_thread_cache_new_email # update or cleanup thread # noqa: E303 (isort issue) if self.emails.count() == 0: ... """ assert isort.code(test_input) == test_input assert isort.code(test_input, lines_after_imports=2) == test_input def test_form_feed_blank_line_not_removed_issue_2562(): """Ensure isort preserves form feed as a valid blank line.""" test_input = 'import sys\n\n\f\nprint("!")\n' assert isort.code(test_input) == 'import sys\n\n\fprint("!")\n' assert isort.code(test_input, lines_after_imports=2) == test_input test_input = 'import sys\n\n\n\fprint("!")\n' assert isort.code(test_input) == 'import sys\n\n\fprint("!")\n' assert isort.code(test_input, lines_after_imports=2) == test_input def test_force_single_line_shouldnt_remove_preceding_comment_lines_issue_1296(): """Tests to ensure force_single_line setting doesn't result in lost comments. See: https://github.com/pycqa/isort/issues/1296 """ test_input = """ # A comment # A comment # Oh no, I'm gone from moo import foo """ # assert isort.code(test_input) == test_input assert isort.code(test_input, force_single_line=True) == test_input def test_ensure_new_line_before_comments_mixed_with_ensure_newline_before_comments_1295(): """Tests to ensure that the black profile can be used in conjunction with force_sort_within_sections. See: https://github.com/pycqa/isort/issues/1295 """ test_input = """ from openzwave.group import ZWaveGroup from openzwave.network import ZWaveNetwork # pylint: disable=import-error from openzwave.option import ZWaveOption """ assert isort.code(test_input, profile="black") == test_input assert isort.code(test_input, profile="black", force_sort_within_sections=True) == test_input def test_trailing_comma_doesnt_introduce_broken_code_with_comment_and_wrap_issue_1302(): """Tests to assert the combination of include_trailing_comma and a wrapped line doesn't break. See: https://github.com/pycqa/isort/issues/1302. """ assert ( isort.code( """ from somewhere import very_very_very_very_very_very_long_symbol # some comment """, line_length=50, include_trailing_comma=True, ) == """ from somewhere import \\ very_very_very_very_very_very_long_symbol # some comment """ ) def test_ensure_sre_parse_is_identified_as_stdlib_issue_1304(): """Ensure sre_parse is identified as STDLIB. See: https://github.com/pycqa/isort/issues/1304. """ assert isort.place_module("sre_parse") == isort.place_module("sre") == isort.sections.STDLIB def test_add_imports_shouldnt_move_lower_comments_issue_1300(): """Ensure add_imports doesn't move comments immediately below imports. See:: https://github.com/pycqa/isort/issues/1300. """ test_input = """from __future__ import unicode_literals from os import path # A comment for a constant ANSWER = 42 """ assert isort.code(test_input, add_imports=["from os import path"]) == test_input def test_windows_newline_issue_1277(): """Test to ensure windows new lines are correctly handled within indented scopes. See: https://github.com/pycqa/isort/issues/1277 """ assert ( isort.code("\ndef main():\r\n import time\r\n\n import sys\r\n") == "\ndef main():\r\n import sys\r\n import time\r\n" ) def test_windows_newline_issue_1278(): """Test to ensure windows new lines are correctly handled within indented scopes. See: https://github.com/pycqa/isort/issues/1278 """ assert isort.check_code( "\ntry:\r\n import datadog_agent\r\n\r\n " "from ..log import CheckLoggingAdapter, init_logging\r\n\r\n init_logging()\r\n" "except ImportError:\r\n pass\r\n" ) def test_check_never_passes_with_indented_headings_issue_1301(): """Test to ensure that test can pass even when there are indented headings. See: https://github.com/pycqa/isort/issues/1301 """ assert isort.check_code( """ try: # stdlib import logging from os import abc, path except ImportError: pass """, import_heading_stdlib="stdlib", ) def test_isort_shouldnt_fail_on_long_from_with_dot_issue_1190(): """Test to ensure that isort will correctly handle formatting a long from import that contains a dot. See: https://github.com/pycqa/isort/issues/1190 """ assert ( isort.code( """ from this_is_a_very_long_import_statement.that_will_occur_across_two_lines\\ .when_the_line_length.is_only_seventynine_chars import ( function1, function2, ) """, line_length=79, multi_line_output=3, ) == """ from this_is_a_very_long_import_statement.that_will_occur_across_two_lines""" """.when_the_line_length.is_only_seventynine_chars import ( function1, function2 ) """ ) def test_isort_shouldnt_add_extra_new_line_when_fass_and_n_issue_1315(): """Test to ensure isort doesn't add a second extra new line when combining --fss and -n options. See: https://github.com/pycqa/isort/issues/1315 """ assert isort.check_code( """import sys # Comment canary from . import foo """, ensure_newline_before_comments=True, # -n force_sort_within_sections=True, # -fss show_diff=True, # for better debugging in the case the test case fails. ) assert ( isort.code( """ from . import foo # Comment canary from .. import foo """, ensure_newline_before_comments=True, force_sort_within_sections=True, ) == """ from . import foo # Comment canary from .. import foo """ ) def test_isort_doesnt_rewrite_import_with_dot_to_from_import_issue_1280(): """Test to ensure isort doesn't rewrite imports in the from of import y.x into from y import x. This is because they are not technically fully equivalent to each other and can introduce broken behaviour. See: https://github.com/pycqa/isort/issues/1280 """ assert isort.check_code( """ import test.module import test.module as m from test import module from test import module as m """, show_diff=True, ) def test_isort_shouldnt_introduce_extra_lines_with_fass_issue_1322(): """Tests to ensure isort doesn't introduce extra lines when used with fass option. See: https://github.com/pycqa/isort/issues/1322 """ assert ( isort.code( """ import logging # Comment canary from foo import bar import quux """, force_sort_within_sections=True, ensure_newline_before_comments=True, ) == """ import logging # Comment canary from foo import bar import quux """ ) def test_comments_should_cause_wrapping_on_long_lines_black_mode_issue_1219(): """Tests to ensure if isort encounters a single import line which is made too long with a comment it is wrapped when using black profile. See: https://github.com/pycqa/isort/issues/1219 """ assert isort.check_code( """ from many_stop_words import ( get_stop_words as get_base_stopwords, # extended list of stop words, also for en ) """, show_diff=True, profile="black", ) def test_comment_blocks_should_stay_associated_without_extra_lines_issue_1156(): """Tests to ensure isort doesn't add an extra line when there are large import blocks or otherwise warp the intent. See: https://github.com/pycqa/isort/issues/1156 """ assert ( isort.code( """from top_level_ignored import config # isort:skip #################################### # COMMENT BLOCK SEPARATING THESE # #################################### from ast import excepthandler import logging """ ) == """from top_level_ignored import config # isort:skip import logging #################################### # COMMENT BLOCK SEPARATING THESE # #################################### from ast import excepthandler """ ) def test_comment_shouldnt_be_duplicated_with_fass_enabled_issue_1329(): """Tests to ensure isort doesn't duplicate comments when imports occur with comment on top, immediately after large comment blocks. See: https://github.com/pycqa/isort/pull/1329/files. """ assert isort.check_code( """''' Multi-line docstring ''' # Comment for A. import a # Comment for B - not A! import b """, force_sort_within_sections=True, show_diff=True, ) def test_wrap_mode_equal_to_line_length_with_indendet_imports_issue_1333(): assert isort.check_code( """ import a import b def function(): import a as b import c as d """, line_length=17, wrap_length=17, show_diff=True, ) def test_isort_skipped_nested_imports_issue_1339(): """Ensure `isort:skip are honored in nested imports. See: https://github.com/pycqa/isort/issues/1339. """ assert isort.check_code( """ def import_test(): from os ( # isort:skip import path ) """, show_diff=True, ) def test_windows_diff_too_large_misrepresentative_issue_1348(test_path): """Ensure isort handles windows files correctly when it come to producing a diff with --diff. See: https://github.com/pycqa/isort/issues/1348 """ diff_output = StringIO() isort.file(test_path / "example_crlf_file.py", show_diff=diff_output) diff_output.seek(0) assert diff_output.read().endswith( "-1,5 +1,5 @@\n+import a\r\n import b\r\n-import a\r\n \r\n \r\n def func():\r\n" ) def test_combine_as_does_not_lose_comments_issue_1321(): """Test to ensure isort doesn't lose comments when --combine-as is used. See: https://github.com/pycqa/isort/issues/1321 """ test_input = """ from foo import * # noqa from foo import bar as quux # other from foo import x as a # noqa import operator as op # op comment import datetime as dtime # dtime comment from datetime import date as d # dcomm from datetime import datetime as dt # dtcomm """ expected_output = """ import datetime as dtime # dtime comment import operator as op # op comment from datetime import date as d, datetime as dt # dcomm; dtcomm from foo import * # noqa from foo import bar as quux, x as a # other; noqa """ assert isort.code(test_input, combine_as_imports=True) == expected_output def test_combine_as_does_not_lose_comments_issue_1381(): """Test to ensure isort doesn't lose comments when --combine-as is used. See: https://github.com/pycqa/isort/issues/1381 """ test_input = """ from smtplib import SMTPConnectError, SMTPNotSupportedError # important comment """ assert "# important comment" in isort.code(test_input, combine_as_imports=True) test_input = """ from appsettings import AppSettings, ObjectSetting, StringSetting # type: ignore """ assert "# type: ignore" in isort.code(test_input, combine_as_imports=True) def test_incorrect_grouping_when_comments_issue_1396(): """Test to ensure isort groups import correct independent of the comments present. See: https://github.com/pycqa/isort/issues/1396 """ assert ( isort.code( """from django.shortcuts import render from apps.profiler.models import Project from django.contrib.auth.decorators import login_required from django.views.generic import ( # ListView, # DetailView, TemplateView, # CreateView, # View ) """, line_length=88, known_first_party=["apps"], known_django=["django"], sections=["FUTURE", "STDLIB", "DJANGO", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"], ) == """from django.contrib.auth.decorators import login_required from django.shortcuts import render from django.views.generic import \\ TemplateView # ListView,; DetailView,; CreateView,; View from apps.profiler.models import Project """ ) assert ( isort.code( """from django.contrib.auth.decorators import login_required from django.shortcuts import render from apps.profiler.models import Project from django.views.generic import ( # ListView,; DetailView,; CreateView,; View TemplateView, ) """, line_length=88, known_first_party=["apps"], known_django=["django"], sections=["FUTURE", "STDLIB", "DJANGO", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"], include_trailing_comma=True, multi_line_output=3, force_grid_wrap=0, use_parentheses=True, ensure_newline_before_comments=True, ) == """from django.contrib.auth.decorators import login_required from django.shortcuts import render from django.views.generic import ( # ListView,; DetailView,; CreateView,; View TemplateView, ) from apps.profiler.models import Project """ ) def test_reverse_relative_combined_with_force_sort_within_sections_issue_1395(): """Test to ensure reverse relative combines well with other common isort settings. See: https://github.com/pycqa/isort/issues/1395. """ assert isort.check_code( """from .fileA import a_var from ..fileB import b_var """, show_diff=True, reverse_relative=True, force_sort_within_sections=True, order_by_type=False, case_sensitive=False, multi_line_output=5, sections=["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "APPLICATION", "LOCALFOLDER"], lines_after_imports=2, no_lines_before="LOCALFOLDER", ) def test_isort_should_be_able_to_add_independent_of_doc_string_placement_issue_1420(): """isort should be able to know when an import requested to be added is successfully added, independent of where the top doc string is located. See: https://github.com/PyCQA/isort/issues/1420 """ assert isort.check_code( '''"""module docstring""" import os ''', show_diff=True, add_imports=["os"], ) def test_force_single_line_should_not_influence_order_of_star_import(): """isort should never move comments to different import statement. Originally reported as an issue with moving comments across import statements, see: https://github.com/PyCQA/isort/issues/1427 """ expected = """from package import * # noqa from package import CONSTANT """ assert isort.check_code( expected, force_single_line=False, show_diff=True, ) assert isort.check_code( expected, force_single_line=True, show_diff=True, ) assert ( isort.code( """from package import CONSTANT from package import * # noqa """, force_single_line=False, ) == expected ) assert ( isort.code( """from package import CONSTANT from package import * # noqa """, force_single_line=True, ) == expected ) def test_isort_doesnt_misplace_comments_issue_1431(): """Test to ensure isort won't misplace comments. See: https://github.com/PyCQA/isort/issues/1431 """ input_text = """from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( MyLovelyCompanyTeamProjectComponent, # NOT DRY ) from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( MyLovelyCompanyTeamProjectComponent as component, # DRY ) """ assert isort.code(input_text, profile="black") == input_text def test_isort_doesnt_misplace_add_import_issue_1445(): """Test to ensure isort won't misplace an added import depending on docstring position See: https://github.com/PyCQA/isort/issues/1445 """ assert ( isort.code( '''#!/usr/bin/env python """module docstring""" ''', add_imports=["import os"], ) == '''#!/usr/bin/env python """module docstring""" import os ''' ) assert isort.check_code( '''#!/usr/bin/env python """module docstring""" import os ''', add_imports=["import os"], show_diff=True, ) def test_isort_doesnt_mangle_code_when_adding_imports_issue_1444(): """isort should NEVER mangle code. This particularly nasty and easy to reproduce bug, caused isort to produce invalid code just by adding a single import statement depending on comment placement. See: https://github.com/PyCQA/isort/issues/1444 """ assert ( isort.code( ''' """module docstring""" ''', add_imports=["import os"], ) == ''' """module docstring""" import os ''' ) def test_isort_float_to_top_with_sort_on_off_tests(): """Characterization test for current behaviour of float-to-top on isort: on/off sections. - imports in isort:off sections stay where they are - imports in isort:on sections float up, but to the top of the isort:on section (not the top of the file)""" assert ( isort.code( """ def foo(): pass import a # isort: off import stays_in_section x = 1 import stays_in_place # isort: on def bar(): pass import floats_to_top_of_section def baz(): pass """, float_to_top=True, ) == """import a def foo(): pass # isort: off import stays_in_section x = 1 import stays_in_place # isort: on import floats_to_top_of_section def bar(): pass def baz(): pass """ ) to_sort = """# isort: off def foo(): pass import stays_in_place import no_float_to_to_top import no_ordering def bar(): pass """ # No changes if isort is off assert isort.code(to_sort, float_to_top=True) == to_sort def test_isort_float_to_top_respects_isort_off_with_crlf_issue_2528(): to_sort = """ import b import a # isort: off pytest.importorskip('cattrs') import z import y """ assert ( isort.code(to_sort, float_to_top=True) == """ import a import b # isort: off pytest.importorskip('cattrs') import z import y """ ) def test_isort_doesnt_float_to_top_correctly_when_imports_not_at_top_issue_1382(): """isort should float existing imports to the top, if they are currently below the top. See: https://github.com/PyCQA/isort/issues/1382 """ assert ( isort.code( """ def foo(): pass import a def bar(): pass """, float_to_top=True, ) == """import a def foo(): pass def bar(): pass """ ) assert ( isort.code( """ def foo(): pass import a def bar(): pass """, float_to_top=True, ) == """import a def foo(): pass def bar(): pass """ ) assert ( isort.code( '''"""My comment """ def foo(): pass import a def bar(): pass ''', float_to_top=True, ) == '''"""My comment """ import a def foo(): pass def bar(): pass ''' ) assert ( isort.code( ''' """My comment """ def foo(): pass import a def bar(): pass ''', float_to_top=True, ) == ''' """My comment """ import a def foo(): pass def bar(): pass ''' ) assert ( isort.code( '''#!/usr/bin/env bash """My comment """ def foo(): pass import a def bar(): pass ''', float_to_top=True, ) == '''#!/usr/bin/env bash """My comment """ import a def foo(): pass def bar(): pass ''' ) assert ( isort.code( '''#!/usr/bin/env bash """My comment """ def foo(): pass import a def bar(): pass ''', float_to_top=True, ) == '''#!/usr/bin/env bash """My comment """ import a def foo(): pass def bar(): pass ''' ) def test_empty_float_to_top_shouldnt_error_issue_1453(): """isort shouldn't error when float to top is set with a mostly empty file""" assert isort.check_code( """ """, show_diff=True, float_to_top=True, ) assert isort.check_code( """ """, show_diff=True, ) def test_import_sorting_shouldnt_be_endless_with_headers_issue_1454(): """isort should never enter an endless sorting loop. See: https://github.com/PyCQA/isort/issues/1454 """ assert isort.check_code( """ # standard library imports import sys try: # Comment about local lib # related third party imports from local_lib import stuff except ImportError as e: pass """, known_third_party=["local_lib"], import_heading_thirdparty="related third party imports", show_diff=True, ) def test_isort_should_leave_non_import_from_lines_alone_issue_1488(): """isort should never mangle non-import from statements. See: https://github.com/PyCQA/isort/issues/1488 """ raise_from_should_be_ignored = """ raise SomeException("Blah") \\ from exceptionsInfo.popitem()[1] """ assert isort.check_code(raise_from_should_be_ignored, show_diff=True) yield_from_should_be_ignored = """ def generator_function(): yield \\ from other_function()[1] """ assert isort.check_code(yield_from_should_be_ignored, show_diff=True) wont_ignore_comment_contiuation = """ # one # two def function(): # three \\ import b import a """ assert ( isort.code(wont_ignore_comment_contiuation) == """ # one # two def function(): # three \\ import a import b """ ) will_ignore_if_non_comment_continuation = """ # one # two def function(): raise \\ import b import a """ assert isort.check_code(will_ignore_if_non_comment_continuation, show_diff=True) yield_from_parens_should_be_ignored = """ def generator_function(): ( yield from other_function()[1] ) """ assert isort.check_code(yield_from_parens_should_be_ignored, show_diff=True) yield_from_lots_of_parens_and_space_should_be_ignored = """ def generator_function(): ( ( (((( ((((( (( ((( yield from other_function()[1] ))))))))))))) ))) """ assert isort.check_code(yield_from_lots_of_parens_and_space_should_be_ignored, show_diff=True) yield_from_should_be_ignored_when_following_import_statement = """ def generator_function(): import os yield \\ from other_function()[1] """ assert isort.check_code( yield_from_should_be_ignored_when_following_import_statement, show_diff=True ) yield_at_file_end_ignored = """ def generator_function(): ( ( (((( ((((( (( ((( yield """ assert isort.check_code(yield_at_file_end_ignored, show_diff=True) raise_at_file_end_ignored = """ def generator_function(): ( ( (((( ((((( (( ((( raise ( """ assert isort.check_code(raise_at_file_end_ignored, show_diff=True) raise_from_at_file_end_ignored = """ def generator_function(): ( ( (((( ((((( (( ((( raise \\ from \\ """ assert isort.check_code(raise_from_at_file_end_ignored, show_diff=True) def test_isort_float_to_top_correctly_identifies_single_line_comments_1499(): """Test to ensure isort correctly handles the case where float to top is used to push imports to the top and the top comment is a multiline type but only one line. See: https://github.com/PyCQA/isort/issues/1499 """ assert isort.code( '''#!/usr/bin/env bash """My comment""" def foo(): pass import a def bar(): pass ''', float_to_top=True, ) == ( '''#!/usr/bin/env bash """My comment""" import a def foo(): pass def bar(): pass ''' ) assert isort.code( """#!/usr/bin/env bash '''My comment''' def foo(): pass import a def bar(): pass """, float_to_top=True, ) == ( """#!/usr/bin/env bash '''My comment''' import a def foo(): pass def bar(): pass """ ) assert isort.check_code( """#!/usr/bin/env bash '''My comment''' import a x = 1 """, float_to_top=True, show_diff=True, ) def test_isort_shouldnt_mangle_from_multi_line_string_issue_1507(): """isort was seen mangling lines that happened to contain the word from after a yield happened to be in a file. Clearly this shouldn't happen. See: https://github.com/PyCQA/isort/issues/1507. """ assert isort.check_code( ''' def a(): yield f( """ select %s from (values %%s) as t(%s) """ ) def b(): return ( """ select name from foo """ % main_table ) def c(): query = ( """ select {keys} from (values %s) as t(id) """ ) def d(): query = f"""select t.id from {table} t {extra}""" ''', show_diff=True, ) def test_isort_should_keep_all_as_and_non_as_imports_issue_1523(): """isort should keep as and non-as imports of the same path that happen to exist within the same statement. See: https://github.com/PyCQA/isort/issues/1523. """ assert isort.check_code( """ from selenium.webdriver import Remote, Remote as Driver """, show_diff=True, combine_as_imports=True, ) def test_isort_shouldnt_introduce_syntax_error_issue_1539(): """isort should NEVER introduce syntax errors. In 5.5.4 some strings that contained a line starting with from could lead to no empty paren. See: https://github.com/PyCQA/isort/issues/1539. """ assert isort.check_code( '''"""Foobar from {}""".format( "bar", ) ''', show_diff=True, ) assert isort.check_code( '''"""Foobar import {}""".format( "bar", ) ''', show_diff=True, ) assert ( isort.code( '''"""Foobar from {}""" from a import b, a ''', ) == '''"""Foobar from {}""" from a import a, b ''' ) assert ( isort.code( '''"""Foobar from {}""" import b import a ''', ) == '''"""Foobar from {}""" import a import b ''' ) def test_isort_shouldnt_split_skip_issue_1548(): """Ensure isort doesn't add a spurious new line if isort: skip is combined with float to top. See: https://github.com/PyCQA/isort/issues/1548. """ assert isort.check_code( """from tools.dependency_pruning.prune_dependencies import ( # isort:skip prune_dependencies, ) """, show_diff=True, profile="black", float_to_top=True, ) assert isort.check_code( """from tools.dependency_pruning.prune_dependencies import ( # isort:skip prune_dependencies, ) import a import b """, show_diff=True, profile="black", float_to_top=True, ) assert isort.check_code( """from tools.dependency_pruning.prune_dependencies import # isort:skip import a import b """, show_diff=True, float_to_top=True, ) assert isort.check_code( """from tools.dependency_pruning.prune_dependencies import ( # isort:skip a ) import b """, show_diff=True, profile="black", float_to_top=True, ) assert isort.check_code( """from tools.dependency_pruning.prune_dependencies import ( # isort:skip ) """, show_diff=True, profile="black", float_to_top=True, ) assert isort.check_code( """from tools.dependency_pruning.prune_dependencies import ( # isort:skip )""", show_diff=True, profile="black", float_to_top=True, ) assert ( isort.code( """from tools.dependency_pruning.prune_dependencies import ( # isort:skip ) """, profile="black", float_to_top=True, add_imports=["import os"], ) == """from tools.dependency_pruning.prune_dependencies import ( # isort:skip ) import os """ ) assert ( isort.code( """from tools.dependency_pruning.prune_dependencies import ( # isort:skip )""", profile="black", float_to_top=True, add_imports=["import os"], ) == """from tools.dependency_pruning.prune_dependencies import ( # isort:skip ) import os """ ) def test_isort_shouldnt_split_skip_issue_1556(): assert isort.check_code( """ from tools.dependency_pruning.prune_dependencies import ( # isort:skip prune_dependencies, ) from tools.developer_pruning.prune_developers import ( # isort:skip prune_developers, ) """, show_diff=True, profile="black", float_to_top=True, ) assert isort.check_code( """ from tools.dependency_pruning.prune_dependencies import ( # isort:skip prune_dependencies, ) from tools.developer_pruning.prune_developers import x # isort:skip """, show_diff=True, profile="black", float_to_top=True, ) def test_isort_losing_imports_vertical_prefix_from_module_import_wrap_mode_issue_1542(): """Ensure isort doesn't lose imports when a comment is combined with an import and wrap mode VERTICAL_PREFIX_FROM_MODULE_IMPORT is used. See: https://github.com/PyCQA/isort/issues/1542. """ assert ( isort.code( """ from xxxxxxxxxxxxxxxx import AAAAAAAAAA, BBBBBBBBBB from xxxxxxxxxxxxxxxx import CCCCCCCCC, DDDDDDDDD # xxxxxxxxxxxxxxxxxx print(CCCCCCCCC) """, multi_line_output=9, ) == """ from xxxxxxxxxxxxxxxx import AAAAAAAAAA, BBBBBBBBBB # xxxxxxxxxxxxxxxxxx from xxxxxxxxxxxxxxxx import CCCCCCCCC, DDDDDDDDD print(CCCCCCCCC) """ ) assert isort.check_code( """ from xxxxxxxxxxxxxxxx import AAAAAAAAAA, BBBBBBBBBB from xxxxxxxxxxxxxxxx import CCCCCCCCC, DDDDDDDDD # xxxxxxxxxxxxxxxxxx isort: skip print(CCCCCCCCC) """, show_diff=True, multi_line_output=9, ) def test_isort_adding_second_comma_issue_1621(): """Ensure isort doesn't add a second comma when very long comment is present See: https://github.com/PyCQA/isort/issues/1621. """ assert isort.check_code( """from .test import ( TestTestTestTestTestTest2 as TestTestTestTestTestTest1, """ """# Some really long comment bla bla bla bla bla ) """, profile="black", show_diff=True, ) assert ( isort.code( """from .test import ( TestTestTestTestTestTest2 as TestTestTestTestTestTest1 """ """# Some really long comment bla bla bla bla bla ) """, profile="black", ) == """from .test import ( TestTestTestTestTestTest2 as TestTestTestTestTestTest1, """ """# Some really long comment bla bla bla bla bla ) """ ) def test_isort_shouldnt_duplicate_comments_issue_1631(): assert isort.check_code( """ import a # a comment import a as b # b comment """, show_diff=True, ) assert ( isort.code( """ import a # a comment import a as a # b comment """, remove_redundant_aliases=True, ) == """ import a # a comment; b comment """ ) def test_isort_shouldnt_add_extra_new_lines_with_import_heading_issue_1670(): snippet = """#!/usr/bin/python3 -ttu # Standard Library import argparse import datetime import attr import requests def foo() -> int: print("Hello world") return 0 def spam(): # Standard Library import collections import logging """ assert ( isort.code( snippet, import_heading_stdlib="Standard Library", ) == snippet ) def test_isort_shouldnt_add_extra_line_float_to_top_issue_1667(): assert isort.check_code( """ import sys sys.path.insert(1, 'path/containing/something_else/..') import something_else # isort:skip # Some constant SOME_CONSTANT = 4 """, show_diff=True, float_to_top=True, ) def test_isort_shouldnt_move_noqa_comment_issue_1594(): assert ( isort.code( """ from .test import TestTestTestTestTestTest1 # noqa: F401 from .test import TestTestTestTestTestTest2, TestTestTestTestTestTest3, """ """TestTestTestTestTestTest4, TestTestTestTestTestTest5 # noqa: F401 """, profile="black", ) == """ from .test import TestTestTestTestTestTest1 # noqa: F401 from .test import ( # noqa: F401 TestTestTestTestTestTest2, TestTestTestTestTestTest3, TestTestTestTestTestTest4, TestTestTestTestTestTest5, ) """ ) def test_isort_correctly_handles_unix_vs_linux_newlines_issue_1566(): import_statement = ( "from impacket.smb3structs import (\n" "SMB2_CREATE, SMB2_FLAGS_DFS_OPERATIONS, SMB2_IL_IMPERSONATION, " "SMB2_OPLOCK_LEVEL_NONE, SMB2Create," "\nSMB2Create_Response, SMB2Packet)\n" ) assert isort.code(import_statement, line_length=120) == isort.code( import_statement.replace("\n", "\r\n"), line_length=120 ).replace("\r\n", "\n") def test_isort_treats_src_paths_same_as_from_config_as_cli_issue_1711(tmpdir): assert isort.check_code( """ import mymodule import sqlalchemy """, show_diff=True, ) config_file = tmpdir.join(".isort.cfg") config_file.write( """ [settings] src_paths= api """ ) api_dir = tmpdir.mkdir("api") api_dir.join("mymodule.py").write("# comment") config = isort.settings.Config(str(config_file)) assert isort.check_code( """ import sqlalchemy import mymodule """, show_diff=True, config=config, ) def test_isort_should_never_quietly_remove_imports_in_hanging_line_mode_issue_1741(): assert ( isort.code( """ from src import abcd, qwerty, efg, xyz # some comment """, line_length=50, multi_line_output=2, ) == """ from src import abcd, efg, qwerty, xyz \\ # some comment """ ) assert ( isort.code( """ from src import abcd, qwerty, efg, xyz # some comment """, line_length=54, multi_line_output=2, ) == """ from src import abcd, efg, qwerty, xyz # some comment """ ) assert ( isort.code( """ from src import abcd, qwerty, efg, xyz # some comment """, line_length=53, multi_line_output=2, ) == """ from src import abcd, efg, qwerty, xyz \\ # some comment """ ) assert ( isort.code( """ from src import abcd, qwerty, efg, xyz # some comment """, line_length=30, multi_line_output=2, ) == """ from src import abcd, efg, \\ qwerty, xyz \\ # some comment """ ) @pytest.mark.parametrize("multi_line_output", range(12)) def test_isort_should_never_quietly_remove_imports_in_any_hangin_mode_issue_1741( multi_line_output: int, ): sorted_code = isort.code( """ from src import abcd, qwerty, efg, xyz # some comment """, line_length=30, multi_line_output=multi_line_output, ) assert "abcd" in sorted_code assert "qwerty" in sorted_code assert "efg" in sorted_code assert "xyz" in sorted_code def test_isort_should_keep_multi_noqa_with_star_issue_1744(): assert isort.check_code( """ from typing import * # noqa from typing import IO, BinaryIO, Union # noqa """, show_diff=True, ) assert isort.check_code( """ from typing import * # noqa 1 from typing import IO, BinaryIO, Union # noqa 2 """, show_diff=True, ) assert isort.check_code( """ from typing import * # noqa from typing import IO, BinaryIO, Union """, show_diff=True, ) assert isort.check_code( """ from typing import * from typing import IO, BinaryIO, Union # noqa """, show_diff=True, ) assert ( isort.code( """ from typing import * # hi from typing import IO, BinaryIO, Union # noqa """, combine_star=True, ) == """ from typing import * # noqa; hi """ ) assert ( isort.code( """ from typing import * # noqa from typing import IO, BinaryIO, Union # noqa """, combine_star=True, ) == """ from typing import * # noqa """ ) def test_isort_should_keep_multiple_noqa_comments_force_single_line_mode_issue_1721(): assert isort.check_code( """ from some_very_long_filename_to_import_from_that_causes_a_too_long_import_row import ( # noqa: E501 CONSTANT_1, ) from some_very_long_filename_to_import_from_that_causes_a_too_long_import_row import ( # noqa: E501 CONSTANT_2, ) """, show_diff=True, profile="black", force_single_line=True, ) def test_isort_should_only_add_imports_to_valid_location_issue_1769(): assert ( isort.code( '''v = """ """.split( "\n" ) ''', add_imports=["from __future__ import annotations"], ) == '''from __future__ import annotations v = """ """.split( "\n" ) ''' ) assert ( isort.code( '''v=""""""''', add_imports=["from __future__ import annotations"], ) == '''from __future__ import annotations v="""""" ''' ) def test_literal_sort_at_top_of_file_issue_1792(): assert ( isort.code( '''"""I'm a docstring! Look at me!""" # isort: unique-list __all__ = ["Foo", "Foo", "Bar"] from typing import final # arbitrary @final class Foo: ... @final class Bar: ... ''' ) == '''"""I'm a docstring! Look at me!""" # isort: unique-list __all__ = ["Bar", "Foo"] from typing import final # arbitrary @final class Foo: ... @final class Bar: ... ''' ) def test_isort_should_produce_the_same_code_on_subsequent_runs_issue_1799(tmpdir): code = """import sys from importlib.metadata import PackageNotFoundError, version """ config_file = tmpdir.join(".isort.cfg") config_file.write( """[isort] profile=black src_paths=isort,test line_length=100 skip=.tox,.venv,build,dist,docs,tests extra_standard_library=pkg_resources,setuptools,typing known_test=pytest known_first_party=ibpt sections=FUTURE,STDLIB,TEST,THIRDPARTY,FIRSTPARTY,LOCALFOLDER import_heading_firstparty=internal import_heading_thirdparty=external """ ) settings = isort.settings.Config(str(config_file)) assert isort.code(code, config=settings) == isort.code( isort.code(code, config=settings), config=settings ) def test_check_code_should_not_false_positive_with_float_to_top_and_add_imports(): """isort check_code should not report incorrectly sorted imports when float_to_top and add_imports are used together and the import is already present. See: https://github.com/PyCQA/isort/issues/1971 """ assert isort.check_code( isort.code("1\n", float_to_top=True, add_imports=["import os"]), float_to_top=True, add_imports=["import os"], ) def test_unrecoverable_exception_on_valid_input_ending_with_backslash_issue_1893(): """Ensure isort doesn't raise an IndexError on valid input ending with a backslash without a trailing newline, as was the case in issue #1893: https://github.com/PyCQA/isort/issues/1893 """ assert isort.code("import os #\\") == "import os # \\\n" def test_comment_on_opening_line_of_aliased_import_does_not_move(): """Ensure isort doesn't move comments from the opening "from ... import (" line to the alias attribute line when using import aliases that wrap across multiple lines. See: https://github.com/PyCQA/isort/issues/2392 """ # Opening-line comment (e.g. "# type: ignore") must stay on the "import (" line, # not drift down to the alias attribute line. test_input = """\ from a_long_name_to_enforce.splitting_across.two_lines import ( # type: ignore[attr-defined] a_random_attribute as renamed_random_attribute, ) """ assert isort.code(test_input, profile="black") == test_input # An inline comment on the *attribute* line must stay on the attribute line, not move up to # the opening line. attr_comment_input = """\ from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( MyLovelyCompanyTeamProjectComponent as component, # DRY alias ) """ assert isort.code(attr_comment_input, profile="black") == attr_comment_input # When both an opening-line comment and an attribute-line comment are present, # each must remain on its original line. both_comments_input = """\ from a_long_name_to_enforce.splitting_across.two_lines import ( # opening comment a_random_attribute as renamed_random_attribute, # attr comment ) """ assert isort.code(both_comments_input, profile="black") == both_comments_input # Non-alias imports with an opening-line comment should also keep the comment in place. non_alias_input = """\ from a_long_name_to_enforce.splitting_across.two_lines import ( # type: ignore a_random_attribute, ) """ assert isort.code(non_alias_input, profile="black") == non_alias_input # When the import is short enough to fit on one line the opening-line comment is # preserved at the end of that single line (both alias and non-alias). short_alias = "from mod import attr as alias # type: ignore[attr-defined]\n" assert isort.code(short_alias, profile="black") == short_alias # The fix must also work when use_parentheses=True with other wrap modes # (not just the Black profile's VERTICAL_HANGING_INDENT mode). test_input_no_trailing_comma = """\ from a_long_name_to_enforce.splitting_across.two_lines import ( # type: ignore[attr-defined] a_random_attribute as renamed_random_attribute ) """ assert ( isort.code( test_input_no_trailing_comma, use_parentheses=True, multi_line_output=3, line_length=88, ) == test_input_no_trailing_comma ) # When the import is short enough to fit on one line both the opening-line comment and the # attribute-line comment are preserved at the end of the single line (both alias and non-alias). short_line = """\ from mod import ( # My comment attr as alias # type: ignore[attr-defined] ) """ assert ( isort.code(short_line, profile="black") == "from mod import attr as alias # type: ignore[attr-defined] # My comment\n" ) def test_sort_reexports_with_stdin_raises_error_issue_2393(): """Ensure --sort-reexports raises a clear error when used with stdin.""" fake_stdin = TextIOWrapper(BytesIO(b"from test import B, A\n")) with pytest.raises(SystemExit) as exc_info: main(argv=["--sort-reexports", "-"], stdin=fake_stdin) assert exc_info.value.code != 0 def test_split_on_trailing_comma_idempotent_with_non_default_wrap_mode(): """Ensure isort output is idempotent when ``split_on_trailing_comma`` and ``include_trailing_comma`` are combined with a non-default ``multi_line_output`` mode. With both options enabled, isort always appends a trailing comma when it wraps an import across multiple lines. ``split_on_trailing_comma`` then explodes any import that ends with a trailing comma onto individual lines on the *next* run. Previously the explode was only applied when the *input* already carried a trailing comma, so the first pass wrapped using the requested ``multi_line_output`` mode (e.g. VERTICAL or GRID) and only the second pass collapsed the result to VERTICAL_HANGING_INDENT, making the output unstable. """ to_sort = "from a.b.c import (d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s)\n" # Every multi-line wrap mode must reach a fixpoint after a single pass. for multi_line_output in (0, 1, 2, 3, 4, 5): first_pass = isort.code( to_sort, include_trailing_comma=True, split_on_trailing_comma=True, multi_line_output=multi_line_output, line_length=40, ) second_pass = isort.code( first_pass, include_trailing_comma=True, split_on_trailing_comma=True, multi_line_output=multi_line_output, line_length=40, ) assert first_pass == second_pass, ( f"not idempotent for multi_line_output={multi_line_output}" ) # The Black profile sets include_trailing_comma + split_on_trailing_comma; combining it # with an explicit wrap-mode override must remain stable too. black_override = isort.code(to_sort, profile="black", multi_line_output=1, line_length=40) assert black_override == isort.code( black_override, profile="black", multi_line_output=1, line_length=40 ) def test_noqa_wrap_mode_idempotent_with_existing_comment(): """Ensure ``multi_line_output=NOQA`` does not keep prepending ``NOQA`` to an import that already carries its own comment. In NOQA mode isort appends ``# NOQA`` to imports it cannot fit on one line. When the import also has its own comment (e.g. ``# leading``) the first pass correctly produces ``# NOQA leading``. Re-parsing that line gives a single combined comment ``"NOQA leading"``, so the old "already has NOQA" guard - which compared against the list of comments - no longer matched and isort prepended yet another ``NOQA`` on every subsequent run (``# NOQA NOQA leading``, ``# NOQA NOQA NOQA leading`` ...), never reaching a fixpoint. """ to_sort = ( "from a import ( # leading\n" " b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z\n" ")\n" ) first_pass = isort.code(to_sort, multi_line_output=7) assert first_pass == ( "from a import b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, " "v, w, x, y, z # NOQA leading\n" ) # A single NOQA must be present, and re-running must not add more of them. assert first_pass.count("NOQA") == 1 second_pass = isort.code(first_pass, multi_line_output=7) assert second_pass == first_pass assert second_pass.count("NOQA") == 1 def test_noqa_wrap_mode_does_not_accumulate_spaces_with_as_import(): """A long ``as`` import in NOQA mode must not grow extra spaces before ``# NOQA``. With ``force_single_line`` an aliased import that overflows the line length is emitted on its own line and gets a ``# NOQA`` appended. The auto-added comment is re-parsed on the next run and put back through ``add_to_line``, which used to keep the whitespace that had preceded the stripped ``#`` and then add the comment prefix on top of it - so every pass inserted two more spaces (``import x # NOQA`` -> ``import x # NOQA`` -> ...), never reaching a fixpoint. See issue #2394. """ to_sort = "from my_package.my_module import super_long_file_name as super_long_alias\n" first_pass = isort.code(to_sort, multi_line_output=7, force_single_line=True, line_length=40) assert first_pass == ( "from my_package.my_module import super_long_file_name as super_long_alias # NOQA\n" ) second_pass = isort.code( first_pass, multi_line_output=7, force_single_line=True, line_length=40 ) assert second_pass == first_pass def test_sort_reexports_respects_black_profile_issue_2280(): """``--sort-reexports`` must honor the active formatting config, not stdlib ``pprint``. ``isort.literal`` used to format the sorted ``__all__`` with stdlib ``pprint``, which only reads ``config.line_length`` and ignores ``include_trailing_comma`` and the quote style entirely. So a long ``__all__`` under ``--profile=black`` came back single-quoted, wrapped in ``pprint`` style (a bare continuation line prefixed with one space) and without a trailing comma - output that black immediately reformats. See issue #2280: https://github.com/pycqa/isort/issues/2280 The sorted list must instead match what black itself produces: one element per line, double quotes, a trailing comma and a hanging-indented closing bracket. """ test_input = """__all__ = [ "AliasAddress", "Address", "BankAccountConnection", "Certificate", "ConcessionFee", "ContactDetails", "Determination", ] """ expected_output = """__all__ = [ "Address", "AliasAddress", "BankAccountConnection", "Certificate", "ConcessionFee", "ContactDetails", "Determination", ] """ assert isort.code(test_input, profile="black", sort_reexports=True) == expected_output def test_literal_dict_sort_respects_black_profile_issue_2280(): """The ``# isort: dict`` literal sort shares the same formatter as ``--sort-reexports`` and must likewise honor the black profile rather than stdlib ``pprint`` (which produced single quotes, pprint-style wrapping and no trailing comma). Same root cause as #2280. """ test_input = ( "# isort: dict\n" + "d = {" + ", ".join(f"'key_{i:02d}': 'value_{i:02d}'" for i in (3, 1, 2, 0)) + "}\n" ) expected_output = ( "# isort: dict\nd = {\n" + "".join(f' "key_{i:02d}": "value_{i:02d}",\n' for i in range(4)) + "}\n" ) assert isort.code(test_input, profile="black") == expected_output def test_sort_reexports_output_is_black_stable_issue_2280(): """isort's sorted __all__ under the black profile must be a fixpoint for both isort and black (running either again changes nothing). See issue #2280.""" import black # noqa: PLC0415 from black.report import NothingChanged # noqa: PLC0415 source = ( "__all__ = [\n" + "".join(f' "Name{i:02d}",\n' for i in (5, 3, 9, 1, 7, 2, 8, 4, 6, 0)) + "]\n" ) first = isort.code(source, profile="black", sort_reexports=True) # isort is idempotent assert isort.code(first, profile="black", sort_reexports=True) == first # black leaves isort's output unchanged try: black_out = black.format_file_contents(first, fast=True, mode=black.FileMode()) except NothingChanged: black_out = first assert black_out == first def test_sort_reexports_check_mode_multiline_all_issue_2280(): """``--check`` on a multi-line ``__all__`` with ``--sort-reexports`` must not crash. Check mode routes output to a null stream whose ``tell()`` is always 0. The reexport handling used ``output_stream.seek(output_stream.tell() - len(first_line))`` to roll back over the opening line, which went negative and raised ``ValueError: Negative seek position``. Our black-compatible formatter emits multi-line ``__all__``, so ``isort --check`` began crashing on isort's own output. See issue #2280. """ # already-sorted, black-formatted multi-line __all__ (what isort itself now produces) sorted_all = "__all__ = [\n" + "".join(f' "Name{i:02d}",\n' for i in range(9)) + "]\n" # check must report "no changes" without raising, in both string and stream forms assert isort.check_code(sorted_all, show_diff=False, profile="black", sort_reexports=True) # and with imports before it (the realistic module case) with_imports = "from .core import A\n\n" + sorted_all checked = isort.code(with_imports, profile="black", sort_reexports=True) assert isort.check_code(checked, show_diff=False, profile="black", sort_reexports=True) def test_noqa_added_to_long_force_single_line_as_import_with_comment_issue_2093(): """A long ``as`` import with inline comment must get ``# NOQA`` in NOQA mode. With ``force_single_line`` an aliased import that carries an inline comment and overflows the line length must still receive ``# NOQA``. Previously ``with_comments()`` wrapped ``wrap.line()``, so the length check ran on the import without the comment and ``# NOQA`` was never added. """ to_sort = ( "from my_package.my_module import super_long_file_name as super_long_alias" " # type: ignore\n" ) first_pass = isort.code(to_sort, multi_line_output=7, force_single_line=True, line_length=40) assert first_pass == ( "from my_package.my_module import super_long_file_name as super_long_alias" " # type: ignore # NOQA\n" ) second_pass = isort.code( first_pass, multi_line_output=7, force_single_line=True, line_length=40 ) assert second_pass == first_pass def test_noqa_added_to_long_as_import_with_opening_comment_issue_2093(): """A long ``as`` import with opening-line comment must get ``# NOQA`` in NOQA mode. When ``use_parentheses`` is enabled, opening-line comments are kept on the ``from X import (`` line. The ``wrap.line()`` call ran before the comment was attached, so it saw a short import and never added ``# NOQA``. The fix re-runs ``wrap.line()`` in NOQA mode after the comment is attached. """ to_sort = ( "from my_package.my_module import (\n" " super_long_file_name as super_long_alias # type: ignore\n" ")\n" ) first_pass = isort.code(to_sort, multi_line_output=7, line_length=40) assert first_pass == ( "from my_package.my_module import super_long_file_name as super_long_alias" " # type: ignore # NOQA\n" ) second_pass = isort.code(first_pass, multi_line_output=7, line_length=40) assert second_pass == first_pass def test_noqa_added_to_long_combined_straight_imports_issue_2093(): """Long combined straight imports must get ``# NOQA`` in NOQA mode. With ``combine_straight_imports`` enabled, multiple ``import`` statements are merged into a single ``import a, b, c, ...`` line. Previously this line was appended directly without going through ``wrap.line()``, so ``# NOQA`` was never added even when it exceeded the line length. """ to_sort = "import a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p\n" first_pass = isort.code( to_sort, multi_line_output=7, combine_straight_imports=True, line_length=40 ) assert first_pass == ("import a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p # NOQA\n") second_pass = isort.code( first_pass, multi_line_output=7, combine_straight_imports=True, line_length=40 ) assert second_pass == first_pass def test_noqa_added_to_long_straight_import_issue_2093(): """Long straight imports must get ``# NOQA`` in NOQA mode. Straight imports (``import x`` or ``import x as y``) that exceed the line length were emitted directly without going through ``wrap.line()``, so ``# NOQA`` was never added. The fix wraps each import through ``wrap.line()``. """ to_sort = "import aaaa_long_module_name as bbbb_long_alias_name\n" first_pass = isort.code(to_sort, multi_line_output=7, line_length=40) assert first_pass == "import aaaa_long_module_name as bbbb_long_alias_name # NOQA\n" second_pass = isort.code(first_pass, multi_line_output=7, line_length=40) assert second_pass == first_pass def test_noqa_added_to_long_as_import_with_opening_line_comment_issue_2093(): """A long ``as`` import with an opening-line comment and ``use_parentheses``. When ``use_parentheses`` is enabled and the import carries an opening-line comment, the comment is attached to the first line after wrapping. In NOQA mode ``wrap.line()`` is re-run on that line so ``# NOQA`` is added when it still exceeds the line length. In non-NOQA modes the comment is preserved on the ``from X import (`` line without ``# NOQA``. """ to_sort = ( "from my_package.my_module import super_long_file_name as super_long_alias" " # type: ignore\n" ) # NOQA mode: # NOQA is appended after the opening-line comment. first_pass = isort.code(to_sort, multi_line_output=7, use_parentheses=True, line_length=40) assert first_pass == ( "from my_package.my_module import super_long_file_name as super_long_alias" " # NOQA # type: ignore\n" ) # Non-NOQA mode (grid): the comment stays on the opening parenthesis line. grid_pass = isort.code(to_sort, multi_line_output=0, use_parentheses=True, line_length=40) assert grid_pass == ( "from my_package.my_module import ( # type: ignore\n" " super_long_file_name as super_long_alias)\n" ) # With ignore_comments the opening-line comment is stripped, leaving # NOQA. ignored_pass = isort.code( to_sort, multi_line_output=7, use_parentheses=True, ignore_comments=True, line_length=40, ) assert ignored_pass == ( "from my_package.my_module import super_long_file_name as super_long_alias # NOQA\n" ) def test_noqa_added_to_long_combined_straight_imports_with_bare_comment_issue_2093(): """A bare ``#`` comment with ``combine_straight_imports`` gets ``# NOQA``. A bare ``#`` with no text parses to an empty comment string. When such a comment is present on a combined straight import that exceeds the line length, the empty comment branch must still pass through ``wrap.line()`` so that ``# NOQA`` is added in NOQA mode. """ to_sort = "import a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p #\n" first_pass = isort.code( to_sort, multi_line_output=7, combine_straight_imports=True, line_length=40 ) assert first_pass == ("import a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p # # NOQA\n") def test_isort_skip_is_honored_with_future_import_issue_2092(): """A per-line ``isort: skip`` must be honored even when a ``__future__`` import is present. ``__future__`` imports are always floated to the very top, which used to make isort splice the sorted import block ahead of a following ``# isort: skip`` line and relocate the skipped import below the block - silently violating the skip directive. The skipped import must stay exactly where it is, and the result must be stable across re-runs. See issue #2092. """ to_sort = ( "from __future__ import annotations\n" "\n" "from foo import bar # isort: skip\n" "from bar import baz\n" ) first_pass = isort.code(to_sort) assert first_pass == to_sort assert isort.check_code(to_sort, show_diff=True) # An interleaved skip (between two regular imports) must keep its position rather than # being sorted to the bottom of the block, and must be idempotent. interleaved = "import aaa\nfrom foo import bar # isort: skip\nimport ccc\n" sorted_interleaved = isort.code(interleaved) lines = sorted_interleaved.splitlines() skip_index = next(i for i, line in enumerate(lines) if "# isort: skip" in line) assert lines.index("import ccc") > skip_index # skip not relocated below the block assert isort.code(sorted_interleaved) == sorted_interleaved def test_isort_does_not_drop_aliased_import_when_plain_name_has_a_comment(): """A name imported both plainly (with a trailing comment) and aliased must keep its alias when a sibling sorts ahead of it. ``from x import m # c`` and ``from x import m as z`` combine into one group, and the alias is only emitted while ``m`` leads that group. When ``aaa`` sorts before ``m`` the comment pass used to consume ``m`` and drop ``m as z``, which only surfaced on a second run once the group was re-sorted. """ to_sort = "from x import aaa\nfrom x import m # c\nfrom x import m as z\n" assert isort.code(to_sort) == to_sort # The same holds for a relative (local-folder) import. relative = "from . import bar, one\nfrom . import one as zzz # NOQA\n" relative_sorted = isort.code(relative) expected = "from . import bar\nfrom . import one # NOQA\nfrom . import one as zzz\n" assert relative_sorted == expected assert isort.code(relative_sorted) == relative_sorted def test_add_import_keeps_a_prefixed_module_docstring_first_issue_1893(): """``add_imports`` must not move an import above a module docstring that carries a string prefix (``r``, ``b``, ``f``, ``u`` or a legal combination of them), as reported in issue #1893: https://github.com/pycqa/isort/issues/1893 """ # The exact input from the report: the added import was emitted a second time, above # the docstring. reported = 'r"""module docstring\n"""\nfrom __future__ import annotations\n' assert isort.code(reported, add_imports=["from __future__ import annotations"]) == reported # The import still has to be added, below the docstring -- where an unprefixed # docstring already puts it. with_code = 'r"""module docstring\n"""\n\nx = 1\n' assert ( isort.code(with_code, add_imports=["import a"]) == 'r"""module docstring\n"""\n\nimport a\n\nx = 1\n' ) # Every prefix isort itself recognises, in both quote flavours and in lower, # upper and mixed case. for prefix in sorted(STRING_PREFIXES): for cased in sorted({prefix, prefix.upper(), prefix.capitalize()}): for quote in ('"""', "'''"): docstring = f"{cased}{quote}module docstring\n{quote}\n" source = docstring + "import a\n" assert isort.code(source, add_imports=["import a"]) == source, cased + quote isort-9.0.1/tests/unit/test_settings.py0000644000000000000000000002330213615410400015173 0ustar00import os import sys from pathlib import Path import pytest from isort import exceptions, settings from isort.settings import Config from isort.wrap_modes import WrapModes class TestConfig: instance = Config() def test_init(self): assert Config() def test_init_unsupported_settings_fails_gracefully(self): with pytest.raises(exceptions.UnsupportedSettings): Config(apply=True) with pytest.raises(exceptions.UnsupportedSettings) as error: Config(apply=True) assert error.value.unsupported_settings == {"apply": {"value": True, "source": "runtime"}} def test_known_settings(self): assert Config(known_third_party=["one"]).known_third_party == frozenset({"one"}) assert Config(known_thirdparty=["two"]).known_third_party == frozenset({"two"}) assert Config( known_third_party=["one"], known_thirdparty=["two"] ).known_third_party == frozenset({"one"}) def test_invalid_settings_path(self): with pytest.raises(exceptions.InvalidSettingsPath): Config(settings_path="this_couldnt_possibly_actually_exists/could_it") def test_invalid_pyversion(self): with pytest.raises(ValueError, match=r"The python version 10 is not supported."): Config(py_version=10) def test_invalid_profile(self): with pytest.raises(exceptions.ProfileDoesNotExist): Config(profile="blackandwhitestylemixedwithpep8") def test_is_skipped(self): assert Config().is_skipped(Path("C:\\path\\isort.py")) assert Config(skip=["/path/isort.py"]).is_skipped(Path("C:\\path\\isort.py")) def test_is_supported_filetype(self): assert self.instance.is_supported_filetype("file.py") assert self.instance.is_supported_filetype("file.pyi") assert self.instance.is_supported_filetype("file.pyx") assert self.instance.is_supported_filetype("file.pxd") assert not self.instance.is_supported_filetype("file.pyc") assert not self.instance.is_supported_filetype("file.txt") assert not self.instance.is_supported_filetype("file.pex") def test_is_supported_filetype_ioerror(self, tmpdir): does_not_exist = tmpdir.join("fake.txt") assert not self.instance.is_supported_filetype(str(does_not_exist)) def test_is_supported_filetype_shebang(self, tmpdir): path = tmpdir.join("myscript") path.write("#!/usr/bin/env python\n") assert self.instance.is_supported_filetype(str(path)) def test_is_supported_filetype_editor_backup(self, tmpdir): path = tmpdir.join("myscript~") path.write("#!/usr/bin/env python\n") assert not self.instance.is_supported_filetype(str(path)) def test_is_supported_filetype_defaults(self, tmpdir): assert self.instance.is_supported_filetype(str(tmpdir.join("stub.pyi"))) assert self.instance.is_supported_filetype(str(tmpdir.join("source.py"))) assert self.instance.is_supported_filetype(str(tmpdir.join("source.pyx"))) def test_is_supported_filetype_configuration(self, tmpdir): config = Config(supported_extensions=("pyx",), blocked_extensions=("py",)) assert config.is_supported_filetype(str(tmpdir.join("stub.pyx"))) assert not config.is_supported_filetype(str(tmpdir.join("stub.py"))) @pytest.mark.skipif( sys.platform == "win32", reason="cannot create fifo file on Windows platform" ) def test_is_supported_filetype_fifo(self, tmpdir): fifo_file = os.path.join(tmpdir, "fifo_file") os.mkfifo(fifo_file) assert not self.instance.is_supported_filetype(fifo_file) def test_src_paths_are_combined_and_deduplicated(self): src_paths = ["src", "tests"] src_full_paths = (Path(os.getcwd()) / f for f in src_paths) assert sorted(Config(src_paths=src_paths * 2).src_paths) == sorted(src_full_paths) def test_src_paths_supports_glob_expansion(self, tmp_path): libs = tmp_path / "libs" libs.mkdir() requests = libs / "requests" requests.mkdir() beautifulpasta = libs / "beautifulpasta" beautifulpasta.mkdir() assert sorted(Config(directory=tmp_path, src_paths=["libs/*"]).src_paths) == sorted( (beautifulpasta, requests) ) def test_deprecated_multi_line_output(self): assert Config(multi_line_output=6).multi_line_output == WrapModes.VERTICAL_GRID_GROUPED # noqa def test_as_list(): assert settings._as_list([" one "]) == ["one"] assert settings._as_list("one,two") == ["one", "two"] def _write_simple_settings(tmp_file): tmp_file.write_text( """ [isort] force_grid_wrap=true """, "utf8", ) def test_find_config(tmpdir): tmp_config = tmpdir.join(".isort.cfg") # can't find config if it has no relevant section tmp_config.write_text( """ [section] force_grid_wrap=true """, "utf8", ) assert not settings._find_config(str(tmpdir))[1] # or if it is malformed tmp_config.write_text("""arstoyrsyan arienrsaeinrastyngpuywnlguyn354q^%$)(%_)@$""", "utf8") assert not settings._find_config(str(tmpdir))[1] # can when it has either a file format, or generic relevant section _write_simple_settings(tmp_config) assert settings._find_config(str(tmpdir))[1] def test_find_config_deep(tmpdir): # can't find config if it is further up than MAX_CONFIG_SEARCH_DEPTH dirs = [f"dir{i}" for i in range(settings.MAX_CONFIG_SEARCH_DEPTH + 1)] tmp_dirs = tmpdir.ensure(*dirs, dirs=True) tmp_config = tmpdir.join("dir0", ".isort.cfg") _write_simple_settings(tmp_config) assert not settings._find_config(str(tmp_dirs))[1] # but can find config if it is MAX_CONFIG_SEARCH_DEPTH up one_parent_up = os.path.split(str(tmp_dirs))[0] assert settings._find_config(one_parent_up)[1] def test_get_config_data(tmpdir): test_config = tmpdir.join("test_config.editorconfig") test_config.write_text( """ root = true [*.{js,py}] indent_style=tab indent_size=tab [*.py] force_grid_wrap=false comment_prefix="text" [*.{java}] indent_style = space """, "utf8", ) loaded_settings = settings._get_config_data( str(test_config), sections=settings.CONFIG_SECTIONS[".editorconfig"] ) assert loaded_settings assert loaded_settings["comment_prefix"] == "text" assert loaded_settings["force_grid_wrap"] == 0 assert loaded_settings["indent"] == "\t" assert isinstance(loaded_settings["source"], str) assert str(tmpdir) in loaded_settings["source"] def test_editorconfig_without_sections(tmpdir): test_config = tmpdir.join("test_config.editorconfig") test_config.write_text("\nroot = true\n", "utf8") loaded_settings = settings._get_config_data(str(test_config), sections=("*.py",)) assert not loaded_settings def test_get_config_data_with_toml_and_utf8(tmpdir): test_config = tmpdir.join("pyproject.toml") # Exception: UnicodeDecodeError: 'gbk' codec can't decode byte 0x84 in position 57 test_config.write_text( """ [project] description = "基于FastAPI + Mysql的 TodoList" # Exception: UnicodeDecodeError name = "TodoList" version = "0.1.0" [tool.isort] multi_line_output = 3 """, "utf8", ) loaded_settings = settings._get_config_data( str(test_config), sections=settings.CONFIG_SECTIONS["pyproject.toml"] ) assert loaded_settings assert isinstance(loaded_settings["source"], str) assert str(tmpdir) in loaded_settings["source"] def test_as_bool(): assert settings._as_bool("TrUe") is True assert settings._as_bool("true") is True assert settings._as_bool("t") is True assert settings._as_bool("FALSE") is False assert settings._as_bool("faLSE") is False assert settings._as_bool("f") is False with pytest.raises(ValueError, match="invalid truth value"): settings._as_bool("") with pytest.raises(ValueError, match="invalid truth value falsey"): settings._as_bool("falsey") with pytest.raises(ValueError, match="invalid truth value truthy"): settings._as_bool("truthy") def test_find_all_configs(tmpdir): setup_cfg = """ [isort] profile=django """ pyproject_toml = """ [tool.isort] profile = "hug" """ isort_cfg = """ [settings] profile=black """ pyproject_toml_broken = """ [tool.isorts] something = nothing """ dir1 = tmpdir / "subdir1" dir2 = tmpdir / "subdir2" dir3 = tmpdir / "subdir3" dir4 = tmpdir / "subdir4" dir1.mkdir() dir2.mkdir() dir3.mkdir() dir4.mkdir() setup_cfg_file = dir1 / "setup.cfg" setup_cfg_file.write_text(setup_cfg, "utf-8") pyproject_toml_file = dir2 / "pyproject.toml" pyproject_toml_file.write_text(pyproject_toml, "utf-8") isort_cfg_file = dir3 / ".isort.cfg" isort_cfg_file.write_text(isort_cfg, "utf-8") pyproject_toml_file_broken = dir4 / "pyproject.toml" pyproject_toml_file_broken.write_text(pyproject_toml_broken, "utf-8") config_trie = settings.find_all_configs(str(tmpdir)) config_info_1 = config_trie.search(str(dir1 / "test1.py")) assert config_info_1[0] == str(setup_cfg_file) assert config_info_1[0] == str(setup_cfg_file) assert config_info_1[1]["profile"] == "django" config_info_2 = config_trie.search(str(dir2 / "test2.py")) assert config_info_2[0] == str(pyproject_toml_file) assert config_info_2[0] == str(pyproject_toml_file) assert config_info_2[1]["profile"] == "hug" config_info_3 = config_trie.search(str(dir3 / "test3.py")) assert config_info_3[0] == str(isort_cfg_file) assert config_info_3[0] == str(isort_cfg_file) assert config_info_3[1]["profile"] == "black" config_info_4 = config_trie.search(str(tmpdir / "file4.py")) assert config_info_4[0] == "default" isort-9.0.1/tests/unit/test_ticketed_features.py0000644000000000000000000006757313615410400017047 0ustar00"""A growing set of tests designed to ensure when isort implements a feature described in a ticket it fully works as defined in the associated ticket. """ import warnings from functools import partial from io import StringIO import pytest import isort from isort import Config, exceptions def test_semicolon_ignored_for_dynamic_lines_after_import_issue_1178(): """Test to ensure even if a semicolon is in the decorator in the line following an import the correct line spacing determination will be made. See: https://github.com/pycqa/isort/issues/1178. """ assert isort.check_code( """ import pytest @pytest.mark.skip(';') def test_thing(): pass """, show_diff=True, ) def test_isort_automatically_removes_duplicate_aliases_issue_1193(): """Test to ensure isort can automatically remove duplicate aliases. See: https://github.com/pycqa/isort/issues/1281 """ assert isort.check_code("from urllib import parse as parse\n", show_diff=True) assert ( isort.code("from urllib import parse as parse", remove_redundant_aliases=True) == "from urllib import parse\n" ) assert isort.check_code("import os as os\n", show_diff=True) assert isort.code("import os as os", remove_redundant_aliases=True) == "import os\n" def test_isort_enables_floating_imports_to_top_of_module_issue_1228(): """Test to ensure isort will allow floating all non-indented imports to the top of a file. See: https://github.com/pycqa/isort/issues/1228. """ assert ( isort.code( """ import os def my_function_1(): pass import sys def my_function_2(): pass """, float_to_top=True, ) == """ import os import sys def my_function_1(): pass def my_function_2(): pass """ ) assert ( isort.code( """ import os def my_function_1(): pass # isort: split import sys def my_function_2(): pass """, float_to_top=True, ) == """ import os def my_function_1(): pass # isort: split import sys def my_function_2(): pass """ ) assert ( isort.code( """ import os def my_function_1(): pass # isort: off import b import a def y(): pass # isort: on import b def my_function_2(): pass import a """, float_to_top=True, ) == """ import os def my_function_1(): pass # isort: off import b import a def y(): pass # isort: on import a import b def my_function_2(): pass """ ) def test_isort_provides_official_api_for_diff_output_issue_1335(): """Test to ensure isort API for diff capturing allows capturing diff without sys.stdout. See: https://github.com/pycqa/isort/issues/1335. """ diff_output = StringIO() isort.code("import b\nimport a\n", show_diff=diff_output) diff_output.seek(0) assert "+import a" in diff_output.read() def test_isort_warns_when_known_sections_dont_match_issue_1331(): """Test to ensure that isort warns if there is a mismatch between sections and known_sections. See: https://github.com/pycqa/isort/issues/1331. """ assert ( isort.place_module( "bot_core", config=Config( known_robotlocomotion_upstream=["bot_core"], sections=["ROBOTLOCOMOTION_UPSTREAM", "THIRDPARTY"], ), ) == "ROBOTLOCOMOTION_UPSTREAM" ) with pytest.warns(UserWarning): assert ( isort.place_module( "bot_core", config=Config( known_robotlocomotion_upstream=["bot_core"], sections=["ROBOTLOOMOTION_UPSTREAM", "THIRDPARTY"], ), ) == "THIRDPARTY" ) with pytest.warns(UserWarning): assert ( isort.place_module( "bot_core", config=Config(known_robotlocomotion_upstream=["bot_core"]) ) == "THIRDPARTY" ) def test_isort_supports_append_only_imports_issue_727(): """Test to ensure isort provides a way to only add imports as an append. See: https://github.com/pycqa/isort/issues/727. """ assert isort.code("", add_imports=["from __future__ import absolute_imports"]) == "" assert ( isort.code("import os", add_imports=["from __future__ import absolute_imports"]) == """from __future__ import absolute_imports import os """ ) # issue 1838: don't append in middle of class assert isort.check_code( '''class C: """a """ # comment ''', append_only=True, add_imports=["from __future__ import annotations"], show_diff=True, ) def test_isort_supports_shared_profiles_issue_970(): """Test to ensure isort provides a way to use shared profiles. See: https://github.com/pycqa/isort/issues/970. """ assert isort.code("import a", profile="example") == "import a\n" # shared profile assert isort.code("import a", profile="black") == "import a\n" # bundled profile with pytest.raises(exceptions.ProfileDoesNotExist): assert isort.code("import a", profile="madeupfake") == "import a\n" # non-existent profile def test_treating_comments_as_code_issue_1357(): """Test to ensure isort provides a way to treat comments as code. See: https://github.com/pycqa/isort/issues/1357 """ assert ( isort.code( """# %% import numpy as np np.array([1,2,3]) # %% import pandas as pd pd.Series([1,2,3]) # %% # This is a comment on the second import import pandas as pd pd.Series([4,5,6])""", treat_comments_as_code=["# comment1", "# %%"], ) == """# %% import numpy as np np.array([1,2,3]) # %% import pandas as pd pd.Series([1,2,3]) # %% # This is a comment on the second import import pandas as pd pd.Series([4,5,6]) """ ) assert ( isort.code( """# %% import numpy as np np.array([1,2,3]) # %% import pandas as pd pd.Series([1,2,3]) # %% # This is a comment on the second import import pandas as pd pd.Series([4,5,6])""", treat_comments_as_code=["# comment1", "# %%"], float_to_top=True, ) == """# %% import numpy as np # This is a comment on the second import import pandas as pd np.array([1,2,3]) # %% pd.Series([1,2,3]) # %% pd.Series([4,5,6]) """ ) assert ( isort.code( """# %% import numpy as np np.array([1,2,3]) # %% import pandas as pd pd.Series([1,2,3]) # %% # This is a comment on the second import import pandas as pd pd.Series([4,5,6])""", treat_all_comments_as_code=True, ) == """# %% import numpy as np np.array([1,2,3]) # %% import pandas as pd pd.Series([1,2,3]) # %% # This is a comment on the second import import pandas as pd pd.Series([4,5,6]) """ ) assert ( isort.code( """import b # these are special imports that have to do with installing X plugin import c import a """, treat_all_comments_as_code=True, ) == """import b # these are special imports that have to do with installing X plugin import a import c """ ) def test_isort_allows_setting_import_types_issue_1181(): """Test to ensure isort provides a way to set the type of imports. See: https://github.com/pycqa/isort/issues/1181 """ assert isort.code("from x import AA, Big, variable") == "from x import AA, Big, variable\n" assert ( isort.code("from x import AA, Big, variable", constants=["variable"]) == "from x import AA, variable, Big\n" ) assert ( isort.code("from x import AA, Big, variable", variables=["AA"]) == "from x import Big, AA, variable\n" ) assert ( isort.code( "from x import AA, Big, variable", constants=["Big"], variables=["AA"], classes=["variable"], ) == "from x import Big, variable, AA\n" ) def test_isort_enables_deduping_section_headers_issue_953(): """isort should provide a way to only have identical import headings show up once. See: https://github.com/pycqa/isort/issues/953 """ isort_code = partial( isort.code, config=Config( import_heading_firstparty="Local imports.", import_heading_localfolder="Local imports.", dedup_headings=True, known_first_party=["isort"], ), ) assert ( isort_code("from . import something") == """# Local imports. from . import something """ ) assert ( isort_code( """from isort import y from . import something""" ) == """# Local imports. from isort import y from . import something """ ) assert isort_code("import os") == "import os\n" def test_isort_doesnt_remove_as_imports_when_combine_star_issue_1380(): """Test to ensure isort will not remove as imports along side other imports when requested to combine star imports together. See: https://github.com/PyCQA/isort/issues/1380 """ test_input = """ from a import a from a import * from a import b from a import b as y from a import c """ assert ( isort.code( test_input, combine_star=True, ) == isort.code(test_input, combine_star=True, force_single_line=True) == isort.code( test_input, combine_star=True, force_single_line=True, combine_as_imports=True, ) == """ from a import * from a import b as y """ ) def test_isort_support_custom_groups_above_stdlib_that_contain_stdlib_modules_issue_1407(): """Test to ensure it is possible to declare custom groups above standard library that include modules from the standard library. See: https://github.com/PyCQA/isort/issues/1407 """ assert isort.check_code( """ from __future__ import annotations from typing import * from pathlib import Path """, known_typing=["typing"], sections=["FUTURE", "TYPING", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"], no_lines_before=["TYPING"], show_diff=True, ) def test_isort_intelligently_places_noqa_comments_issue_1456(): assert isort.check_code( """ from my.horribly.long.import.line.that.just.keeps.on.going.and.going.and.going import ( # noqa my_symbol, ) """, force_single_line=True, show_diff=True, multi_line_output=3, include_trailing_comma=True, force_grid_wrap=0, use_parentheses=True, line_length=79, ) assert isort.check_code( """ from my.horribly.long.import.line.that.just.keeps.on.going.and.going.and.going import ( my_symbol, ) """, force_single_line=True, show_diff=True, multi_line_output=3, include_trailing_comma=True, force_grid_wrap=0, use_parentheses=True, line_length=79, ) assert isort.check_code( """ from my.horribly.long.import.line.that.just.keeps.on.going.and.going.and.going import ( # noqa my_symbol ) """, force_single_line=True, use_parentheses=True, multi_line_output=3, line_length=79, show_diff=True, ) assert isort.check_code( """ from my.horribly.long.import.line.that.just.keeps.on.going.and.going.and.going import ( my_symbol ) """, force_single_line=True, use_parentheses=True, multi_line_output=3, line_length=79, show_diff=True, ) # see: https://github.com/PyCQA/isort/issues/1415 assert isort.check_code( "from dials.test.algorithms.spot_prediction." "test_scan_static_reflection_predictor import ( # noqa: F401\n" " data as static_test,\n)\n", profile="black", show_diff=True, ) def test_isort_respects_quiet_from_sort_file_api_see_1461(capsys, tmpdir): """Test to ensure isort respects the quiet API parameter when passed in via the API. See: https://github.com/PyCQA/isort/issues/1461. """ settings_file = tmpdir.join(".isort.cfg") custom_settings_file = tmpdir.join(".custom.isort.cfg") tmp_file = tmpdir.join("file.py") tmp_file.write("import b\nimport a\n") isort.file(tmp_file) out, error = capsys.readouterr() assert not error assert "Fixing" in out # When passed in directly as a setting override tmp_file.write("import b\nimport a\n") isort.file(tmp_file, quiet=True) out, error = capsys.readouterr() assert not error assert not out # Present in an automatically loaded configuration file settings_file.write( """ [isort] quiet = true """ ) tmp_file.write("import b\nimport a\n") isort.file(tmp_file) out, error = capsys.readouterr() assert not error assert not out # In a custom configuration file settings_file.write( """ [isort] quiet = false """ ) custom_settings_file.write( """ [isort] quiet = true """ ) tmp_file.write("import b\nimport a\n") isort.file(tmp_file, settings_file=str(custom_settings_file)) out, error = capsys.readouterr() assert not error assert not out # Reused configuration object custom_config = Config(settings_file=str(custom_settings_file)) isort.file(tmp_file, config=custom_config) out, error = capsys.readouterr() assert not error assert not out def test_isort_should_warn_on_empty_custom_config_issue_1433(tmpdir): """Feedback should be provided when a user provides a custom settings file that has no discoverable configuration. See: https://github.com/PyCQA/isort/issues/1433 """ settings_file = tmpdir.join(".custom.cfg") settings_file.write( """ [settings] quiet = true """ ) with pytest.warns(UserWarning): assert not Config(settings_file=str(settings_file)).quiet settings_file.write( """ [isort] quiet = true """ ) with warnings.catch_warnings(): warnings.simplefilter("error") assert Config(settings_file=str(settings_file)).quiet def test_float_to_top_should_respect_existing_newlines_between_imports_issue_1502(): """When a file has an existing top of file import block before code but after comments isort's float to top feature should respect the existing spacing between the top file comment and the import statements. See: https://github.com/PyCQA/isort/issues/1502 """ assert isort.check_code( """#!/usr/bin/env bash '''My comment''' import a x = 1 """, float_to_top=True, show_diff=True, ) assert isort.check_code( """#!/usr/bin/env bash '''My comment''' import a x = 1 """, float_to_top=True, show_diff=True, ) assert ( isort.code( """#!/usr/bin/env bash '''My comment''' import a x = 1 """, float_to_top=True, add_imports=["import b"], ) == """#!/usr/bin/env bash '''My comment''' import a import b x = 1 """ ) assert ( isort.code( """#!/usr/bin/env bash '''My comment''' def my_function(): pass import a """, float_to_top=True, ) == """#!/usr/bin/env bash '''My comment''' import a def my_function(): pass """ ) assert ( isort.code( """#!/usr/bin/env bash '''My comment''' def my_function(): pass """, add_imports=["import os"], float_to_top=True, ) == """#!/usr/bin/env bash '''My comment''' import os def my_function(): pass """ ) def test_api_to_allow_custom_diff_and_output_stream_1583(capsys, tmpdir): """isort should provide a way from the Python API to process an existing file and output to a stream the new version of that file, as well as a diff to a different stream. See: https://github.com/PyCQA/isort/issues/1583 """ tmp_file = tmpdir.join("file.py") tmp_file.write("import b\nimport a\n") isort_diff = StringIO() isort_output = StringIO() isort.file(tmp_file, show_diff=isort_diff, output=isort_output) _, error = capsys.readouterr() assert not error isort_diff.seek(0) isort_diff_content = isort_diff.read() assert "+import a" in isort_diff_content assert " import b" in isort_diff_content assert "-import a" in isort_diff_content isort_output.seek(0) assert isort_output.read().splitlines() == ["import a", "import b"] # should still work with no diff produced tmp_file2 = tmpdir.join("file2.py") tmp_file2.write("import a\nimport b\n") isort_diff2 = StringIO() isort_output2 = StringIO() isort.file(tmp_file2, show_diff=isort_diff2, output=isort_output2) _, error = capsys.readouterr() assert not error isort_diff2.seek(0) assert not isort_diff2.read() def test_autofix_mixed_indent_imports_1575(): """isort should automatically fix import statements that are sent in with incorrect mixed indentation. See: https://github.com/PyCQA/isort/issues/1575 """ assert ( isort.code( """ import os import os """ ) == """ import os """ ) assert ( isort.code( """ def one(): import os import os """ ) == """ def one(): import os import os """ ) assert ( isort.code( """ import os import os import os import os import os """ ) == """ import os """ ) def test_indented_import_headings_issue_1604(): """Test to ensure it is possible to toggle import headings on indented import sections See: https://github.com/PyCQA/isort/issues/1604 """ assert ( isort.code( """ import numpy as np def function(): import numpy as np """, import_heading_thirdparty="External imports", ) == """ # External imports import numpy as np def function(): # External imports import numpy as np """ ) assert ( isort.code( """ import numpy as np def function(): import numpy as np """, import_heading_thirdparty="External imports", indented_import_headings=False, ) == """ # External imports import numpy as np def function(): import numpy as np """ ) def test_isort_auto_detects_and_ignores_invalid_from_imports_issue_1688(): """isort should automatically detect and ignore incorrectly written from import statements see: https://github.com/PyCQA/isort/issues/1688 """ assert ( isort.code( """ from package1 import alright from package2 imprt and_its_gone from package3 import also_ok """ ) == """ from package1 import alright from package2 imprt and_its_gone from package3 import also_ok """ ) def test_isort_allows_reversing_sort_order_issue_1645(): """isort allows reversing the sort order for those who prefer Z or longer imports first. see: https://github.com/PyCQA/isort/issues/1688 """ assert ( isort.code( """ from xxx import ( g, hi, def, abcd, ) """, profile="black", reverse_sort=True, length_sort=True, line_length=20, ) == """ from xxx import ( abcd, def, hi, g, ) """ ) def test_isort_can_push_star_imports_above_others_issue_1504(): """isort should provide a way to push star imports above other imports to avoid explicit imports from being overwritten. see: https://github.com/PyCQA/isort/issues/1504 """ assert ( ( isort.code( """ from ._bar import Any, All, Not from ._foo import a, * """, star_first=True, ) ) == """ from ._foo import * from ._foo import a from ._bar import All, Any, Not """ ) def test_isort_can_combine_reverse_sort_with_force_sort_within_sections_issue_1726(): """isort should support reversing import order even with force sort within sections turned on. See: https://github.com/PyCQA/isort/issues/1726 """ assert ( isort.code( """ import blaaa from bl4aaaaaaaaaaaaaaaa import r import blaaaaaaaaaaaa import bla import blaaaaaaa from bl1aaaaaaaaaaaaaa import this_is_1 from bl2aaaaaaa import THIIIIIIIIIIIISS_is_2 from bl3aaaaaa import less """, length_sort=True, reverse_sort=True, force_sort_within_sections=True, ) == """ from bl2aaaaaaa import THIIIIIIIIIIIISS_is_2 from bl1aaaaaaaaaaaaaa import this_is_1 from bl4aaaaaaaaaaaaaaaa import r from bl3aaaaaa import less import blaaaaaaaaaaaa import blaaaaaaa import blaaa import bla """ ) def test_isort_can_turn_off_import_adds_with_action_comment_issue_1737(): assert ( isort.code( """ import os """, add_imports=[ "from __future__ import absolute_imports", "from __future__ import annotations", ], ) == """ from __future__ import absolute_imports, annotations import os """ ) assert isort.check_code( """ # isort: dont-add-imports import os """, show_diff=True, add_imports=[ "from __future__ import absolute_imports", "from __future__ import annotations", ], ) assert ( isort.code( """ # isort: dont-add-import: from __future__ import annotations import os """, add_imports=[ "from __future__ import absolute_imports", "from __future__ import annotations", ], ) == """ # isort: dont-add-import: from __future__ import annotations from __future__ import absolute_imports import os """ ) def test_sort_configurable_sort_issue_1732() -> None: """Test support for pluggable isort sort functions.""" test_input = ( "from bob2.apples2 import aardvark as aardvark2\n" "from bob.apples import aardvark \n" "import module9\n" "import module10\n" "import module200\n" ) assert isort.code(test_input, sort_order="native") == ( "import module10\n" "import module200\n" "import module9\n" "from bob.apples import aardvark\n" "from bob2.apples2 import aardvark as aardvark2\n" ) assert ( isort.code(test_input, sort_order="natural") == isort.code(test_input) == ( "import module9\n" "import module10\n" "import module200\n" "from bob2.apples2 import aardvark as aardvark2\n" "from bob.apples import aardvark\n" ) ) assert ( isort.code(test_input, sort_order="natural_plus") == isort.code(test_input) == ( "import module9\n" "import module10\n" "import module200\n" "from bob2.apples2 import aardvark as aardvark2\n" "from bob.apples import aardvark\n" ) ) with pytest.raises(exceptions.SortingFunctionDoesNotExist): isort.code(test_input, sort_order="round") def test_cython_pure_python_imports_2062(): """Test to ensure an import form a cython.cimports remains import, not cimport. See: https://github.com/pycqa/isort/issues/2062. """ assert isort.check_code( """ import cython from cython.cimports.libc import math def use_libc_math(): return math.ceil(5.5) """, show_diff=True, ) def test_unindented_comment_in_indented_block_issue_1899(): """Test that unindented comments before indented imports are not corrupted. See: https://github.com/PyCQA/isort/issues/1899 """ test_input = """import sys if True: # this will get cut off import os """ assert isort.code(test_input) == test_input def test_sort_separate_packages_issue_2104(): """ Test to ensure that packages within a section can be separated by blank lines. See: https://github.com/PyCQA/isort/issues/2104 """ # Base case as described in issue assert ( isort.code( """ import os import sys from django.db.models.signals import m2m_changed from django.utils import functional from django_filters import BooleanFilter from junitparser import JUnitXml from junitparser import TestSuite from loguru import logger """, force_single_line=True, separate_packages=["THIRDPARTY"], ) == """ import os import sys from django.db.models.signals import m2m_changed from django.utils import functional from django_filters import BooleanFilter from junitparser import JUnitXml from junitparser import TestSuite from loguru import logger """ ) # Check that multiline comments aren't broken up assert ( isort.code( """ from junitparser import TestSuite # Some multiline # comment from loguru import logger """, force_single_line=True, separate_packages=["THIRDPARTY"], ) == """ from junitparser import TestSuite # Some multiline # comment from loguru import logger """ ) # Check it works for custom sections assert ( isort.code( """ import os from package2 import bar from package1 import foo """, force_single_line=True, known_MYPACKAGES=["package1", "package2"], sections=["STDLIB", "MYPACKAGES"], separate_packages=["MYPACKAGES"], ) == """ import os from package1 import foo from package2 import bar """ ) # Check it works for packages with deeper nesting assert ( isort.code( """ import os from package2 import bar from package1.a.b import foo from package1.a.c import baz """, force_single_line=True, known_MYPACKAGES=["package1.a", "package2"], sections=["STDLIB", "MYPACKAGES"], separate_packages=["MYPACKAGES"], ) == """ import os from package1.a.b import foo from package1.a.c import baz from package2 import bar """ ) def test_combine_as_with_comments_and_vertical_hang_2316() -> None: """Comments should still produce correct parentheses with multi_line_output=3. See: https://github.com/PyCQA/isort/issues/2316 """ assert ( isort.code( "from pythonosc.udp_client import SimpleUDPClient # type: ignore[import-untyped]", combine_as_imports=True, multi_line_output=3, ) == """\ from pythonosc.udp_client import ( # type: ignore[import-untyped] SimpleUDPClient ) """ ) assert ( isort.code( "from pythonosc.udp_client import SimpleUDPClient # type: ignore[import-untyped]", combine_as_imports=True, multi_line_output=3, include_trailing_comma=True, ) == """\ from pythonosc.udp_client import ( # type: ignore[import-untyped] SimpleUDPClient, ) """ ) def test_isort_preserves_empty_inline_comments() -> None: """isort should not strip empty inline comments (bare `#`). See: https://github.com/PyCQA/isort/issues/1913 """ # Straight imports assert isort.code("import a #\n") == "import a #\n" # From imports assert isort.code("from foo import bar #\n") == "from foo import bar #\n" # Should be idempotent assert isort.code(isort.code("import a #\n")) == "import a #\n" assert isort.code(isort.code("from foo import bar #\n")) == "from foo import bar #\n" # Regular (non-empty) comments should still work assert isort.code("import a # comment\n") == "import a # comment\n" assert isort.code("from foo import bar # comment\n") == "from foo import bar # comment\n" def test_comment_not_duplicated_across_from_imports_2282() -> None: """isort should not copy a comment from one 'from X import' line to other lines. See: https://github.com/PyCQA/isort/issues/2282 """ # Comment should stay only on the line with a, b - not copied to e code = """\ from foo import a, b # comment from foo import c as d from foo import e """ assert isort.code(code) == code isort-9.0.1/tests/unit/test_utils.py0000644000000000000000000000304213615410400014472 0ustar00from isort.utils import Trie def test_trie(): trie_root = Trie("default", {"line_length": 70}) trie_root.insert("/temp/config1/.isort.cfg", {"line_length": 71}) trie_root.insert("/temp/config2/setup.cfg", {"line_length": 72}) trie_root.insert("/temp/config3/pyproject.toml", {"line_length": 73}) # Ensure that appropriate configs are resolved for files in different directories config1 = trie_root.search("/temp/config1/subdir/file1.py") assert config1[0] == "/temp/config1/.isort.cfg" assert config1[1] == {"line_length": 71} config1_2 = trie_root.search("/temp/config1/file1_2.py") assert config1_2[0] == "/temp/config1/.isort.cfg" assert config1_2[1] == {"line_length": 71} config2 = trie_root.search("/temp/config2/subdir/subsubdir/file2.py") assert config2[0] == "/temp/config2/setup.cfg" assert config2[1] == {"line_length": 72} config2_2 = trie_root.search("/temp/config2/subdir/file2_2.py") assert config2_2[0] == "/temp/config2/setup.cfg" assert config2_2[1] == {"line_length": 72} config3 = trie_root.search("/temp/config3/subdir/subsubdir/subsubsubdir/file3.py") assert config3[0] == "/temp/config3/pyproject.toml" assert config3[1] == {"line_length": 73} config3_2 = trie_root.search("/temp/config3/file3.py") assert config3_2[0] == "/temp/config3/pyproject.toml" assert config3_2[1] == {"line_length": 73} config_outside = trie_root.search("/temp/file.py") assert config_outside[0] == "default" assert config_outside[1] == {"line_length": 70} isort-9.0.1/tests/unit/test_wrap.py0000644000000000000000000000753413615410400014315 0ustar00import pytest from isort import code, wrap from isort.settings import Config from isort.wrap_modes import WrapModes def test_import_statement(): assert wrap.import_statement("", [], []) == "" assert ( wrap.import_statement("from x import ", ["y"], [], config=Config(balanced_wrapping=True)) == "from x import (y)" ) assert ( wrap.import_statement("from long_import ", ["verylong"] * 10, []) == """from long_import (verylong, verylong, verylong, verylong, verylong, verylong, verylong, verylong, verylong, verylong)""" ) assert wrap.import_statement("from x import ", ["y", "z"], [], explode=True) == ( "from x import (\n y,\n z,\n)" ) @pytest.mark.parametrize( ("multi_line_output", "expected"), [ ( WrapModes.VERTICAL_HANGING_INDENT, """from a import ( b as c # comment that is long enough that this import doesn't fit in one line (parens) )""", ), ( WrapModes.VERTICAL, """from a import ( b as c) # comment that is long enough that this import doesn't fit in one line (parens)""", ), ], ) def test_line__comment_with_brackets__expects_unchanged_comment(multi_line_output, expected): content = ( "from a import b as c " "# comment that is long enough that this import doesn't fit in one line (parens)" ) config = Config( multi_line_output=multi_line_output, use_parentheses=True, ) assert wrap.line(content=content, line_separator="\n", config=config) == expected def test_line_star_import_wrapped_with_backslash() -> None: """Star imports cannot use parenthesis-based wrapping, so should use backslashes. See issue #2267. """ content = "from very.very.very.very.very.very.very.very.very.long.line import *" expected = "from very.very.very.very.very.very.very.very.very.long.line import \\\n *" config = Config(line_length=20) assert wrap.line(content=content, line_separator="\n", config=config) == expected def test_line_star_cimport_wrapped_with_backslash() -> None: """Star cimports should also use backslashes.""" content = "from very.very.very.very.very.very.very.very.very.long.line cimport *" expected = "from very.very.very.very.very.very.very.very.very.long.line cimport \\\n *" config = Config(line_length=20) assert wrap.line(content=content, line_separator="\n", config=config) == expected def test_line_star_import_with_comment_wrapped_with_backslash() -> None: """When falling back to backslashes for start imports, comments should be preserved.""" content = "from very.very.very.very.very.very.very.very.very.long.line import * # noqa: F401" config = Config(line_length=20) expected = ( "from very.very.very.very.very.very.very.very.very.long.line import \\\n * # noqa: F401" ) assert wrap.line(content=content, line_separator="\n", config=config) == expected def test_line_star_import_in_noqa_mode_is_not_backslash_wrapped() -> None: """NOQA mode should prevent backslashes getting inserted for too long star imports.""" content = "from very.very.very.very.very.very.very.very.very.very.very.long.line import *" config = Config(line_length=20, multi_line_output=WrapModes.NOQA) assert wrap.line(content=content, line_separator="\n", config=config) == ( "from very.very.very.very.very.very.very.very.very.very.very.long.line import * # NOQA" ) def test_star_import_wrapped_end_to_end() -> None: """New lines should be preserved at the end of too long start imports.""" source = "from very.very.very.very.very.very.very.very.very.long.line import *\n" expected = "from very.very.very.very.very.very.very.very.very.long.line import \\\n *\n" assert code(source, line_length=20, force_single_line=True) == expected isort-9.0.1/tests/unit/test_wrap_modes.py0000644000000000000000000004067513615410400015507 0ustar00import pytest from hypothesis import given, reject from hypothesis import strategies as st import isort from isort import wrap_modes from isort.wrap_modes import WrapModes, _wrap_modes def test_wrap_mode_interface(): assert ( wrap_modes._wrap_mode_interface("statement", [], "", "", 80, [], "", "", True, True) == "" ) def test_auto_saved(): """hypothesis_auto tests cases that have been saved to ensure they run each test cycle""" assert ( wrap_modes.noqa( comment_prefix="-\U000bf82c\x0c\U0004608f\x10%", comments=[], imports=[], include_trailing_comma=False, indent="0\x19", line_length=-19659, line_separator="\x15\x0b\U00086494\x1d\U000e00a2\U000ee216\U0006708a\x03\x1f", remove_comments=False, statement="\U00092452", white_space="\U000a7322\U000c20e3-\U0010eae4\x07\x14\U0007d486", ) == "\U00092452-\U000bf82c\x0c\U0004608f\x10% NOQA" ) assert ( wrap_modes.noqa( comment_prefix='\x12\x07\U0009e994🁣"\U000ae787\x0e', comments=["\x00\U0001ae99\U0005c3e7\U0004d08e", "\x1e", "", ""], imports=["*"], include_trailing_comma=True, indent="", line_length=31492, line_separator="\U00071610\U0005bfbc", remove_comments=False, statement="", white_space="\x08\x01ⷓ\x16%\U0006cd8c", ) == '*\x12\x07\U0009e994🁣"\U000ae787\x0e \x00\U0001ae99\U0005c3e7\U0004d08e \x1e ' ) assert ( wrap_modes.noqa( comment_prefix=" #", comments=["NOQA", "THERE"], imports=[], include_trailing_comma=False, indent="0\x19", line_length=-19659, line_separator="\n", remove_comments=False, statement="hi", white_space=" ", ) == "hi # NOQA THERE" ) def test_backslash_grid(): """Tests the backslash_grid grid wrap mode, ensuring it matches formatting expectations. See: https://github.com/PyCQA/isort/issues/1434 """ assert ( isort.code( """ from kopf.engines import loggers, posting from kopf.reactor import causation, daemons, effects, handling, lifecycles, registries from kopf.storage import finalizers, states from kopf.structs import (bodies, configuration, containers, diffs, handlers as handlers_, patches, resources) """, multi_line_output=11, line_length=88, combine_as_imports=True, ) == """ from kopf.engines import loggers, posting from kopf.reactor import causation, daemons, effects, handling, lifecycles, registries from kopf.storage import finalizers, states from kopf.structs import bodies, configuration, containers, diffs, \\ handlers as handlers_, patches, resources """ ) @pytest.mark.parametrize("include_trailing_comma", [False, True]) @pytest.mark.parametrize("line_length", [18, 19]) @pytest.mark.parametrize("multi_line_output", [4, 5]) def test_vertical_grid_size_near_line_length( multi_line_output: int, line_length: int, include_trailing_comma: bool, ): separator = " " # Cases where the input should be wrapped: if ( # Mode 4 always adds a closing ")", making the imports line 19 chars, # if include_trailing_comma is True that becomes 20 chars. (multi_line_output == 4 and line_length < 19 + int(include_trailing_comma)) # Modes 5 and 6 only add a comma, if include_trailing_comma is True, # so their lines are 18 or 19 chars long. or (multi_line_output != 4 and line_length < 18 + int(include_trailing_comma)) ): separator = "\n " test_input = f"from foo import (\n aaaa, bbb,{separator}ccc" if include_trailing_comma: test_input += "," if multi_line_output != 4: test_input += "\n" test_input += ")\n" assert ( isort.code( test_input, multi_line_output=multi_line_output, line_length=line_length, include_trailing_comma=include_trailing_comma, ) == test_input ) # This test code was written by the `hypothesis.extra.ghostwriter` module # and is provided under the Creative Commons Zero public domain dedication. @given( statement=st.text(), imports=st.lists(st.text()), white_space=st.text(), indent=st.text(), line_length=st.integers(), comments=st.lists(st.text()), line_separator=st.text(), comment_prefix=st.text(), include_trailing_comma=st.booleans(), remove_comments=st.booleans(), ) def test_fuzz_backslash_grid( statement, imports, white_space, indent, line_length, comments, line_separator, comment_prefix, include_trailing_comma, remove_comments, ): try: isort.wrap_modes.backslash_grid( statement=statement, imports=imports, white_space=white_space, indent=indent, line_length=line_length, comments=comments, line_separator=line_separator, comment_prefix=comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=remove_comments, ) except ValueError: reject() @given( statement=st.text(), imports=st.lists(st.text()), white_space=st.text(), indent=st.text(), line_length=st.integers(), comments=st.lists(st.text()), line_separator=st.text(), comment_prefix=st.text(), include_trailing_comma=st.booleans(), remove_comments=st.booleans(), ) def test_fuzz_grid( statement, imports, white_space, indent, line_length, comments, line_separator, comment_prefix, include_trailing_comma, remove_comments, ): try: isort.wrap_modes.grid( statement=statement, imports=imports, white_space=white_space, indent=indent, line_length=line_length, comments=comments, line_separator=line_separator, comment_prefix=comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=remove_comments, ) except ValueError: reject() @given( statement=st.text(), imports=st.lists(st.text()), white_space=st.text(), indent=st.text(), line_length=st.integers(), comments=st.lists(st.text()), line_separator=st.text(), comment_prefix=st.text(), include_trailing_comma=st.booleans(), remove_comments=st.booleans(), ) def test_fuzz_hanging_indent( statement, imports, white_space, indent, line_length, comments, line_separator, comment_prefix, include_trailing_comma, remove_comments, ): try: isort.wrap_modes.hanging_indent( statement=statement, imports=imports, white_space=white_space, indent=indent, line_length=line_length, comments=comments, line_separator=line_separator, comment_prefix=comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=remove_comments, ) except ValueError: reject() @pytest.mark.parametrize("include_trailing_comma", [True, False]) def test_hanging_indent__with_include_trailing_comma__expect_same_result(include_trailing_comma): result = isort.wrap_modes.hanging_indent( statement="from datetime import ", imports=["datetime", "time", "timedelta", "timezone", "tzinfo"], white_space=" ", indent=" ", line_length=50, comments=[], line_separator="\n", comment_prefix=" #", include_trailing_comma=include_trailing_comma, remove_comments=False, ) assert result == "from datetime import datetime, time, timedelta, \\\n timezone, tzinfo" @given( statement=st.text(), imports=st.lists(st.text()), white_space=st.text(), indent=st.text(), line_length=st.integers(), comments=st.lists(st.text()), line_separator=st.text(), comment_prefix=st.text(), include_trailing_comma=st.booleans(), remove_comments=st.booleans(), ) def test_fuzz_hanging_indent_with_parentheses( statement, imports, white_space, indent, line_length, comments, line_separator, comment_prefix, include_trailing_comma, remove_comments, ): try: isort.wrap_modes.hanging_indent_with_parentheses( statement=statement, imports=imports, white_space=white_space, indent=indent, line_length=line_length, comments=comments, line_separator=line_separator, comment_prefix=comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=remove_comments, ) except ValueError: reject() @given( statement=st.text(), imports=st.lists(st.text()), white_space=st.text(), indent=st.text(), line_length=st.integers(), comments=st.lists(st.text()), line_separator=st.text(), comment_prefix=st.text(), include_trailing_comma=st.booleans(), remove_comments=st.booleans(), ) def test_fuzz_noqa( statement, imports, white_space, indent, line_length, comments, line_separator, comment_prefix, include_trailing_comma, remove_comments, ): try: isort.wrap_modes.noqa( statement=statement, imports=imports, white_space=white_space, indent=indent, line_length=line_length, comments=comments, line_separator=line_separator, comment_prefix=comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=remove_comments, ) except ValueError: reject() @given( statement=st.text(), imports=st.lists(st.text()), white_space=st.text(), indent=st.text(), line_length=st.integers(), comments=st.lists(st.text()), line_separator=st.text(), comment_prefix=st.text(), include_trailing_comma=st.booleans(), remove_comments=st.booleans(), ) def test_fuzz_vertical( statement, imports, white_space, indent, line_length, comments, line_separator, comment_prefix, include_trailing_comma, remove_comments, ): try: isort.wrap_modes.vertical( statement=statement, imports=imports, white_space=white_space, indent=indent, line_length=line_length, comments=comments, line_separator=line_separator, comment_prefix=comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=remove_comments, ) except ValueError: reject() @given( statement=st.text(), imports=st.lists(st.text()), white_space=st.text(), indent=st.text(), line_length=st.integers(), comments=st.lists(st.text()), line_separator=st.text(), comment_prefix=st.text(), include_trailing_comma=st.booleans(), remove_comments=st.booleans(), ) def test_fuzz_vertical_grid( statement, imports, white_space, indent, line_length, comments, line_separator, comment_prefix, include_trailing_comma, remove_comments, ): try: isort.wrap_modes.vertical_grid( statement=statement, imports=imports, white_space=white_space, indent=indent, line_length=line_length, comments=comments, line_separator=line_separator, comment_prefix=comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=remove_comments, ) except ValueError: reject() @given( statement=st.text(), imports=st.lists(st.text()), white_space=st.text(), indent=st.text(), line_length=st.integers(), comments=st.lists(st.text()), line_separator=st.text(), comment_prefix=st.text(), include_trailing_comma=st.booleans(), remove_comments=st.booleans(), ) def test_fuzz_vertical_grid_grouped( statement, imports, white_space, indent, line_length, comments, line_separator, comment_prefix, include_trailing_comma, remove_comments, ): try: isort.wrap_modes.vertical_grid_grouped( statement=statement, imports=imports, white_space=white_space, indent=indent, line_length=line_length, comments=comments, line_separator=line_separator, comment_prefix=comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=remove_comments, ) except ValueError: reject() @given( statement=st.text(), imports=st.lists(st.text()), white_space=st.text(), indent=st.text(), line_length=st.integers(), comments=st.lists(st.text()), line_separator=st.text(), comment_prefix=st.text(), include_trailing_comma=st.booleans(), remove_comments=st.booleans(), ) def test_fuzz_vertical_hanging_indent( statement, imports, white_space, indent, line_length, comments, line_separator, comment_prefix, include_trailing_comma, remove_comments, ): try: isort.wrap_modes.vertical_hanging_indent( statement=statement, imports=imports, white_space=white_space, indent=indent, line_length=line_length, comments=comments, line_separator=line_separator, comment_prefix=comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=remove_comments, ) except ValueError: reject() @given( statement=st.text(), imports=st.lists(st.text()), white_space=st.text(), indent=st.text(), line_length=st.integers(), comments=st.lists(st.text()), line_separator=st.text(), comment_prefix=st.text(), include_trailing_comma=st.booleans(), remove_comments=st.booleans(), ) def test_fuzz_vertical_hanging_indent_bracket( statement, imports, white_space, indent, line_length, comments, line_separator, comment_prefix, include_trailing_comma, remove_comments, ): try: isort.wrap_modes.vertical_hanging_indent_bracket( statement=statement, imports=imports, white_space=white_space, indent=indent, line_length=line_length, comments=comments, line_separator=line_separator, comment_prefix=comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=remove_comments, ) except ValueError: reject() @given( statement=st.text(), imports=st.lists(st.text()), white_space=st.text(), indent=st.text(), line_length=st.integers(), comments=st.lists(st.text()), line_separator=st.text(), comment_prefix=st.text(), include_trailing_comma=st.booleans(), remove_comments=st.booleans(), ) def test_fuzz_vertical_prefix_from_module_import( statement, imports, white_space, indent, line_length, comments, line_separator, comment_prefix, include_trailing_comma, remove_comments, ): try: isort.wrap_modes.vertical_prefix_from_module_import( statement=statement, imports=imports, white_space=white_space, indent=indent, line_length=line_length, comments=comments, line_separator=line_separator, comment_prefix=comment_prefix, include_trailing_comma=include_trailing_comma, remove_comments=remove_comments, ) except ValueError: reject() def test_enum_matches_order_of_definitions(): """Before isort 9.0.0 WrapModes was dynamically generated, this tests for breaking changes.""" old_enum_logic = {wrap_mode: index for index, wrap_mode in enumerate(_wrap_modes.keys())} new_enum_logic = {wrap_mode.name: wrap_mode.value for wrap_mode in WrapModes} assert old_enum_logic == new_enum_logic, ( "WrapModes enum order has changed, this is a breaking change." ) isort-9.0.1/tests/unit/utils.py0000644000000000000000000000154213615410400013436 0ustar00from io import BytesIO, StringIO, TextIOWrapper import isort class UnseekableTextIOWrapper(TextIOWrapper): def seek(self, *args, **kwargs): raise ValueError("underlying stream is not seekable") class UnreadableStream(StringIO): def readable(self, *args, **kwargs) -> bool: return False def as_stream(text: str) -> UnseekableTextIOWrapper: return UnseekableTextIOWrapper(BytesIO(text.encode("utf8"))) def isort_test(code: str, expected_output: str = "", **config): """Runs isort against the given code snippet and ensures that it gives consistent output across multiple runs, and if an expected_output is given - that it matches that. """ expected_output = expected_output or code output = isort.code(code, **config) assert output == expected_output assert output == isort.code(output, **config) isort-9.0.1/tests/unit/example_projects/namespaces/almost-implicit/.isort.cfg0000644000000000000000000000003213615410400024406 0ustar00[settings] src_paths=root isort-9.0.1/tests/unit/example_projects/namespaces/almost-implicit/root/y.py0000644000000000000000000000000013615410400024307 0ustar00isort-9.0.1/tests/unit/example_projects/namespaces/almost-implicit/root/nested/__init__.py0000644000000000000000000000000013615410400027060 0ustar00isort-9.0.1/tests/unit/example_projects/namespaces/almost-implicit/root/nested/x.py0000644000000000000000000000000013615410400025570 0ustar00isort-9.0.1/tests/unit/example_projects/namespaces/implicit/.isort.cfg0000644000000000000000000000003213615410400023111 0ustar00[settings] src_paths=root isort-9.0.1/tests/unit/example_projects/namespaces/implicit/root/nested/__init__.py0000644000000000000000000000000013615410400025563 0ustar00isort-9.0.1/tests/unit/example_projects/namespaces/implicit/root/nested/x.py0000644000000000000000000000000013615410400024273 0ustar00isort-9.0.1/tests/unit/example_projects/namespaces/none/.isort.cfg0000644000000000000000000000003213615410400022236 0ustar00[settings] src_paths=root isort-9.0.1/tests/unit/example_projects/namespaces/none/root/__init__.py0000644000000000000000000000000013615410400023426 0ustar00isort-9.0.1/tests/unit/example_projects/namespaces/none/root/nested/__init__.py0000644000000000000000000000000013615410400024710 0ustar00isort-9.0.1/tests/unit/example_projects/namespaces/pkg_resource/.isort.cfg0000644000000000000000000000003213615410400023767 0ustar00[settings] src_paths=root isort-9.0.1/tests/unit/example_projects/namespaces/pkg_resource/root/__init__.py0000644000000000000000000000007013615410400025166 0ustar00__import__("pkg_resources").declare_namespace(__name__) isort-9.0.1/tests/unit/example_projects/namespaces/pkg_resource/root/nested/__init__.py0000644000000000000000000000000013615410400026441 0ustar00isort-9.0.1/tests/unit/example_projects/namespaces/pkg_resource/root/nested/x.py0000644000000000000000000000000013615410400025151 0ustar00isort-9.0.1/tests/unit/example_projects/namespaces/pkgutil/.isort.cfg0000644000000000000000000000003213615410400022756 0ustar00[settings] src_paths=root isort-9.0.1/tests/unit/example_projects/namespaces/pkgutil/root/__init__.py0000644000000000000000000000010113615410400024150 0ustar00__path__ = __import__("pkgutil").extend_path(__path__, __name__) isort-9.0.1/tests/unit/example_projects/namespaces/pkgutil/root/nested/__init__.py0000644000000000000000000000000013615410400025430 0ustar00isort-9.0.1/tests/unit/example_projects/namespaces/pkgutil/root/nested/x.py0000644000000000000000000000000013615410400024140 0ustar00isort-9.0.1/tests/unit/example_projects/namespaces/weird_encoding/.isort.cfg0000644000000000000000000000003213615410400024257 0ustar00[settings] src_paths=root isort-9.0.1/tests/unit/example_projects/namespaces/weird_encoding/root/__init__.py0000644000000000000000000000012313615410400025455 0ustar00description = "基于FastAPI + Mysql的 TodoList" # Exception: UnicodeDecodeError isort-9.0.1/tests/unit/example_projects/namespaces/weird_encoding/root/nested/__init__.py0000644000000000000000000000000013615410400026731 0ustar00isort-9.0.1/tests/unit/profiles/__init__.py0000644000000000000000000000000013615410400015644 0ustar00isort-9.0.1/tests/unit/profiles/test_attrs.py0000644000000000000000000000346513615410400016323 0ustar00from functools import partial from ..utils import isort_test attrs_isort_test = partial(isort_test, profile="attrs") def test_attrs_code_snippet_one(): attrs_isort_test( """from __future__ import absolute_import, division, print_function import sys from functools import partial from . import converters, exceptions, filters, setters, validators from ._config import get_run_validators, set_run_validators from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types from ._make import ( NOTHING, Attribute, Factory, attrib, attrs, fields, fields_dict, make_class, validate, ) from ._version_info import VersionInfo __version__ = "20.2.0.dev0" """ ) def test_attrs_code_snippet_two(): attrs_isort_test( """from __future__ import absolute_import, division, print_function import copy import linecache import sys import threading import uuid import warnings from operator import itemgetter from . import _config, setters from ._compat import ( PY2, isclass, iteritems, metadata_proxy, ordered_dict, set_closure_cell, ) from .exceptions import ( DefaultAlreadySetError, FrozenInstanceError, NotAnAttrsClassError, PythonTooOldError, UnannotatedAttributeError, ) # This is used at least twice, so cache it here. _obj_setattr = object.__setattr__ """ ) def test_attrs_code_snippet_three(): attrs_isort_test( ''' """ Commonly useful validators. """ from __future__ import absolute_import, division, print_function import re from ._make import _AndValidator, and_, attrib, attrs from .exceptions import NotCallableError __all__ = [ "and_", "deep_iterable", "deep_mapping", "in_", "instance_of", "is_callable", "matches_re", "optional", "provides", ] ''' ) isort-9.0.1/tests/unit/profiles/test_black.py0000644000000000000000000002377113615410400016244 0ustar00import black from black.report import NothingChanged import isort def black_format(code: str, is_pyi: bool = False, line_length: int = 88) -> str: """Formats the provided code snippet using black""" try: return black.format_file_contents( code, fast=True, mode=black.FileMode( is_pyi=is_pyi, line_length=line_length, ), ) except NothingChanged: return code def black_test(code: str, expected_output: str = "", *, is_pyi: bool = False, **config_kwargs): """Tests that the given code: - Behaves the same when formatted multiple times with isort. - Agrees with black formatting. - Matches the desired output or itself if none is provided. """ expected_output = expected_output or code config_kwargs = { "extension": "pyi" if is_pyi else None, "profile": "black", **config_kwargs, } # output should stay consistent over multiple runs output = isort.code(code, **config_kwargs) assert output == isort.code(code, **config_kwargs) # output should agree with black black_output = black_format(output, is_pyi=is_pyi) assert output == black_output # output should match expected output assert output == expected_output def test_black_snippet_one(): """Test consistent code formatting between isort and black for code snippet from black repository. See: https://github.com/psf/black/blob/master/tests/test_black.py """ black_test( """#!/usr/bin/env python3 import asyncio import logging from concurrent.futures import ThreadPoolExecutor from contextlib import contextmanager from dataclasses import replace from functools import partial import inspect from io import BytesIO, TextIOWrapper import os from pathlib import Path from platform import system import regex as re import sys from tempfile import TemporaryDirectory import types from typing import ( Any, BinaryIO, Callable, Dict, Generator, List, Tuple, Iterator, TypeVar, ) import unittest from unittest.mock import patch, MagicMock import click from click import unstyle from click.testing import CliRunner import black from black import Feature, TargetVersion try: import blackd from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop from aiohttp import web except ImportError: has_blackd_deps = False else: has_blackd_deps = True from pathspec import PathSpec # Import other test classes from .test_primer import PrimerCLITests # noqa: F401 DEFAULT_MODE = black.FileMode(experimental_string_processing=True) """, """#!/usr/bin/env python3 import asyncio import inspect import logging import os import sys import types import unittest from concurrent.futures import ThreadPoolExecutor from contextlib import contextmanager from dataclasses import replace from functools import partial from io import BytesIO, TextIOWrapper from pathlib import Path from platform import system from tempfile import TemporaryDirectory from typing import ( Any, BinaryIO, Callable, Dict, Generator, Iterator, List, Tuple, TypeVar, ) from unittest.mock import MagicMock, patch import black import click import regex as re from black import Feature, TargetVersion from click import unstyle from click.testing import CliRunner try: import blackd from aiohttp import web from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop except ImportError: has_blackd_deps = False else: has_blackd_deps = True from pathspec import PathSpec # Import other test classes from .test_primer import PrimerCLITests # noqa: F401 DEFAULT_MODE = black.FileMode(experimental_string_processing=True) """, ) def test_black_snippet_two(): """Test consistent code formatting between isort and black for code snippet from black repository. See: https://github.com/psf/black/blob/master/tests/test_primer.py """ black_test( '''#!/usr/bin/env python3 import asyncio import sys import unittest from contextlib import contextmanager from copy import deepcopy from io import StringIO from os import getpid from pathlib import Path from platform import system from subprocess import CalledProcessError from tempfile import TemporaryDirectory, gettempdir from typing import Any, Callable, Generator, Iterator, Tuple from unittest.mock import Mock, patch from click.testing import CliRunner from black_primer import cli, lib EXPECTED_ANALYSIS_OUTPUT = """\ -- primer results 📊 -- 68 / 69 succeeded (98.55%) ✅ 1 / 69 FAILED (1.45%) 💩 - 0 projects disabled by config - 0 projects skipped due to Python version - 0 skipped due to long checkout Failed projects: ## black: - Returned 69 - stdout: Black didn't work """ ''', '''#!/usr/bin/env python3 import asyncio import sys import unittest from contextlib import contextmanager from copy import deepcopy from io import StringIO from os import getpid from pathlib import Path from platform import system from subprocess import CalledProcessError from tempfile import TemporaryDirectory, gettempdir from typing import Any, Callable, Generator, Iterator, Tuple from unittest.mock import Mock, patch from black_primer import cli, lib from click.testing import CliRunner EXPECTED_ANALYSIS_OUTPUT = """-- primer results 📊 -- 68 / 69 succeeded (98.55%) ✅ 1 / 69 FAILED (1.45%) 💩 - 0 projects disabled by config - 0 projects skipped due to Python version - 0 skipped due to long checkout Failed projects: ## black: - Returned 69 - stdout: Black didn't work """ ''', ) def test_black_snippet_three(): """Test consistent code formatting between isort and black for code snippet from black repository. See: https://github.com/psf/black/blob/master/src/black/__init__.py """ black_test( """import ast import asyncio from abc import ABC, abstractmethod from collections import defaultdict from concurrent.futures import Executor, ThreadPoolExecutor, ProcessPoolExecutor from contextlib import contextmanager from datetime import datetime from enum import Enum from functools import lru_cache, partial, wraps import io import itertools import logging from multiprocessing import Manager, freeze_support import os from pathlib import Path import pickle import regex as re import signal import sys import tempfile import tokenize import traceback from typing import ( Any, Callable, Collection, Dict, Generator, Generic, Iterable, Iterator, List, Optional, Pattern, Sequence, Set, Sized, Tuple, Type, TypeVar, Union, cast, TYPE_CHECKING, ) from typing_extensions import Final from mypy_extensions import mypyc_attr from appdirs import user_cache_dir from dataclasses import dataclass, field, replace import click import toml from typed_ast import ast3, ast27 from pathspec import PathSpec # lib2to3 fork from blib2to3.pytree import Node, Leaf, type_repr from blib2to3 import pygram, pytree from blib2to3.pgen2 import driver, token from blib2to3.pgen2.grammar import Grammar from blib2to3.pgen2.parse import ParseError from _black_version import version as __version__ if TYPE_CHECKING: import colorama # noqa: F401 DEFAULT_LINE_LENGTH = 88 """, """import ast import asyncio import io import itertools import logging import os import pickle import signal import sys import tempfile import tokenize import traceback from abc import ABC, abstractmethod from collections import defaultdict from concurrent.futures import Executor, ProcessPoolExecutor, ThreadPoolExecutor from contextlib import contextmanager from dataclasses import dataclass, field, replace from datetime import datetime from enum import Enum from functools import lru_cache, partial, wraps from multiprocessing import Manager, freeze_support from pathlib import Path from typing import ( TYPE_CHECKING, Any, Callable, Collection, Dict, Generator, Generic, Iterable, Iterator, List, Optional, Pattern, Sequence, Set, Sized, Tuple, Type, TypeVar, Union, cast, ) import click import regex as re import toml from _black_version import version as __version__ from appdirs import user_cache_dir from blib2to3 import pygram, pytree from blib2to3.pgen2 import driver, token from blib2to3.pgen2.grammar import Grammar from blib2to3.pgen2.parse import ParseError # lib2to3 fork from blib2to3.pytree import Leaf, Node, type_repr from mypy_extensions import mypyc_attr from pathspec import PathSpec from typed_ast import ast3, ast27 from typing_extensions import Final if TYPE_CHECKING: import colorama # noqa: F401 DEFAULT_LINE_LENGTH = 88 """, ) def test_black_pyi_file(): """Test consistent code formatting between isort and black for `.pyi` files. black only allows no more than two consecutive blank lines in a `.pyi` file. """ black_test( """# comment import math from typing import Sequence import numpy as np def add(a: np.ndarray, b: np.ndarray) -> np.ndarray: ... def sub(a: np.ndarray, b: np.ndarray) -> np.ndarray: ... """, """# comment import math from typing import Sequence import numpy as np def add(a: np.ndarray, b: np.ndarray) -> np.ndarray: ... def sub(a: np.ndarray, b: np.ndarray) -> np.ndarray: ... """, is_pyi=False, lines_before_imports=2, lines_after_imports=2, ) black_test( """# comment import math from typing import Sequence import numpy as np def add(a: np.ndarray, b: np.ndarray) -> np.ndarray: ... def sub(a: np.ndarray, b: np.ndarray) -> np.ndarray: ... """, """# comment import math from typing import Sequence import numpy as np def add(a: np.ndarray, b: np.ndarray) -> np.ndarray: ... def sub(a: np.ndarray, b: np.ndarray) -> np.ndarray: ... """, is_pyi=True, lines_before_imports=2, # will be ignored lines_after_imports=2, # will be ignored ) def test_black_trailing_comma(): black_test( "from x import (a, b, c,)\n", """\ from x import ( a, b, c, ) """, ) isort-9.0.1/tests/unit/profiles/test_django.py0000644000000000000000000000723513615410400016427 0ustar00from functools import partial from ..utils import isort_test django_isort_test = partial(isort_test, profile="django", known_first_party=["django"]) def test_django_snippet_one(): django_isort_test( """import copy import inspect import warnings from functools import partialmethod from itertools import chain from django.apps import apps from django.conf import settings from django.core import checks from django.core.exceptions import ( NON_FIELD_ERRORS, FieldDoesNotExist, FieldError, MultipleObjectsReturned, ObjectDoesNotExist, ValidationError, ) from django.db import ( DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY, DatabaseError, connection, connections, router, transaction, ) from django.db.models import ( NOT_PROVIDED, ExpressionWrapper, IntegerField, Max, Value, ) from django.db.models.constants import LOOKUP_SEP from django.db.models.constraints import CheckConstraint from django.db.models.deletion import CASCADE, Collector from django.db.models.fields.related import ( ForeignObjectRel, OneToOneField, lazy_related_operation, resolve_relation, ) from django.db.models.functions import Coalesce from django.db.models.manager import Manager from django.db.models.options import Options from django.db.models.query import Q from django.db.models.signals import ( class_prepared, post_init, post_save, pre_init, pre_save, ) from django.db.models.utils import make_model_tuple from django.utils.encoding import force_str from django.utils.hashable import make_hashable from django.utils.text import capfirst, get_text_list from django.utils.translation import gettext_lazy as _ from django.utils.version import get_version class Deferred: def __repr__(self): return '' def __str__(self): return ''""" ) def test_django_snippet_two(): django_isort_test( '''from django.utils.version import get_version VERSION = (3, 2, 0, 'alpha', 0) __version__ = get_version(VERSION) def setup(set_prefix=True): """ Configure the settings (this happens as a side effect of accessing the first setting), configure logging and populate the app registry. Set the thread-local urlresolvers script prefix if `set_prefix` is True. """ from django.apps import apps from django.conf import settings from django.urls import set_script_prefix from django.utils.log import configure_logging configure_logging(settings.LOGGING_CONFIG, settings.LOGGING) if set_prefix: set_script_prefix( '/' if settings.FORCE_SCRIPT_NAME is None else settings.FORCE_SCRIPT_NAME ) apps.populate(settings.INSTALLED_APPS)''' ) def test_django_snippet_three(): django_isort_test( """import cgi import codecs import copy import warnings from io import BytesIO from itertools import chain from urllib.parse import quote, urlencode, urljoin, urlsplit from django.conf import settings from django.core import signing from django.core.exceptions import ( DisallowedHost, ImproperlyConfigured, RequestDataTooBig, ) from django.core.files import uploadhandler from django.http.multipartparser import MultiPartParser, MultiPartParserError from django.utils.datastructures import ( CaseInsensitiveMapping, ImmutableList, MultiValueDict, ) from django.utils.deprecation import RemovedInDjango40Warning from django.utils.encoding import escape_uri_path, iri_to_uri from django.utils.functional import cached_property from django.utils.http import is_same_domain, limited_parse_qsl from django.utils.regex_helper import _lazy_re_compile from .multipartparser import parse_header RAISE_ERROR = object() class UnreadablePostError(OSError): pass""" ) isort-9.0.1/tests/unit/profiles/test_google.py0000644000000000000000000003211113615410400016430 0ustar00from functools import partial from ..utils import isort_test google_isort_test = partial(isort_test, profile="google") def test_google_code_snippet_shared_example(): """Tests snippet examples directly shared with the isort project. See: https://github.com/PyCQA/isort/issues/1486. """ google_isort_test( """import collections import cProfile """ ) google_isort_test( """from a import z from a.b import c from a.b import import_me """ ) def test_google_code_snippet_one(): google_isort_test( '''# coding=utf-8 # Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """JAX user-facing transformations and utilities. The transformations here mostly wrap internal transformations, providing convenience flags to control behavior and handling Python containers of arguments and outputs. The Python containers handled are pytrees (see tree_util.py), which include nested tuples/lists/dicts, where the leaves are arrays. """ # flake8: noqa: F401 import collections import functools import inspect import itertools as it import threading import weakref from typing import Any, Callable, Iterable, List, NamedTuple, Optional, Sequence, Tuple, TypeVar, Union from warnings import warn import numpy as np from contextlib import contextmanager, ExitStack from . import core from . import linear_util as lu from . import ad_util from . import dtypes from .core import eval_jaxpr from .api_util import (wraps, flatten_fun, apply_flat_fun, flatten_fun_nokwargs, flatten_fun_nokwargs2, argnums_partial, flatten_axes, donation_vector, rebase_donate_argnums) from .traceback_util import api_boundary from .tree_util import (tree_map, tree_flatten, tree_unflatten, tree_structure, tree_transpose, tree_leaves, tree_multimap, treedef_is_leaf, Partial) from .util import (unzip2, curry, partial, safe_map, safe_zip, prod, split_list, extend_name_stack, wrap_name, cache) from .lib import xla_bridge as xb from .lib import xla_client as xc # Unused imports to be exported from .lib.xla_bridge import (device_count, local_device_count, devices, local_devices, host_id, host_ids, host_count) from .abstract_arrays import ConcreteArray, ShapedArray, raise_to_shaped from .interpreters import partial_eval as pe from .interpreters import xla from .interpreters import pxla from .interpreters import ad from .interpreters import batching from .interpreters import masking from .interpreters import invertible_ad as iad from .interpreters.invertible_ad import custom_ivjp from .custom_derivatives import custom_jvp, custom_vjp from .config import flags, config, bool_env AxisName = Any # This TypeVar is used below to express the fact that function call signatures # are invariant under the jit, vmap, and pmap transformations. # Specifically, we statically assert that the return type is invariant. # Until PEP-612 is implemented, we cannot express the same invariance for # function arguments. # Note that the return type annotations will generally not strictly hold # in JIT internals, as Tracer values are passed through the function. # Should this raise any type errors for the tracing code in future, we can disable # type checking in parts of the tracing code, or remove these annotations. T = TypeVar("T") map = safe_map zip = safe_zip FLAGS = flags.FLAGS flags.DEFINE_bool("jax_disable_jit", bool_env("JAX_DISABLE_JIT", False), "Disable JIT compilation and just call original Python.") ''', '''# coding=utf-8 # Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """JAX user-facing transformations and utilities. The transformations here mostly wrap internal transformations, providing convenience flags to control behavior and handling Python containers of arguments and outputs. The Python containers handled are pytrees (see tree_util.py), which include nested tuples/lists/dicts, where the leaves are arrays. """ # flake8: noqa: F401 import collections from contextlib import contextmanager from contextlib import ExitStack import functools import inspect import itertools as it import threading from typing import Any, Callable, Iterable, List, NamedTuple, Optional, Sequence, Tuple, TypeVar, Union from warnings import warn import weakref import numpy as np from . import ad_util from . import core from . import dtypes from . import linear_util as lu from .abstract_arrays import ConcreteArray from .abstract_arrays import raise_to_shaped from .abstract_arrays import ShapedArray from .api_util import apply_flat_fun from .api_util import argnums_partial from .api_util import donation_vector from .api_util import flatten_axes from .api_util import flatten_fun from .api_util import flatten_fun_nokwargs from .api_util import flatten_fun_nokwargs2 from .api_util import rebase_donate_argnums from .api_util import wraps from .config import bool_env from .config import config from .config import flags from .core import eval_jaxpr from .custom_derivatives import custom_jvp from .custom_derivatives import custom_vjp from .interpreters import ad from .interpreters import batching from .interpreters import invertible_ad as iad from .interpreters import masking from .interpreters import partial_eval as pe from .interpreters import pxla from .interpreters import xla from .interpreters.invertible_ad import custom_ivjp from .lib import xla_bridge as xb from .lib import xla_client as xc # Unused imports to be exported from .lib.xla_bridge import device_count from .lib.xla_bridge import devices from .lib.xla_bridge import host_count from .lib.xla_bridge import host_id from .lib.xla_bridge import host_ids from .lib.xla_bridge import local_device_count from .lib.xla_bridge import local_devices from .traceback_util import api_boundary from .tree_util import Partial from .tree_util import tree_flatten from .tree_util import tree_leaves from .tree_util import tree_map from .tree_util import tree_multimap from .tree_util import tree_structure from .tree_util import tree_transpose from .tree_util import tree_unflatten from .tree_util import treedef_is_leaf from .util import cache from .util import curry from .util import extend_name_stack from .util import partial from .util import prod from .util import safe_map from .util import safe_zip from .util import split_list from .util import unzip2 from .util import wrap_name AxisName = Any # This TypeVar is used below to express the fact that function call signatures # are invariant under the jit, vmap, and pmap transformations. # Specifically, we statically assert that the return type is invariant. # Until PEP-612 is implemented, we cannot express the same invariance for # function arguments. # Note that the return type annotations will generally not strictly hold # in JIT internals, as Tracer values are passed through the function. # Should this raise any type errors for the tracing code in future, we can disable # type checking in parts of the tracing code, or remove these annotations. T = TypeVar("T") map = safe_map zip = safe_zip FLAGS = flags.FLAGS flags.DEFINE_bool("jax_disable_jit", bool_env("JAX_DISABLE_JIT", False), "Disable JIT compilation and just call original Python.") ''', ) def test_google_code_snippet_two(): google_isort_test( """#!/usr/bin/env python # In[ ]: # coding: utf-8 ###### Searching and Downloading Google Images to the local disk ###### # Import Libraries import sys version = (3, 0) cur_version = sys.version_info if cur_version >= version: # If the Current Version of Python is 3.0 or above import urllib.request from urllib.request import Request, urlopen from urllib.request import URLError, HTTPError from urllib.parse import quote import http.client from http.client import IncompleteRead, BadStatusLine http.client._MAXHEADERS = 1000 else: # If the Current Version of Python is 2.x import urllib2 from urllib2 import Request, urlopen from urllib2 import URLError, HTTPError from urllib import quote import httplib from httplib import IncompleteRead, BadStatusLine httplib._MAXHEADERS = 1000 import time # Importing the time library to check the time of code execution import os import argparse import ssl import datetime import json import re import codecs import socket""", """#!/usr/bin/env python # In[ ]: # coding: utf-8 ###### Searching and Downloading Google Images to the local disk ###### # Import Libraries import sys version = (3, 0) cur_version = sys.version_info if cur_version >= version: # If the Current Version of Python is 3.0 or above import http.client from http.client import BadStatusLine from http.client import IncompleteRead from urllib.parse import quote import urllib.request from urllib.request import HTTPError from urllib.request import Request from urllib.request import URLError from urllib.request import urlopen http.client._MAXHEADERS = 1000 else: # If the Current Version of Python is 2.x from urllib import quote import httplib from httplib import BadStatusLine from httplib import IncompleteRead import urllib2 from urllib2 import HTTPError from urllib2 import Request from urllib2 import URLError from urllib2 import urlopen httplib._MAXHEADERS = 1000 import argparse import codecs import datetime import json import os import re import socket import ssl import time # Importing the time library to check the time of code execution """, ) def test_code_snippet_three(): google_isort_test( '''# Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Monitoring.""" # pylint: disable=invalid-name # TODO(ochang): Remove V3 from names once all metrics are migrated to # stackdriver. from builtins import object from builtins import range from builtins import str import bisect import collections import functools import itertools import re import six import threading import time try: from google.cloud import monitoring_v3 except (ImportError, RuntimeError): monitoring_v3 = None from google.api_core import exceptions from google.api_core import retry from base import errors from base import utils from config import local_config from google_cloud_utils import compute_metadata from google_cloud_utils import credentials from metrics import logs from system import environment''', '''# Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Monitoring.""" # pylint: disable=invalid-name # TODO(ochang): Remove V3 from names once all metrics are migrated to # stackdriver. import bisect from builtins import object from builtins import range from builtins import str import collections import functools import itertools import re import threading import time import six try: from google.cloud import monitoring_v3 except (ImportError, RuntimeError): monitoring_v3 = None from base import errors from base import utils from config import local_config from google.api_core import exceptions from google.api_core import retry from google_cloud_utils import compute_metadata from google_cloud_utils import credentials from metrics import logs from system import environment ''', ) isort-9.0.1/tests/unit/profiles/test_hug.py0000644000000000000000000000561113615410400015744 0ustar00from functools import partial from ..utils import isort_test hug_isort_test = partial(isort_test, profile="hug", known_first_party=["hug"]) def test_hug_code_snippet_one(): hug_isort_test( ''' from __future__ import absolute_import import asyncio import sys from collections import OrderedDict, namedtuple from distutils.util import strtobool from functools import partial from itertools import chain from types import ModuleType from wsgiref.simple_server import make_server import falcon from falcon import HTTP_METHODS import hug.defaults import hug.output_format from hug import introspect from hug._version import current INTRO = """ /#######################################################################\\ `.----``..-------..``.----. :/:::::--:---------:--::::://. .+::::----##/-/oo+:-##----::::// `//::-------/oosoo-------::://. ## ## ## ## ##### .-:------./++o/o-.------::-` ``` ## ## ## ## ## `----.-./+o+:..----. `.:///. ######## ## ## ## ``` `----.-::::::------ `.-:::://. ## ## ## ## ## #### ://::--.``` -:``...-----...` `:--::::::-.` ## ## ## ## ## ## :/:::::::::-:- ````` .:::::-.` ## ## #### ###### ``.--:::::::. .:::.` ``..::. .:: EMBRACE THE APIs OF THE FUTURE ::- .:- -::` ::- VERSION {0} `::- -::` -::-` -::- \\########################################################################/ Copyright (C) 2016 Timothy Edmund Crosley Under the MIT License """.format( current )''' ) def test_hug_code_snippet_two(): hug_isort_test( """from __future__ import absolute_import import functools from collections import namedtuple from falcon import HTTP_METHODS import hug.api import hug.defaults import hug.output_format from hug import introspect from hug.format import underscore def default_output_format( content_type="application/json", apply_globally=False, api=None, cli=False, http=True ): """ ) def test_hug_code_snippet_three(): hug_isort_test( """from __future__ import absolute_import import argparse import asyncio import os import sys from collections import OrderedDict from functools import lru_cache, partial, wraps import falcon from falcon import HTTP_BAD_REQUEST import hug._empty as empty import hug.api import hug.output_format import hug.types as types from hug import introspect from hug.exceptions import InvalidTypeData from hug.format import parse_content_type from hug.types import ( MarshmallowInputSchema, MarshmallowReturnSchema, Multiple, OneOf, SmartBoolean, Text, text, ) DOC_TYPE_MAP = {str: "String", bool: "Boolean", list: "Multiple", int: "Integer", float: "Float"} """ ) isort-9.0.1/tests/unit/profiles/test_open_stack.py0000644000000000000000000000710513615410400017307 0ustar00from functools import partial from ..utils import isort_test open_stack_isort_test = partial(isort_test, profile="open_stack") def test_open_stack_code_snippet_one(): open_stack_isort_test( """import httplib import logging import random import StringIO import time import unittest import eventlet import webob.exc import nova.api.ec2 from nova.api import manager from nova.api import openstack from nova.auth import users from nova.endpoint import cloud import nova.flags from nova.i18n import _ from nova.i18n import _LC from nova import test """, known_first_party=["nova"], py_version="2", order_by_type=False, ) def test_open_stack_code_snippet_two(): open_stack_isort_test( """# Copyright 2011 VMware, Inc # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import inspect import os import random from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import context from neutron_lib.db import api as session from neutron_lib.plugins import directory from neutron_lib import rpc as n_rpc from oslo_concurrency import processutils from oslo_config import cfg from oslo_log import log as logging from oslo_messaging import server as rpc_server from oslo_service import loopingcall from oslo_service import service as common_service from oslo_utils import excutils from oslo_utils import importutils import psutil from neutron.common import config from neutron.common import profiler from neutron.conf import service from neutron import worker as neutron_worker from neutron import wsgi service.register_service_opts(service.SERVICE_OPTS) """, known_first_party=["neutron"], ) def test_open_stack_code_snippet_three(): open_stack_isort_test( """ # Copyright 2013 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools from oslo_log import log as logging import oslo_messaging as messaging from oslo_messaging.rpc import dispatcher from oslo_serialization import jsonutils from oslo_service import periodic_task from oslo_utils import importutils import six import nova.conf import nova.context import nova.exception from nova.i18n import _ __all__ = [ 'init', 'cleanup', 'set_defaults', 'add_extra_exmods', 'clear_extra_exmods', 'get_allowed_exmods', 'RequestContextSerializer', 'get_client', 'get_server', 'get_notifier', ] profiler = importutils.try_import("osprofiler.profiler") """, known_first_party=["nova"], ) isort-9.0.1/tests/unit/profiles/test_plone.py0000644000000000000000000000532113615410400016274 0ustar00from functools import partial from ..utils import isort_test plone_isort_test = partial(isort_test, profile="plone") def test_plone_code_snippet_one(): plone_isort_test( """# -*- coding: utf-8 -*- from plone.app.multilingual.testing import PLONE_APP_MULTILINGUAL_PRESET_FIXTURE # noqa from plone.app.robotframework.testing import REMOTE_LIBRARY_BUNDLE_FIXTURE from plone.app.testing import FunctionalTesting from plone.app.testing import IntegrationTesting from plone.app.testing import PloneWithPackageLayer from plone.testing import z2 import plone.app.multilingualindexes PAMI_FIXTURE = PloneWithPackageLayer( bases=(PLONE_APP_MULTILINGUAL_PRESET_FIXTURE,), name="PAMILayer:Fixture", gs_profile_id="plone.app.multilingualindexes:default", zcml_package=plone.app.multilingualindexes, zcml_filename="configure.zcml", additional_z2_products=["plone.app.multilingualindexes"], ) """ ) def test_plone_code_snippet_two(): plone_isort_test( """# -*- coding: utf-8 -*- from Acquisition import aq_base from App.class_init import InitializeClass from App.special_dtml import DTMLFile from BTrees.OOBTree import OOTreeSet from logging import getLogger from plone import api from plone.app.multilingual.events import ITranslationRegisteredEvent from plone.app.multilingual.interfaces import ITG from plone.app.multilingual.interfaces import ITranslatable from plone.app.multilingual.interfaces import ITranslationManager from plone.app.multilingualindexes.utils import get_configuration from plone.indexer.interfaces import IIndexableObject from Products.CMFPlone.utils import safe_hasattr from Products.DateRecurringIndex.index import DateRecurringIndex from Products.PluginIndexes.common.UnIndex import UnIndex from Products.ZCatalog.Catalog import Catalog from ZODB.POSException import ConflictError from zope.component import getMultiAdapter from zope.component import queryAdapter from zope.globalrequest import getRequest logger = getLogger(__name__) """ ) def test_plone_code_snippet_three(): plone_isort_test( """# -*- coding: utf-8 -*- from plone.app.querystring.interfaces import IQueryModifier from zope.interface import provider import logging logger = logging.getLogger(__name__) """ ) def test_plone_code_snippet_four(): plone_isort_test( """# -*- coding: utf-8 -*- from plone.app.querystring.interfaces import IQueryModifier from zope.interface import provider import logging def my_function(): pass """ ) def test_plone_code_snippet_five(): plone_isort_test( """# -*- coding: utf-8 -*- from plone.app.querystring.interfaces import IQueryModifier from zope.interface import provider import logging class MyClass: pass """ ) isort-9.0.1/tests/unit/profiles/test_pycharm.py0000644000000000000000000000214113615410400016617 0ustar00from functools import partial from ..utils import isort_test pycharm_isort_test = partial(isort_test, profile="pycharm") def test_pycharm_snippet_one(): pycharm_isort_test( """import shutil import sys from io import StringIO from pathlib import Path from typing import ( Optional, TextIO, Union, cast ) from warnings import warn from isort import core from . import io from .exceptions import ( ExistingSyntaxErrors, FileSkipComment, FileSkipSetting, IntroducedSyntaxErrors ) from .format import ( ask_whether_to_apply_changes_to_file, create_terminal_printer, show_unified_diff ) from .io import Empty from .place import module as place_module # noqa: F401 from .place import module_with_reason as place_module_with_reason # noqa: F401 from .settings import ( DEFAULT_CONFIG, Config ) def sort_code_string( code: str, extension: Optional[str] = None, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, disregard_skip: bool = False, show_diff: Union[bool, TextIO] = False, **config_kwargs, ): """ ) isort-9.0.1/tests/unit/profiles/test_wemake.py0000644000000000000000000000600613615410400016431 0ustar00"""A set of test cases for the wemake isort profile. Snippets are taken directly from the wemake-python-styleguide project here: https://github.com/wemake-services/wemake-python-styleguide """ from functools import partial from ..utils import isort_test wemake_isort_test = partial( isort_test, profile="wemake", known_first_party=["wemake_python_styleguide"] ) def test_wemake_snippet_one(): wemake_isort_test( """ import ast import tokenize import traceback from typing import ClassVar, Iterator, Sequence, Type from flake8.options.manager import OptionManager from typing_extensions import final from wemake_python_styleguide import constants, types from wemake_python_styleguide import version as pkg_version from wemake_python_styleguide.options.config import Configuration from wemake_python_styleguide.options.validation import validate_options from wemake_python_styleguide.presets.types import file_tokens as tokens_preset from wemake_python_styleguide.presets.types import filename as filename_preset from wemake_python_styleguide.presets.types import tree as tree_preset from wemake_python_styleguide.transformations.ast_tree import transform from wemake_python_styleguide.violations import system from wemake_python_styleguide.visitors import base VisitorClass = Type[base.BaseVisitor] """ ) def test_wemake_snippet_two(): wemake_isort_test( """ from collections import defaultdict from typing import ClassVar, DefaultDict, List from flake8.formatting.base import BaseFormatter from flake8.statistics import Statistics from flake8.style_guide import Violation from pygments import highlight from pygments.formatters import TerminalFormatter from pygments.lexers import PythonLexer from typing_extensions import Final from wemake_python_styleguide.version import pkg_version #: That url is generated and hosted by Sphinx. DOCS_URL_TEMPLATE: Final = ( 'https://wemake-python-stylegui.de/en/{0}/pages/usage/violations/' ) """ ) def test_wemake_snippet_three(): wemake_isort_test( """ import ast from pep8ext_naming import NamingChecker from typing_extensions import final from wemake_python_styleguide.transformations.ast.bugfixes import ( fix_async_offset, fix_line_number, ) from wemake_python_styleguide.transformations.ast.enhancements import ( set_if_chain, set_node_context, ) @final class _ClassVisitor(ast.NodeVisitor): ... """ ) def test_wemake_snippet_four(): """80 line length should not be fixed""" wemake_isort_test( """ from typing import Iterable, Iterator, Optional, Sequence, Tuple, TypeVar, Union """, """ from typing import Iterable, Iterator, Optional, Sequence, Tuple, TypeVar, Union """, ) def test_wemake_snippet_five(): """81 line length should be fixed""" wemake_isort_test( """ from typing import Iterable, Iterator, Optional, Sequence, Tuple, TypeVar, Union1 """, """ from typing import ( Iterable, Iterator, Optional, Sequence, Tuple, TypeVar, Union1, ) """, ) isort-9.0.1/.gitignore0000644000000000000000000000142613615410400011574 0ustar00*.py[cod] __pycache__ .DS_Store # C extensions *.so # Packages *.egg *.egg-info .eggs build eggs parts var sdist develop-eggs .installed.cfg lib lib64 MANIFEST .eggs # Installer logs pip-log.txt npm-debug.log # Unit test / coverage reports .coverage .coverage.* coverage.xml .pytest_cache .tox nosetests.xml htmlcov .cache .pytest_cache/ .hypothesis/ # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject # SQLite test_exp_framework # npm node_modules/ # dolphin .directory libpeerconnection.log # setuptools dist # IDE Files atlassian-ide-plugin.xml .idea/ *.swp *.kate-swp .ropeproject/ .vscode # pip pip-selfcheck.json # Python3 Venv Files .python-version .venv/ pyvenv.cfg # mypy .mypy_cache # Sphinx documentation build docs/_build/ docs/generated/ isort-9.0.1/LICENSE0000644000000000000000000000210113615410400010600 0ustar00The MIT License (MIT) Copyright (c) 2013 Timothy Edmund Crosley Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. isort-9.0.1/README.md0000644000000000000000000002312613615410400011064 0ustar00[![isort - isort your imports, so you don't have to.](https://raw.githubusercontent.com/pycqa/isort/main/art/logo_large.png)](https://isort.readthedocs.io/) ------------------------------------------------------------------------ [![PyPI version](https://badge.fury.io/py/isort.svg)](https://badge.fury.io/py/isort) [![Python Version](https://img.shields.io/pypi/pyversions/isort)][pypi status] [![CI](https://github.com/PyCQA/isort/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/PyCQA/isort/actions/workflows/continuous-integration.yml) [![Code coverage Status](https://codecov.io/gh/pycqa/isort/branch/main/graph/badge.svg)](https://codecov.io/gh/pycqa/isort) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://pypi.org/project/isort/) [![Downloads](https://pepy.tech/badge/isort)](https://pepy.tech/project/isort) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Imports: isort](https://img.shields.io/badge/imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://isort.readthedocs.io/) [![DeepSource](https://static.deepsource.io/deepsource-badge-light-mini.svg)](https://deepsource.io/gh/pycqa/isort/?ref=repository-badge) [pypi status]: https://pypi.org/project/isort/ _________________ [Read Latest Documentation](https://isort.readthedocs.io/) - [Browse GitHub Code Repository](https://github.com/pycqa/isort/) _________________ # isort your imports, so you don't have to. isort is a Python utility / library to sort imports alphabetically and automatically separate into sections and by type. It provides a command line utility, Python library and [plugins for various editors](https://github.com/pycqa/isort/wiki/isort-Plugins) to quickly sort all your imports. It requires Python 3.10+ to run but supports formatting Python 2 code too. - [Try isort now from your browser!](docs/quick_start/0.-try.md) - [Using black? See the isort and black compatibility guide.](docs/configuration/black_compatibility.md) - [isort has official support for pre-commit!](docs/configuration/pre-commit.md) ![Example Usage](https://raw.github.com/pycqa/isort/main/example.gif) Before isort: ```python from my_lib import Object import os from my_lib import Object3 from my_lib import Object2 import sys from third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14 import sys from __future__ import absolute_import from third_party import lib3 print("Hey") print("yo") ``` After isort: ```python from __future__ import absolute_import import os import sys from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14, lib15) from my_lib import Object, Object2, Object3 print("Hey") print("yo") ``` ## Installing isort Installing isort is as simple as: ```bash pip install isort ``` ## Using isort **From the command line**: To run on specific files: ```bash isort mypythonfile.py mypythonfile2.py ``` To apply recursively: ```bash isort . ``` If [globstar](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html) is enabled, `isort .` is equivalent to: ```bash isort **/*.py ``` To view proposed changes without applying them: ```bash isort mypythonfile.py --diff ``` Finally, to atomically run isort against a project, only applying changes if they don't introduce syntax errors: ```bash isort --atomic . ``` (Note: this is disabled by default, as it prevents isort from running against code written using a different version of Python.) **From within Python**: ```python import isort isort.file("pythonfile.py") ``` or: ```python import isort sorted_code = isort.code("import b\nimport a\n") ``` ## Installing isort's for your preferred text editor Several plugins have been written that enable to use isort from within a variety of text-editors. You can find a full list of them [on the isort wiki](https://github.com/pycqa/isort/wiki/isort-Plugins). Additionally, I will enthusiastically accept pull requests that include plugins for other text editors and add documentation for them as I am notified. ## Multi line output modes You will notice above the \"multi\_line\_output\" setting. This setting defines how from imports wrap when they extend past the line\_length limit and has [12 possible settings](docs/configuration/multi_line_output_modes.md). ## Indentation To change the how constant indents appear - simply change the indent property with the following accepted formats: - Number of spaces you would like. For example: 4 would cause standard 4 space indentation. - Tab - A verbatim string with quotes around it. For example: ```python " " ``` is equivalent to 4. For the import styles that use parentheses, you can control whether or not to include a trailing comma after the last import with the `include_trailing_comma` option (defaults to `False`). ## Intelligently Balanced Multi-line Imports As of isort 3.1.0 support for balanced multi-line imports has been added. With this enabled isort will dynamically change the import length to the one that produces the most balanced grid, while staying below the maximum import length defined. Example: ```python from __future__ import (absolute_import, division, print_function, unicode_literals) ``` Will be produced instead of: ```python from __future__ import (absolute_import, division, print_function, unicode_literals) ``` To enable this set `balanced_wrapping` to `True` in your config or pass the `-e` option into the command line utility. ## Custom Sections and Ordering isort provides configuration options to change almost every aspect of how imports are organized, ordered, or grouped together in sections. [Click here](docs/configuration/custom_sections_and_ordering.md) for an overview of all these options. ## Skip processing of imports (outside of configuration) To make isort ignore a single import simply add a comment at the end of the import line containing the text `isort:skip`: ```python import module # isort:skip ``` or: ```python from xyz import (abc, # isort:skip yo, hey) ``` To make isort skip an entire file simply add `isort:skip_file` to the module's doc string: ```python """ my_module.py Best module ever isort:skip_file """ import b import a ``` ## Adding or removing an import from multiple files isort can be ran or configured to add / remove imports automatically. [See a complete guide here.](docs/configuration/add_or_remove_imports.md) ## Using isort to verify code The `--check-only` option ------------------------- isort can also be used to verify that code is correctly formatted by running it with `-c`. Any files that contain incorrectly sorted and/or formatted imports will be outputted to `stderr`. ```bash isort **/*.py -c -v SUCCESS: /home/timothy/Projects/Open_Source/isort/isort_kate_plugin.py Everything Looks Good! ERROR: /home/timothy/Projects/Open_Source/isort/isort/isort.py Imports are incorrectly sorted. ``` One great place this can be used is with a pre-commit git hook, such as this one by \@acdha: This can help to ensure a certain level of code quality throughout a project. ## Git hook isort provides a hook function that can be integrated into your Git pre-commit script to check Python code before committing. [More info here.](docs/configuration/git_hook.md) ## Spread the word [![Imports: isort](https://img.shields.io/badge/imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://isort.readthedocs.io/) Place this badge at the top of your repository to let others know your project uses isort. For README.md: ```markdown [![Imports: isort](https://img.shields.io/badge/imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://isort.readthedocs.io/) ``` Or README.rst: ```rst .. image:: https://img.shields.io/badge/imports-isort-%231674b1?style=flat&labelColor=ef8336 :target: https://isort.readthedocs.io/ ``` ## Security contact information To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. ## Why isort? isort simply stands for import sort. It was originally called "sortImports" however I got tired of typing the extra characters and came to the realization camelCase is not pythonic. I wrote isort because in an organization I used to work in the manager came in one day and decided all code must have alphabetically sorted imports. The code base was huge - and he meant for us to do it by hand. However, being a programmer - I\'m too lazy to spend 8 hours mindlessly performing a function, but not too lazy to spend 16 hours automating it. I was given permission to open source sortImports and here we are :) ------------------------------------------------------------------------ [Get professionally supported isort with the Tidelift Subscription](https://tidelift.com/subscription/pkg/pypi-isort?utm_source=pypi-isort&utm_medium=referral&utm_campaign=readme) Professional support for isort is available as part of the [Tidelift Subscription](https://tidelift.com/subscription/pkg/pypi-isort?utm_source=pypi-isort&utm_medium=referral&utm_campaign=readme). Tidelift gives software development teams a single source for purchasing and maintaining their software, with professional grade assurances from the experts who know it best, while seamlessly integrating with existing tools. ------------------------------------------------------------------------ Thanks and I hope you find isort useful! ~Timothy Crosley isort-9.0.1/pyproject.toml0000644000000000000000000001337213615410400012523 0ustar00[project] name = "isort" dynamic = ["version"] description = "A Python utility / library to sort Python imports." authors = [{name = "Timothy Crosley", email = "timothy.crosley@gmail.com"}, {name = "staticdev", email = "staticdev-support@proton.me"}] license = "MIT" readme = "README.md" repository = "https://github.com/pycqa/isort" homepage = "https://isort.readthedocs.io//" documentation = "https://isort.readthedocs.io/" keywords = ["Refactor", "Lint", "Imports", "Sort", "Clean"] classifiers = [ "Development Status :: 6 - Mature", "Intended Audience :: Developers", "Natural Language :: English", "Environment :: Console", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.15", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries", "Topic :: Utilities", ] include = [ { path = "tests", format = "sdist" }, { path = "ACKNOWLEDGEMENTS.md", format = "sdist" }, { path = "LICENSE", format = "sdist" }, ] requires-python = ">=3.10.0" dependencies = [ "mypy-extensions>=1.1.0", ] [project.urls] Homepage = "https://isort.readthedocs.io/" Documentation = "https://isort.readthedocs.io/" Repository = "https://github.com/PyCQA/isort" Changelog = "https://github.com/PyCQA/isort/releases" [tool.hatch.version] source = "vcs" [tool.hatch.version.raw-options] local_scheme = "no-local-version" [project.scripts] isort = "isort.main:main" isort-identify-imports = "isort.main:identify_imports_main" [project.optional-dependencies] colors = ["colorama"] [tool.coverage.paths] source = [ "isort/", ".tox/*/lib/python*/site-packages/isort/", ".tox/*/lib/site-packages/isort/" ] tests = ["tests", "*/tests"] [tool.coverage.run] branch = true parallel = true source = ["isort", "tests"] omit = [ "isort/_vendored/*", "tests/*", ] [tool.coverage.report] show_missing = true fail_under = 99 exclude_lines = [ "pragma: no cover", "except ImportError:", "if TYPE_CHECKING:", "if __name__ == .__main__.:", "raise NotImplementedError", ] [build-system] requires = ["hatchling", "hatch-vcs", "hatch-mypyc"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel.hooks.mypyc] # To allow local development without compilation after each change. Force compilation with: # `HATCH_BUILD_HOOK_ENABLE_MYPYC=true uv sync --reinstall` enable-by-default = false dependencies = [ "hatch-mypyc", "mypy", "types-colorama>=0.4.2", ] exclude = [ # Keep `python -m isort` working in mypyc wheels. "isort/__main__.py", # We rely on `__dict__` to make Exceptions pickable, mypyc removes this attribute. "isort/exceptions.py" ] [dependency-groups] dev = [ "bandit>=1.8.6", "black>=25.9.0", "colorama>=0.4.6", "coverage[toml]>=7.10.7", "example-isort-sorting-plugin>=0.1.0", "example-shared-isort-profile>=0.1.0", "flake8>=7.3.0", "flake8-bugbear>=24.12.12", "flake8-pyproject>=1.2.3", "hatch>=1.14.0", "hypothesis>=6.140.3", "hypothesmith>=0.3.3", "libcst>=1.9.0", "mypy>=2.3.1", "pep8-naming>=0.15.1", "pytest>=8.4.2", "pytest-benchmark>=5.1.0", "ruff>=0.13.3", "stdlibs>=2026.2.26", "tox>=4.30.3", "tox-uv>=1.28.0", "types-colorama>=0.4.2", "uv>=0.8.23", ] docs = [ "myst-parser>=4.0.1", "sphinx>=7.4.7", "sphinx-immaterial>=0.13.9", ] [tool.flake8] max-line-length = 100 # Ignore non PEP 8 compliant rules as suggested by black # E203: https://github.com/psf/black/blob/3fab5ade71bccf80ae0a5af76729099869adea56/docs/the_black_code_style/current_style.md#slices extend-ignore = [ "E203" ] exclude = "_vendored" per-file-ignores = [ "tests/unit/example_crlf_file.py:F401", "tests/unit/profiles/test_black.py:E501", "tests/unit/test_regressions.py:E501", ] [tool.mypy] python_version = "3.10" strict = true follow_imports = "silent" packages = ["isort", "tests"] exclude = "isort/_vendored|tests/unit/example_projects|tests/unit/example_crlf_file.py" [[tool.mypy.overrides]] module = "tests.*" allow_untyped_defs = true allow_incomplete_defs = true allow_untyped_calls = true [[tool.mypy.overrides]] module = "importlib_metadata.*" ignore_missing_imports = true [tool.ruff] target-version = "py310" line-length = 100 lint.select = [ "ASYNC", "B", "C4", "C90", "E", "F", "FLY", "PERF", "PIE", "PLC", "PLE", "PT", "RUF", "S", "UP", "W", ] lint.ignore = [ "B904", "E501", "PERF203", "PT030", # Too much work to turn on for now "RUF100", ] lint.exclude = [ "isort/_vendored/*" ] lint.mccabe.max-complexity = 91 # Default is 10 [tool.ruff.lint.per-file-ignores] "isort/hooks.py" = [ "S603" ] "isort/output.py" = [ "PLC0206" ] "isort/settings.py" = [ "PLC0414", "S603", "S607" ] "tests/*" = [ "RUF001", "S" ] "tests/unit/example_crlf_file.py" = [ "F401" ] [tool.isort] profile = "hug" src_paths = ["isort", "tests"] skip = [ "tests/unit/example_crlf_file.py" ] [tool.pytest.ini_options] filterwarnings = [ "error", # isort raies many UserWarnings itself, ignore these globally as it is too much work to disable # these per warning for now. "ignore::UserWarning", # We don't really care about randomizing paths and don't want to add another hypothesis dependency "ignore:.*from_type\\(thing=Path\\).*:hypothesis.errors.SmallSearchSpaceWarning", ] isort-9.0.1/PKG-INFO0000644000000000000000000002622013615410400010700 0ustar00Metadata-Version: 2.5 Name: isort Version: 9.0.1 Summary: A Python utility / library to sort Python imports. Project-URL: Homepage, https://isort.readthedocs.io/ Project-URL: Documentation, https://isort.readthedocs.io/ Project-URL: Repository, https://github.com/PyCQA/isort Project-URL: Changelog, https://github.com/PyCQA/isort/releases Author-email: Timothy Crosley , staticdev License-Expression: MIT License-File: LICENSE Keywords: Clean,Imports,Lint,Refactor,Sort Classifier: Development Status :: 6 - Mature Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: 3.14 Classifier: Programming Language :: Python :: 3.15 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Utilities Requires-Python: >=3.10.0 Requires-Dist: mypy-extensions>=1.1.0 Provides-Extra: colors Requires-Dist: colorama; extra == 'colors' Description-Content-Type: text/markdown [![isort - isort your imports, so you don't have to.](https://raw.githubusercontent.com/pycqa/isort/main/art/logo_large.png)](https://isort.readthedocs.io/) ------------------------------------------------------------------------ [![PyPI version](https://badge.fury.io/py/isort.svg)](https://badge.fury.io/py/isort) [![Python Version](https://img.shields.io/pypi/pyversions/isort)][pypi status] [![CI](https://github.com/PyCQA/isort/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/PyCQA/isort/actions/workflows/continuous-integration.yml) [![Code coverage Status](https://codecov.io/gh/pycqa/isort/branch/main/graph/badge.svg)](https://codecov.io/gh/pycqa/isort) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://pypi.org/project/isort/) [![Downloads](https://pepy.tech/badge/isort)](https://pepy.tech/project/isort) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Imports: isort](https://img.shields.io/badge/imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://isort.readthedocs.io/) [![DeepSource](https://static.deepsource.io/deepsource-badge-light-mini.svg)](https://deepsource.io/gh/pycqa/isort/?ref=repository-badge) [pypi status]: https://pypi.org/project/isort/ _________________ [Read Latest Documentation](https://isort.readthedocs.io/) - [Browse GitHub Code Repository](https://github.com/pycqa/isort/) _________________ # isort your imports, so you don't have to. isort is a Python utility / library to sort imports alphabetically and automatically separate into sections and by type. It provides a command line utility, Python library and [plugins for various editors](https://github.com/pycqa/isort/wiki/isort-Plugins) to quickly sort all your imports. It requires Python 3.10+ to run but supports formatting Python 2 code too. - [Try isort now from your browser!](docs/quick_start/0.-try.md) - [Using black? See the isort and black compatibility guide.](docs/configuration/black_compatibility.md) - [isort has official support for pre-commit!](docs/configuration/pre-commit.md) ![Example Usage](https://raw.github.com/pycqa/isort/main/example.gif) Before isort: ```python from my_lib import Object import os from my_lib import Object3 from my_lib import Object2 import sys from third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14 import sys from __future__ import absolute_import from third_party import lib3 print("Hey") print("yo") ``` After isort: ```python from __future__ import absolute_import import os import sys from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14, lib15) from my_lib import Object, Object2, Object3 print("Hey") print("yo") ``` ## Installing isort Installing isort is as simple as: ```bash pip install isort ``` ## Using isort **From the command line**: To run on specific files: ```bash isort mypythonfile.py mypythonfile2.py ``` To apply recursively: ```bash isort . ``` If [globstar](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html) is enabled, `isort .` is equivalent to: ```bash isort **/*.py ``` To view proposed changes without applying them: ```bash isort mypythonfile.py --diff ``` Finally, to atomically run isort against a project, only applying changes if they don't introduce syntax errors: ```bash isort --atomic . ``` (Note: this is disabled by default, as it prevents isort from running against code written using a different version of Python.) **From within Python**: ```python import isort isort.file("pythonfile.py") ``` or: ```python import isort sorted_code = isort.code("import b\nimport a\n") ``` ## Installing isort's for your preferred text editor Several plugins have been written that enable to use isort from within a variety of text-editors. You can find a full list of them [on the isort wiki](https://github.com/pycqa/isort/wiki/isort-Plugins). Additionally, I will enthusiastically accept pull requests that include plugins for other text editors and add documentation for them as I am notified. ## Multi line output modes You will notice above the \"multi\_line\_output\" setting. This setting defines how from imports wrap when they extend past the line\_length limit and has [12 possible settings](docs/configuration/multi_line_output_modes.md). ## Indentation To change the how constant indents appear - simply change the indent property with the following accepted formats: - Number of spaces you would like. For example: 4 would cause standard 4 space indentation. - Tab - A verbatim string with quotes around it. For example: ```python " " ``` is equivalent to 4. For the import styles that use parentheses, you can control whether or not to include a trailing comma after the last import with the `include_trailing_comma` option (defaults to `False`). ## Intelligently Balanced Multi-line Imports As of isort 3.1.0 support for balanced multi-line imports has been added. With this enabled isort will dynamically change the import length to the one that produces the most balanced grid, while staying below the maximum import length defined. Example: ```python from __future__ import (absolute_import, division, print_function, unicode_literals) ``` Will be produced instead of: ```python from __future__ import (absolute_import, division, print_function, unicode_literals) ``` To enable this set `balanced_wrapping` to `True` in your config or pass the `-e` option into the command line utility. ## Custom Sections and Ordering isort provides configuration options to change almost every aspect of how imports are organized, ordered, or grouped together in sections. [Click here](docs/configuration/custom_sections_and_ordering.md) for an overview of all these options. ## Skip processing of imports (outside of configuration) To make isort ignore a single import simply add a comment at the end of the import line containing the text `isort:skip`: ```python import module # isort:skip ``` or: ```python from xyz import (abc, # isort:skip yo, hey) ``` To make isort skip an entire file simply add `isort:skip_file` to the module's doc string: ```python """ my_module.py Best module ever isort:skip_file """ import b import a ``` ## Adding or removing an import from multiple files isort can be ran or configured to add / remove imports automatically. [See a complete guide here.](docs/configuration/add_or_remove_imports.md) ## Using isort to verify code The `--check-only` option ------------------------- isort can also be used to verify that code is correctly formatted by running it with `-c`. Any files that contain incorrectly sorted and/or formatted imports will be outputted to `stderr`. ```bash isort **/*.py -c -v SUCCESS: /home/timothy/Projects/Open_Source/isort/isort_kate_plugin.py Everything Looks Good! ERROR: /home/timothy/Projects/Open_Source/isort/isort/isort.py Imports are incorrectly sorted. ``` One great place this can be used is with a pre-commit git hook, such as this one by \@acdha: This can help to ensure a certain level of code quality throughout a project. ## Git hook isort provides a hook function that can be integrated into your Git pre-commit script to check Python code before committing. [More info here.](docs/configuration/git_hook.md) ## Spread the word [![Imports: isort](https://img.shields.io/badge/imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://isort.readthedocs.io/) Place this badge at the top of your repository to let others know your project uses isort. For README.md: ```markdown [![Imports: isort](https://img.shields.io/badge/imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://isort.readthedocs.io/) ``` Or README.rst: ```rst .. image:: https://img.shields.io/badge/imports-isort-%231674b1?style=flat&labelColor=ef8336 :target: https://isort.readthedocs.io/ ``` ## Security contact information To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. ## Why isort? isort simply stands for import sort. It was originally called "sortImports" however I got tired of typing the extra characters and came to the realization camelCase is not pythonic. I wrote isort because in an organization I used to work in the manager came in one day and decided all code must have alphabetically sorted imports. The code base was huge - and he meant for us to do it by hand. However, being a programmer - I\'m too lazy to spend 8 hours mindlessly performing a function, but not too lazy to spend 16 hours automating it. I was given permission to open source sortImports and here we are :) ------------------------------------------------------------------------ [Get professionally supported isort with the Tidelift Subscription](https://tidelift.com/subscription/pkg/pypi-isort?utm_source=pypi-isort&utm_medium=referral&utm_campaign=readme) Professional support for isort is available as part of the [Tidelift Subscription](https://tidelift.com/subscription/pkg/pypi-isort?utm_source=pypi-isort&utm_medium=referral&utm_campaign=readme). Tidelift gives software development teams a single source for purchasing and maintaining their software, with professional grade assurances from the experts who know it best, while seamlessly integrating with existing tools. ------------------------------------------------------------------------ Thanks and I hope you find isort useful! ~Timothy Crosley