diff --git a/PKG-INFO b/PKG-INFO index c790708..4301784 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: isort -Version: 5.3.2 +Version: 5.4.2 Summary: A Python utility / library to sort Python imports. Home-page: https://timothycrosley.github.io/isort/ License: MIT @@ -290,6 +290,40 @@ ... ``` +**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) +``` + Note: to change the how constant indents appear - simply change the indent property with the following accepted formats: diff --git a/README.md b/README.md index ed5eff4..99e9114 100644 --- a/README.md +++ b/README.md @@ -251,6 +251,40 @@ ... ``` +**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) +``` + Note: to change the how constant indents appear - simply change the indent property with the following accepted formats: diff --git a/isort/_version.py b/isort/_version.py index 07f0e9e..cfda0f8 100644 --- a/isort/_version.py +++ b/isort/_version.py @@ -1 +1 @@ -__version__ = "5.3.2" +__version__ = "5.4.2" diff --git a/isort/deprecated/finders.py b/isort/deprecated/finders.py index f32e9ac..77eb23f 100644 --- a/isort/deprecated/finders.py +++ b/isort/deprecated/finders.py @@ -85,14 +85,13 @@ regexp = "^" + known_pattern.replace("*", ".*").replace("?", ".?") + "$" self.known_patterns.append((re.compile(regexp), placement)) - @staticmethod - def _parse_known_pattern(pattern: str) -> List[str]: + 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(pattern) - if os.path.isdir(os.path.join(pattern, filename)) + for filename in os.listdir(os.path.join(self.config.directory, pattern)) + if os.path.isdir(os.path.join(self.config.directory, pattern, filename)) ] else: patterns = [pattern] diff --git a/isort/main.py b/isort/main.py index 39c96b9..58c4e10 100644 --- a/isort/main.py +++ b/isort/main.py @@ -328,6 +328,13 @@ action="store_true", ) parser.add_argument( + "--lss", + "--length-sort-straight", + help="Sort straight imports by their string length.", + dest="length_sort_straight", + action="store_true", + ) + parser.add_argument( "-m", "--multi-line", dest="multi_line_output", @@ -335,7 +342,9 @@ + [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-vert-grid-grouped-no-comma).", + "5-vert-grid-grouped, 6-vert-grid-grouped-no-comma, 7-noqa, " + "8-vertical-hanging-indent-bracket, 9-vertical-prefix-from-module-import, " + "10-hanging-indent-with-parentheses).", ) parser.add_argument( "-n", @@ -453,7 +462,7 @@ "--skip-gitignore", action="store_true", dest="skip_gitignore", - help="Treat project as a git respository and ignore files listed in .gitignore", + help="Treat project as a git repository and ignore files listed in .gitignore", ) inline_args_group.add_argument( "--sl", @@ -736,6 +745,7 @@ arguments["remapped_deprecated_args"] = remapped_deprecated_args if "dont_order_by_type" in arguments: arguments["order_by_type"] = False + del arguments["dont_order_by_type"] multi_line_output = arguments.get("multi_line_output", None) if multi_line_output: if multi_line_output.isdigit(): diff --git a/isort/output.py b/isort/output.py index d745f9a..6a21c13 100644 --- a/isort/output.py +++ b/isort/output.py @@ -51,7 +51,10 @@ for section in sections: straight_modules = parsed.imports[section]["straight"] straight_modules = sorting.naturally( - straight_modules, key=lambda key: sorting.module_key(key, config, section_name=section) + straight_modules, + key=lambda key: sorting.module_key( + key, config, section_name=section, straight_import=True + ), ) from_modules = parsed.imports[section]["from"] from_modules = sorting.naturally( @@ -298,6 +301,11 @@ new_section_output.extend(above_comments) if "*" in from_imports and config.combine_star: + if config.combine_as_imports: + comments = list(comments or ()) + comments += parsed.categorized_comments["from"].pop( + f"{module}.__combined_as__", [] + ) import_statement = wrap.line( with_comments( comments, @@ -382,7 +390,6 @@ for as_import in as_imports[from_import] ) - star_import = False if "*" in from_imports: new_section_output.append( with_comments( @@ -393,7 +400,6 @@ ) ) from_imports.remove("*") - star_import = True comments = None for from_import in copy.copy(from_imports): @@ -425,15 +431,16 @@ ) ): from_import_section.append(from_imports.pop(0)) - if star_import: - import_statement = import_start + (", ").join(from_import_section) - else: - import_statement = with_comments( - comments, - import_start + (", ").join(from_import_section), - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, - ) + if config.combine_as_imports: + comments = (comments or []) + list( + parsed.categorized_comments["from"].pop(f"{module}.__combined_as__", ()) + ) + import_statement = with_comments( + comments, + import_start + (", ").join(from_import_section), + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ) if not from_import_section: import_statement = "" diff --git a/isort/parse.py b/isort/parse.py index 350188a..2ab43ac 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -320,10 +320,12 @@ 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] - module = just_imports[0] + "." + nested_module + top_level_module = just_imports[0] + module = top_level_module + "." + nested_module as_name = just_imports[as_index + 1] if nested_module == as_name and config.remove_redundant_aliases: pass @@ -336,7 +338,13 @@ pass elif as_name not in as_map["straight"][module]: as_map["straight"][module].append(as_name) - if not config.combine_as_imports: + + if config.combine_as_imports and nested_module: + categorized_comments["from"].setdefault( + f"{top_level_module}.__combined_as__", [] + ).extend(comments) + comments = [] + else: categorized_comments["straight"][module] = comments comments = [] del just_imports[as_index : as_index + 2] diff --git a/isort/settings.py b/isort/settings.py index 68f09bb..500790b 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -132,6 +132,7 @@ 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() @@ -522,14 +523,13 @@ self._section_comments = tuple(f"# {heading}" for heading in self.import_headings.values()) return self._section_comments - @staticmethod - def _parse_known_pattern(pattern: str) -> List[str]: + 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(pattern) - if os.path.isdir(os.path.join(pattern, 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] @@ -653,7 +653,9 @@ 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.keys() + key: value + for key, value in settings.items() + if key in _DEFAULT_SETTINGS.keys() or key.startswith(KNOWN_PREFIX) } for key, value in settings.items(): diff --git a/isort/sorting.py b/isort/sorting.py index 4d010a1..1664a2f 100644 --- a/isort/sorting.py +++ b/isort/sorting.py @@ -13,6 +13,7 @@ sub_imports: bool = False, ignore_case: bool = False, section_name: Optional[Any] = None, + straight_import: Optional[bool] = False, ) -> str: match = re.match(r"^(\.+)\s*(.*)", module_name) if match: @@ -41,7 +42,11 @@ if not config.case_sensitive: module_name = module_name.lower() - length_sort = config.length_sort or str(section_name).lower() in config.length_sort_sections + 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 = length_sort and (str(len(module_name)) + ":" + module_name) or module_name return f"{module_name in config.force_to_top and 'A' or 'B'}{prefix}{_length_sort_maybe}" diff --git a/pyproject.toml b/pyproject.toml index f19a5c9..1902c9f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ [tool.poetry] name = "isort" -version = "5.3.2" +version = "5.4.2" description = "A Python utility / library to sort Python imports." authors = ["Timothy Crosley "] license = "MIT" diff --git a/setup.py b/setup.py index 0ded62a..306e866 100644 --- a/setup.py +++ b/setup.py @@ -24,9 +24,9 @@ setup_kwargs = { 'name': 'isort', - 'version': '5.3.2', + 'version': '5.4.2', 'description': 'A Python utility / library to sort Python imports.', - 'long_description': '[![isort - isort your imports, so you don\'t have to.](https://raw.githubusercontent.com/timothycrosley/isort/develop/art/logo_large.png)](https://timothycrosley.github.io/isort/)\n\n------------------------------------------------------------------------\n\n[![PyPI version](https://badge.fury.io/py/isort.svg)](https://badge.fury.io/py/isort)\n[![Test Status](https://github.com/timothycrosley/isort/workflows/Test/badge.svg?branch=develop)](https://github.com/timothycrosley/isort/actions?query=workflow%3ATest)\n[![Lint Status](https://github.com/timothycrosley/isort/workflows/Lint/badge.svg?branch=develop)](https://github.com/timothycrosley/isort/actions?query=workflow%3ALint)\n[![Code coverage Status](https://codecov.io/gh/timothycrosley/isort/branch/develop/graph/badge.svg)](https://codecov.io/gh/timothycrosley/isort)\n[![Maintainability](https://api.codeclimate.com/v1/badges/060372d3e77573072609/maintainability)](https://codeclimate.com/github/timothycrosley/isort/maintainability)\n[![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://pypi.org/project/isort/)\n[![Join the chat at https://gitter.im/timothycrosley/isort](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/timothycrosley/isort?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)\n[![Downloads](https://pepy.tech/badge/isort)](https://pepy.tech/project/isort)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://timothycrosley.github.io/isort/)\n[![DeepSource](https://static.deepsource.io/deepsource-badge-light-mini.svg)](https://deepsource.io/gh/timothycrosley/isort/?ref=repository-badge)\n_________________\n\n[Read Latest Documentation](https://timothycrosley.github.io/isort/) - [Browse GitHub Code Repository](https://github.com/timothycrosley/isort/)\n_________________\n\nisort your imports, so you don\'t have to.\n\nisort is a Python utility / library to sort imports alphabetically, and\nautomatically separated into sections and by type. It provides a command line\nutility, Python library and [plugins for various\neditors](https://github.com/timothycrosley/isort/wiki/isort-Plugins) to\nquickly sort all your imports. It requires Python 3.6+ to run but\nsupports formatting Python 2 code too.\n\n[Try isort now from your browser!](https://timothycrosley.github.io/isort/docs/quick_start/0.-try/)\n\n![Example Usage](https://raw.github.com/timothycrosley/isort/develop/example.gif)\n\nBefore isort:\n\n```python\nfrom my_lib import Object\n\nimport os\n\nfrom my_lib import Object3\n\nfrom my_lib import Object2\n\nimport sys\n\nfrom third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14\n\nimport sys\n\nfrom __future__ import absolute_import\n\nfrom third_party import lib3\n\nprint("Hey")\nprint("yo")\n```\n\nAfter isort:\n\n```python\nfrom __future__ import absolute_import\n\nimport os\nimport sys\n\nfrom third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8,\n lib9, lib10, lib11, lib12, lib13, lib14, lib15)\n\nfrom my_lib import Object, Object2, Object3\n\nprint("Hey")\nprint("yo")\n```\n\n## Installing isort\n\nInstalling isort is as simple as:\n\n```bash\npip install isort\n```\n\nInstall isort with requirements.txt support:\n\n```bash\npip install isort[requirements_deprecated_finder]\n```\n\nInstall isort with Pipfile support:\n\n```bash\npip install isort[pipfile_deprecated_finder]\n```\n\nInstall isort with both formats support:\n\n```bash\npip install isort[requirements_deprecated_finder,pipfile_deprecated_finder]\n```\n\n## Using isort\n\n**From the command line**:\n\n```bash\nisort mypythonfile.py mypythonfile2.py\n```\n\nor recursively:\n\n```bash\nisort .\n```\n\n*which is equivalent to:*\n\n```bash\nisort **/*.py\n```\n\nor to see the proposed changes without applying them:\n\n```bash\nisort mypythonfile.py --diff\n```\n\nFinally, to atomically run isort against a project, only applying\nchanges if they don\'t introduce syntax errors do:\n\n```bash\nisort --atomic .\n```\n\n(Note: this is disabled by default as it keeps isort from being able to\nrun against code written using a different version of Python)\n\n**From within Python**:\n\n```bash\nimport isort\n\nisort.file("pythonfile.py")\n```\n\nor:\n\n```bash\nimport isort\n\nsorted_code = isort.code("import b\\nimport a\\n")\n```\n\n## Installing isort\'s for your preferred text editor\n\nSeveral plugins have been written that enable to use isort from within a\nvariety of text-editors. You can find a full list of them [on the isort\nwiki](https://github.com/timothycrosley/isort/wiki/isort-Plugins).\nAdditionally, I will enthusiastically accept pull requests that include\nplugins for other text editors and add documentation for them as I am\nnotified.\n\n## Multi line output modes\n\nYou will notice above the \\"multi\\_line\\_output\\" setting. This setting\ndefines how from imports wrap when they extend past the line\\_length\nlimit and has 6 possible settings:\n\n**0 - Grid**\n\n```python\nfrom third_party import (lib1, lib2, lib3,\n lib4, lib5, ...)\n```\n\n**1 - Vertical**\n\n```python\nfrom third_party import (lib1,\n lib2,\n lib3\n lib4,\n lib5,\n ...)\n```\n\n**2 - Hanging Indent**\n\n```python\nfrom third_party import \\\n lib1, lib2, lib3, \\\n lib4, lib5, lib6\n```\n\n**3 - Vertical Hanging Indent**\n\n```python\nfrom third_party import (\n lib1,\n lib2,\n lib3,\n lib4,\n)\n```\n\n**4 - Hanging Grid**\n\n```python\nfrom third_party import (\n lib1, lib2, lib3, lib4,\n lib5, ...)\n```\n\n**5 - Hanging Grid Grouped**\n\n```python\nfrom third_party import (\n lib1, lib2, lib3, lib4,\n lib5, ...\n)\n```\n\n**6 - Hanging Grid Grouped, No Trailing Comma**\n\nIn Mode 5 isort leaves a single extra space to maintain consistency of\noutput when a comma is added at the end. Mode 6 is the same - except\nthat no extra space is maintained leading to the possibility of lines\none character longer. You can enforce a trailing comma by using this in\nconjunction with `-tc` or `include_trailing_comma: True`.\n\n```python\nfrom third_party import (\n lib1, lib2, lib3, lib4,\n lib5\n)\n```\n\n**7 - NOQA**\n\n```python\nfrom third_party import lib1, lib2, lib3, ... # NOQA\n```\n\nAlternatively, you can set `force_single_line` to `True` (`-sl` on the\ncommand line) and every import will appear on its own line:\n\n```python\nfrom third_party import lib1\nfrom third_party import lib2\nfrom third_party import lib3\n...\n```\n\nNote: to change the how constant indents appear - simply change the\nindent property with the following accepted formats:\n\n- Number of spaces you would like. For example: 4 would cause standard\n 4 space indentation.\n- Tab\n- A verbatim string with quotes around it.\n\nFor example:\n\n```python\n" "\n```\n\nis equivalent to 4.\n\nFor the import styles that use parentheses, you can control whether or\nnot to include a trailing comma after the last import with the\n`include_trailing_comma` option (defaults to `False`).\n\n## Intelligently Balanced Multi-line Imports\n\nAs of isort 3.1.0 support for balanced multi-line imports has been\nadded. With this enabled isort will dynamically change the import length\nto the one that produces the most balanced grid, while staying below the\nmaximum import length defined.\n\nExample:\n\n```python\nfrom __future__ import (absolute_import, division,\n print_function, unicode_literals)\n```\n\nWill be produced instead of:\n\n```python\nfrom __future__ import (absolute_import, division, print_function,\n unicode_literals)\n```\n\nTo enable this set `balanced_wrapping` to `True` in your config or pass\nthe `-e` option into the command line utility.\n\n## Custom Sections and Ordering\n\nYou can change the section order with `sections` option from the default\nof:\n\n```ini\nFUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER\n```\n\nto your preference:\n\n```ini\nsections=FUTURE,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER\n```\n\nYou also can define your own sections and their order.\n\nExample:\n\n```ini\nknown_django=django\nknown_pandas=pandas,numpy\nsections=FUTURE,STDLIB,DJANGO,THIRDPARTY,PANDAS,FIRSTPARTY,LOCALFOLDER\n```\n\nwould create two new sections with the specified known modules.\n\nThe `no_lines_before` option will prevent the listed sections from being\nsplit from the previous section by an empty line.\n\nExample:\n\n```ini\nsections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER\nno_lines_before=LOCALFOLDER\n```\n\nwould produce a section with both FIRSTPARTY and LOCALFOLDER modules\ncombined.\n\n**IMPORTANT NOTE**: It is very important to know when setting `known` sections that the naming\ndoes not directly map for historical reasons. For custom settings, the only difference is\ncapitalization (`known_custom=custom` VS `sections=CUSTOM,...`) for all others reference the\nfollowing mapping:\n\n - `known_standard_library` : `STANDARD_LIBRARY`\n - `extra_standard_library` : `STANDARD_LIBRARY` # Like known standard library but appends instead of replacing\n - `known_future_library` : `FUTURE`\n - `known_first_party`: `FIRSTPARTY`\n - `known_third_party`: `THIRDPARTY`\n - `known_local_folder`: `LOCALFOLDER`\n\nThis will likely be changed in isort 6.0.0+ in a backwards compatible way.\n\n## Auto-comment import sections\n\nSome projects prefer to have import sections uniquely titled to aid in\nidentifying the sections quickly when visually scanning. isort can\nautomate this as well. To do this simply set the\n`import_heading_{section_name}` setting for each section you wish to\nhave auto commented - to the desired comment.\n\nFor Example:\n\n```ini\nimport_heading_stdlib=Standard Library\nimport_heading_firstparty=My Stuff\n```\n\nWould lead to output looking like the following:\n\n```python\n# Standard Library\nimport os\nimport sys\n\nimport django.settings\n\n# My Stuff\nimport myproject.test\n```\n\n## Ordering by import length\n\nisort also makes it easy to sort your imports by length, simply by\nsetting the `length_sort` option to `True`. This will result in the\nfollowing output style:\n\n```python\nfrom evn.util import (\n Pool,\n Dict,\n Options,\n Constant,\n DecayDict,\n UnexpectedCodePath,\n)\n```\n\nIt is also possible to opt-in to sorting imports by length for only\nspecific sections by using `length_sort_` followed by the section name\nas a configuration item, e.g.:\n\n length_sort_stdlib=1\n\n## Controlling how isort sections `from` imports\n\nBy default isort places straight (`import y`) imports above from imports (`from x import y`):\n\n```python\nimport b\nfrom a import a # This will always appear below because it is a from import.\n```\n\nHowever, if you prefer to keep strict alphabetical sorting you can set [force sort within sections](https://timothycrosley.github.io/isort/docs/configuration/options/#force-sort-within-sections) to true. Resulting in:\n\n\n```python\nfrom a import a # This will now appear at top because a appears in the alphabet before b\nimport b\n```\n\nYou can even tell isort to always place from imports on top, instead of the default of placing them on bottom, using [from first](https://timothycrosley.github.io/isort/docs/configuration/options/#from-first).\n\n```python\nfrom b import b # If from first is set to True, all from imports will be placed before non-from imports.\nimport a\n```\n\n## Skip processing of imports (outside of configuration)\n\nTo make isort ignore a single import simply add a comment at the end of\nthe import line containing the text `isort:skip`:\n\n```python\nimport module # isort:skip\n```\n\nor:\n\n```python\nfrom xyz import (abc, # isort:skip\n yo,\n hey)\n```\n\nTo make isort skip an entire file simply add `isort:skip_file` to the\nmodule\'s doc string:\n\n```python\n""" my_module.py\n Best module ever\n\n isort:skip_file\n"""\n\nimport b\nimport a\n```\n\n## Adding an import to multiple files\n\nisort makes it easy to add an import statement across multiple files,\nwhile being assured it\'s correctly placed.\n\nTo add an import to all files:\n\n```bash\nisort -a "from __future__ import print_function" *.py\n```\n\nTo add an import only to files that already have imports:\n\n```bash\nisort -a "from __future__ import print_function" --append-only *.py\n```\n\n\n## Removing an import from multiple files\n\nisort also makes it easy to remove an import from multiple files,\nwithout having to be concerned with how it was originally formatted.\n\nFrom the command line:\n\n```bash\nisort --rm "os.system" *.py\n```\n\n## Using isort to verify code\n\nThe `--check-only` option\n-------------------------\n\nisort can also be used to used to verify that code is correctly\nformatted by running it with `-c`. Any files that contain incorrectly\nsorted and/or formatted imports will be outputted to `stderr`.\n\n```bash\nisort **/*.py -c -v\n\nSUCCESS: /home/timothy/Projects/Open_Source/isort/isort_kate_plugin.py Everything Looks Good!\nERROR: /home/timothy/Projects/Open_Source/isort/isort/isort.py Imports are incorrectly sorted.\n```\n\nOne great place this can be used is with a pre-commit git hook, such as\nthis one by \\@acdha:\n\n\n\nThis can help to ensure a certain level of code quality throughout a\nproject.\n\nGit hook\n--------\n\nisort provides a hook function that can be integrated into your Git\npre-commit script to check Python code before committing.\n\nTo cause the commit to fail if there are isort errors (strict mode),\ninclude the following in `.git/hooks/pre-commit`:\n\n```python\n#!/usr/bin/env python\nimport sys\nfrom isort.hooks import git_hook\n\nsys.exit(git_hook(strict=True, modify=True, lazy=True))\n```\n\nIf you just want to display warnings, but allow the commit to happen\nanyway, call `git_hook` without the strict parameter. If you want to\ndisplay warnings, but not also fix the code, call `git_hook` without the\nmodify parameter.\nThe `lazy` argument is to support users who are "lazy" to add files\nindividually to the index and tend to use `git commit -a` instead.\nSet it to `True` to ensure all tracked files are properly isorted,\nleave it out or set it to `False` to check only files added to your\nindex.\n\n## Setuptools integration\n\nUpon installation, isort enables a `setuptools` command that checks\nPython files declared by your project.\n\nRunning `python setup.py isort` on the command line will check the files\nlisted in your `py_modules` and `packages`. If any warning is found, the\ncommand will exit with an error code:\n\n```bash\n$ python setup.py isort\n```\n\nAlso, to allow users to be able to use the command without having to\ninstall isort themselves, add isort to the setup\\_requires of your\n`setup()` like so:\n\n```python\nsetup(\n name="project",\n packages=["project"],\n\n setup_requires=[\n "isort"\n ]\n)\n```\n\n## Spread the word\n\n[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://timothycrosley.github.io/isort/)\n\nPlace this badge at the top of your repository to let others know your project uses isort.\n\nFor README.md:\n\n```markdown\n[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://timothycrosley.github.io/isort/)\n```\n\nOr README.rst:\n\n```rst\n.. image:: https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336\n :target: https://timothycrosley.github.io/isort/\n```\n\n## Security contact information\n\nTo report a security vulnerability, please use the [Tidelift security\ncontact](https://tidelift.com/security). Tidelift will coordinate the\nfix and disclosure.\n\n## Why isort?\n\nisort simply stands for import sort. It was originally called\n"sortImports" however I got tired of typing the extra characters and\ncame to the realization camelCase is not pythonic.\n\nI wrote isort because in an organization I used to work in the manager\ncame in one day and decided all code must have alphabetically sorted\nimports. The code base was huge - and he meant for us to do it by hand.\nHowever, being a programmer - I\\\'m too lazy to spend 8 hours mindlessly\nperforming a function, but not too lazy to spend 16 hours automating it.\nI was given permission to open source sortImports and here we are :)\n\n------------------------------------------------------------------------\n\n[Get professionally supported isort with the Tidelift\nSubscription](https://tidelift.com/subscription/pkg/pypi-isort?utm_source=pypi-isort&utm_medium=referral&utm_campaign=readme)\n\nProfessional support for isort is available as part of the [Tidelift\nSubscription](https://tidelift.com/subscription/pkg/pypi-isort?utm_source=pypi-isort&utm_medium=referral&utm_campaign=readme).\nTidelift gives software development teams a single source for purchasing\nand maintaining their software, with professional grade assurances from\nthe experts who know it best, while seamlessly integrating with existing\ntools.\n\n------------------------------------------------------------------------\n\nThanks and I hope you find isort useful!\n\n~Timothy Crosley\n', + 'long_description': '[![isort - isort your imports, so you don\'t have to.](https://raw.githubusercontent.com/timothycrosley/isort/develop/art/logo_large.png)](https://timothycrosley.github.io/isort/)\n\n------------------------------------------------------------------------\n\n[![PyPI version](https://badge.fury.io/py/isort.svg)](https://badge.fury.io/py/isort)\n[![Test Status](https://github.com/timothycrosley/isort/workflows/Test/badge.svg?branch=develop)](https://github.com/timothycrosley/isort/actions?query=workflow%3ATest)\n[![Lint Status](https://github.com/timothycrosley/isort/workflows/Lint/badge.svg?branch=develop)](https://github.com/timothycrosley/isort/actions?query=workflow%3ALint)\n[![Code coverage Status](https://codecov.io/gh/timothycrosley/isort/branch/develop/graph/badge.svg)](https://codecov.io/gh/timothycrosley/isort)\n[![Maintainability](https://api.codeclimate.com/v1/badges/060372d3e77573072609/maintainability)](https://codeclimate.com/github/timothycrosley/isort/maintainability)\n[![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://pypi.org/project/isort/)\n[![Join the chat at https://gitter.im/timothycrosley/isort](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/timothycrosley/isort?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)\n[![Downloads](https://pepy.tech/badge/isort)](https://pepy.tech/project/isort)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://timothycrosley.github.io/isort/)\n[![DeepSource](https://static.deepsource.io/deepsource-badge-light-mini.svg)](https://deepsource.io/gh/timothycrosley/isort/?ref=repository-badge)\n_________________\n\n[Read Latest Documentation](https://timothycrosley.github.io/isort/) - [Browse GitHub Code Repository](https://github.com/timothycrosley/isort/)\n_________________\n\nisort your imports, so you don\'t have to.\n\nisort is a Python utility / library to sort imports alphabetically, and\nautomatically separated into sections and by type. It provides a command line\nutility, Python library and [plugins for various\neditors](https://github.com/timothycrosley/isort/wiki/isort-Plugins) to\nquickly sort all your imports. It requires Python 3.6+ to run but\nsupports formatting Python 2 code too.\n\n[Try isort now from your browser!](https://timothycrosley.github.io/isort/docs/quick_start/0.-try/)\n\n![Example Usage](https://raw.github.com/timothycrosley/isort/develop/example.gif)\n\nBefore isort:\n\n```python\nfrom my_lib import Object\n\nimport os\n\nfrom my_lib import Object3\n\nfrom my_lib import Object2\n\nimport sys\n\nfrom third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14\n\nimport sys\n\nfrom __future__ import absolute_import\n\nfrom third_party import lib3\n\nprint("Hey")\nprint("yo")\n```\n\nAfter isort:\n\n```python\nfrom __future__ import absolute_import\n\nimport os\nimport sys\n\nfrom third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8,\n lib9, lib10, lib11, lib12, lib13, lib14, lib15)\n\nfrom my_lib import Object, Object2, Object3\n\nprint("Hey")\nprint("yo")\n```\n\n## Installing isort\n\nInstalling isort is as simple as:\n\n```bash\npip install isort\n```\n\nInstall isort with requirements.txt support:\n\n```bash\npip install isort[requirements_deprecated_finder]\n```\n\nInstall isort with Pipfile support:\n\n```bash\npip install isort[pipfile_deprecated_finder]\n```\n\nInstall isort with both formats support:\n\n```bash\npip install isort[requirements_deprecated_finder,pipfile_deprecated_finder]\n```\n\n## Using isort\n\n**From the command line**:\n\n```bash\nisort mypythonfile.py mypythonfile2.py\n```\n\nor recursively:\n\n```bash\nisort .\n```\n\n*which is equivalent to:*\n\n```bash\nisort **/*.py\n```\n\nor to see the proposed changes without applying them:\n\n```bash\nisort mypythonfile.py --diff\n```\n\nFinally, to atomically run isort against a project, only applying\nchanges if they don\'t introduce syntax errors do:\n\n```bash\nisort --atomic .\n```\n\n(Note: this is disabled by default as it keeps isort from being able to\nrun against code written using a different version of Python)\n\n**From within Python**:\n\n```bash\nimport isort\n\nisort.file("pythonfile.py")\n```\n\nor:\n\n```bash\nimport isort\n\nsorted_code = isort.code("import b\\nimport a\\n")\n```\n\n## Installing isort\'s for your preferred text editor\n\nSeveral plugins have been written that enable to use isort from within a\nvariety of text-editors. You can find a full list of them [on the isort\nwiki](https://github.com/timothycrosley/isort/wiki/isort-Plugins).\nAdditionally, I will enthusiastically accept pull requests that include\nplugins for other text editors and add documentation for them as I am\nnotified.\n\n## Multi line output modes\n\nYou will notice above the \\"multi\\_line\\_output\\" setting. This setting\ndefines how from imports wrap when they extend past the line\\_length\nlimit and has 6 possible settings:\n\n**0 - Grid**\n\n```python\nfrom third_party import (lib1, lib2, lib3,\n lib4, lib5, ...)\n```\n\n**1 - Vertical**\n\n```python\nfrom third_party import (lib1,\n lib2,\n lib3\n lib4,\n lib5,\n ...)\n```\n\n**2 - Hanging Indent**\n\n```python\nfrom third_party import \\\n lib1, lib2, lib3, \\\n lib4, lib5, lib6\n```\n\n**3 - Vertical Hanging Indent**\n\n```python\nfrom third_party import (\n lib1,\n lib2,\n lib3,\n lib4,\n)\n```\n\n**4 - Hanging Grid**\n\n```python\nfrom third_party import (\n lib1, lib2, lib3, lib4,\n lib5, ...)\n```\n\n**5 - Hanging Grid Grouped**\n\n```python\nfrom third_party import (\n lib1, lib2, lib3, lib4,\n lib5, ...\n)\n```\n\n**6 - Hanging Grid Grouped, No Trailing Comma**\n\nIn Mode 5 isort leaves a single extra space to maintain consistency of\noutput when a comma is added at the end. Mode 6 is the same - except\nthat no extra space is maintained leading to the possibility of lines\none character longer. You can enforce a trailing comma by using this in\nconjunction with `-tc` or `include_trailing_comma: True`.\n\n```python\nfrom third_party import (\n lib1, lib2, lib3, lib4,\n lib5\n)\n```\n\n**7 - NOQA**\n\n```python\nfrom third_party import lib1, lib2, lib3, ... # NOQA\n```\n\nAlternatively, you can set `force_single_line` to `True` (`-sl` on the\ncommand line) and every import will appear on its own line:\n\n```python\nfrom third_party import lib1\nfrom third_party import lib2\nfrom third_party import lib3\n...\n```\n\n**8 - Vertical Hanging Indent Bracket**\n\nSame as Mode 3 - _Vertical Hanging Indent_ but the closing parentheses\non the last line is indented.\n\n```python\nfrom third_party import (\n lib1,\n lib2,\n lib3,\n lib4,\n )\n```\n\n**9 - Vertical Prefix From Module Import**\n\nStarts a new line with the same `from MODULE import ` prefix when lines are longer than the line length limit.\n\n```python\nfrom third_party import lib1, lib2, lib3\nfrom third_party import lib4, lib5, lib6\n```\n\n**10 - Hanging Indent With Parentheses**\n\nSame as Mode 2 - _Hanging Indent_ but uses parentheses instead of backslash\nfor wrapping long lines.\n\n```python\nfrom third_party import (\n lib1, lib2, lib3,\n lib4, lib5, lib6)\n```\n\nNote: to change the how constant indents appear - simply change the\nindent property with the following accepted formats:\n\n- Number of spaces you would like. For example: 4 would cause standard\n 4 space indentation.\n- Tab\n- A verbatim string with quotes around it.\n\nFor example:\n\n```python\n" "\n```\n\nis equivalent to 4.\n\nFor the import styles that use parentheses, you can control whether or\nnot to include a trailing comma after the last import with the\n`include_trailing_comma` option (defaults to `False`).\n\n## Intelligently Balanced Multi-line Imports\n\nAs of isort 3.1.0 support for balanced multi-line imports has been\nadded. With this enabled isort will dynamically change the import length\nto the one that produces the most balanced grid, while staying below the\nmaximum import length defined.\n\nExample:\n\n```python\nfrom __future__ import (absolute_import, division,\n print_function, unicode_literals)\n```\n\nWill be produced instead of:\n\n```python\nfrom __future__ import (absolute_import, division, print_function,\n unicode_literals)\n```\n\nTo enable this set `balanced_wrapping` to `True` in your config or pass\nthe `-e` option into the command line utility.\n\n## Custom Sections and Ordering\n\nYou can change the section order with `sections` option from the default\nof:\n\n```ini\nFUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER\n```\n\nto your preference:\n\n```ini\nsections=FUTURE,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER\n```\n\nYou also can define your own sections and their order.\n\nExample:\n\n```ini\nknown_django=django\nknown_pandas=pandas,numpy\nsections=FUTURE,STDLIB,DJANGO,THIRDPARTY,PANDAS,FIRSTPARTY,LOCALFOLDER\n```\n\nwould create two new sections with the specified known modules.\n\nThe `no_lines_before` option will prevent the listed sections from being\nsplit from the previous section by an empty line.\n\nExample:\n\n```ini\nsections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER\nno_lines_before=LOCALFOLDER\n```\n\nwould produce a section with both FIRSTPARTY and LOCALFOLDER modules\ncombined.\n\n**IMPORTANT NOTE**: It is very important to know when setting `known` sections that the naming\ndoes not directly map for historical reasons. For custom settings, the only difference is\ncapitalization (`known_custom=custom` VS `sections=CUSTOM,...`) for all others reference the\nfollowing mapping:\n\n - `known_standard_library` : `STANDARD_LIBRARY`\n - `extra_standard_library` : `STANDARD_LIBRARY` # Like known standard library but appends instead of replacing\n - `known_future_library` : `FUTURE`\n - `known_first_party`: `FIRSTPARTY`\n - `known_third_party`: `THIRDPARTY`\n - `known_local_folder`: `LOCALFOLDER`\n\nThis will likely be changed in isort 6.0.0+ in a backwards compatible way.\n\n## Auto-comment import sections\n\nSome projects prefer to have import sections uniquely titled to aid in\nidentifying the sections quickly when visually scanning. isort can\nautomate this as well. To do this simply set the\n`import_heading_{section_name}` setting for each section you wish to\nhave auto commented - to the desired comment.\n\nFor Example:\n\n```ini\nimport_heading_stdlib=Standard Library\nimport_heading_firstparty=My Stuff\n```\n\nWould lead to output looking like the following:\n\n```python\n# Standard Library\nimport os\nimport sys\n\nimport django.settings\n\n# My Stuff\nimport myproject.test\n```\n\n## Ordering by import length\n\nisort also makes it easy to sort your imports by length, simply by\nsetting the `length_sort` option to `True`. This will result in the\nfollowing output style:\n\n```python\nfrom evn.util import (\n Pool,\n Dict,\n Options,\n Constant,\n DecayDict,\n UnexpectedCodePath,\n)\n```\n\nIt is also possible to opt-in to sorting imports by length for only\nspecific sections by using `length_sort_` followed by the section name\nas a configuration item, e.g.:\n\n length_sort_stdlib=1\n\n## Controlling how isort sections `from` imports\n\nBy default isort places straight (`import y`) imports above from imports (`from x import y`):\n\n```python\nimport b\nfrom a import a # This will always appear below because it is a from import.\n```\n\nHowever, if you prefer to keep strict alphabetical sorting you can set [force sort within sections](https://timothycrosley.github.io/isort/docs/configuration/options/#force-sort-within-sections) to true. Resulting in:\n\n\n```python\nfrom a import a # This will now appear at top because a appears in the alphabet before b\nimport b\n```\n\nYou can even tell isort to always place from imports on top, instead of the default of placing them on bottom, using [from first](https://timothycrosley.github.io/isort/docs/configuration/options/#from-first).\n\n```python\nfrom b import b # If from first is set to True, all from imports will be placed before non-from imports.\nimport a\n```\n\n## Skip processing of imports (outside of configuration)\n\nTo make isort ignore a single import simply add a comment at the end of\nthe import line containing the text `isort:skip`:\n\n```python\nimport module # isort:skip\n```\n\nor:\n\n```python\nfrom xyz import (abc, # isort:skip\n yo,\n hey)\n```\n\nTo make isort skip an entire file simply add `isort:skip_file` to the\nmodule\'s doc string:\n\n```python\n""" my_module.py\n Best module ever\n\n isort:skip_file\n"""\n\nimport b\nimport a\n```\n\n## Adding an import to multiple files\n\nisort makes it easy to add an import statement across multiple files,\nwhile being assured it\'s correctly placed.\n\nTo add an import to all files:\n\n```bash\nisort -a "from __future__ import print_function" *.py\n```\n\nTo add an import only to files that already have imports:\n\n```bash\nisort -a "from __future__ import print_function" --append-only *.py\n```\n\n\n## Removing an import from multiple files\n\nisort also makes it easy to remove an import from multiple files,\nwithout having to be concerned with how it was originally formatted.\n\nFrom the command line:\n\n```bash\nisort --rm "os.system" *.py\n```\n\n## Using isort to verify code\n\nThe `--check-only` option\n-------------------------\n\nisort can also be used to used to verify that code is correctly\nformatted by running it with `-c`. Any files that contain incorrectly\nsorted and/or formatted imports will be outputted to `stderr`.\n\n```bash\nisort **/*.py -c -v\n\nSUCCESS: /home/timothy/Projects/Open_Source/isort/isort_kate_plugin.py Everything Looks Good!\nERROR: /home/timothy/Projects/Open_Source/isort/isort/isort.py Imports are incorrectly sorted.\n```\n\nOne great place this can be used is with a pre-commit git hook, such as\nthis one by \\@acdha:\n\n\n\nThis can help to ensure a certain level of code quality throughout a\nproject.\n\nGit hook\n--------\n\nisort provides a hook function that can be integrated into your Git\npre-commit script to check Python code before committing.\n\nTo cause the commit to fail if there are isort errors (strict mode),\ninclude the following in `.git/hooks/pre-commit`:\n\n```python\n#!/usr/bin/env python\nimport sys\nfrom isort.hooks import git_hook\n\nsys.exit(git_hook(strict=True, modify=True, lazy=True))\n```\n\nIf you just want to display warnings, but allow the commit to happen\nanyway, call `git_hook` without the strict parameter. If you want to\ndisplay warnings, but not also fix the code, call `git_hook` without the\nmodify parameter.\nThe `lazy` argument is to support users who are "lazy" to add files\nindividually to the index and tend to use `git commit -a` instead.\nSet it to `True` to ensure all tracked files are properly isorted,\nleave it out or set it to `False` to check only files added to your\nindex.\n\n## Setuptools integration\n\nUpon installation, isort enables a `setuptools` command that checks\nPython files declared by your project.\n\nRunning `python setup.py isort` on the command line will check the files\nlisted in your `py_modules` and `packages`. If any warning is found, the\ncommand will exit with an error code:\n\n```bash\n$ python setup.py isort\n```\n\nAlso, to allow users to be able to use the command without having to\ninstall isort themselves, add isort to the setup\\_requires of your\n`setup()` like so:\n\n```python\nsetup(\n name="project",\n packages=["project"],\n\n setup_requires=[\n "isort"\n ]\n)\n```\n\n## Spread the word\n\n[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://timothycrosley.github.io/isort/)\n\nPlace this badge at the top of your repository to let others know your project uses isort.\n\nFor README.md:\n\n```markdown\n[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://timothycrosley.github.io/isort/)\n```\n\nOr README.rst:\n\n```rst\n.. image:: https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336\n :target: https://timothycrosley.github.io/isort/\n```\n\n## Security contact information\n\nTo report a security vulnerability, please use the [Tidelift security\ncontact](https://tidelift.com/security). Tidelift will coordinate the\nfix and disclosure.\n\n## Why isort?\n\nisort simply stands for import sort. It was originally called\n"sortImports" however I got tired of typing the extra characters and\ncame to the realization camelCase is not pythonic.\n\nI wrote isort because in an organization I used to work in the manager\ncame in one day and decided all code must have alphabetically sorted\nimports. The code base was huge - and he meant for us to do it by hand.\nHowever, being a programmer - I\\\'m too lazy to spend 8 hours mindlessly\nperforming a function, but not too lazy to spend 16 hours automating it.\nI was given permission to open source sortImports and here we are :)\n\n------------------------------------------------------------------------\n\n[Get professionally supported isort with the Tidelift\nSubscription](https://tidelift.com/subscription/pkg/pypi-isort?utm_source=pypi-isort&utm_medium=referral&utm_campaign=readme)\n\nProfessional support for isort is available as part of the [Tidelift\nSubscription](https://tidelift.com/subscription/pkg/pypi-isort?utm_source=pypi-isort&utm_medium=referral&utm_campaign=readme).\nTidelift gives software development teams a single source for purchasing\nand maintaining their software, with professional grade assurances from\nthe experts who know it best, while seamlessly integrating with existing\ntools.\n\n------------------------------------------------------------------------\n\nThanks and I hope you find isort useful!\n\n~Timothy Crosley\n', 'author': 'Timothy Crosley', 'author_email': 'timothy.crosley@gmail.com', 'maintainer': None, diff --git a/tests/test_isort.py b/tests/test_isort.py index 0f7d713..36260d0 100644 --- a/tests/test_isort.py +++ b/tests/test_isort.py @@ -30,6 +30,8 @@ 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 @@ -52,8 +54,11 @@ 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 @@ -605,11 +610,33 @@ ) +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" @@ -619,6 +646,7 @@ assert test_output == ( "import os\n" "import sys\n" + "import datetime\n" "\n" "import looooooooooooooooooooooooooooooooooooooong\n" "import medium_sizeeeeeeeeeeeeea\n" @@ -1044,27 +1072,33 @@ assert test_output == "import os\nimport sys\n\nimport profile.test\n" -def test_known_pattern_path_expansion() -> None: +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" - "import isort.settings\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=["./", "this", "kate_plugin", "isort"], + known_first_party=["src/", "this", "kate_plugin"], + directory=str(tmpdir), ) test_output_old_finder = isort.code( code=test_input, default_section="FIRSTPARTY", old_finders=True, - known_first_party=["./", "this", "kate_plugin", "isort"], + known_first_party=["src/", "this", "kate_plugin"], + directory=str(tmpdir), ) assert ( test_output_old_finder @@ -1073,8 +1107,9 @@ "import os\n" "import sys\n" "\n" - "import isort.settings\n" + "import bar\n" "import this\n" + "from foo import settings\n" "from kate_plugin import isort_plugin\n" ) ) diff --git a/tests/test_main.py b/tests/test_main.py index d0bfad5..0d27387 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -39,6 +39,8 @@ 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} def test_ascii_art(capsys): diff --git a/tests/test_regressions.py b/tests/test_regressions.py index 5d1d041..6fae92d 100644 --- a/tests/test_regressions.py +++ b/tests/test_regressions.py @@ -492,3 +492,46 @@ 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/timothycrosley/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/timothycrosley/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)