Repository: timothycrosley/isort
Pull request #326 (2015-09-04 09:19:55)

Adding NOQA Option
This PR adds the option to leave long import lines long and to add a NOQA option.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7b8b3b97..4c6c8a4c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,3 +25,6 @@ Changelog
 
 ### 4.1.2
 - Fixed issue #323: Accidental default configuration change introduced
+
+### 4.2.0
+- Added option "NOQA" Do not wrap lines, but add a noqa statement at the end
diff --git a/README.rst b/README.rst
index e001f24f..f120339c 100644
--- a/README.rst
+++ b/README.rst
@@ -310,6 +310,12 @@ past the line_length limit and has 6 possible settings:
         lib5, ...
     )
 
+**6 - NOQA**
+
+.. code-block:: 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:
 
diff --git a/isort/isort.py b/isort/isort.py
index 076ace34..9a509534 100644
--- a/isort/isort.py
+++ b/isort/isort.py
@@ -304,7 +304,8 @@ class SortImports(object):
         """
             Returns an import wrapped to the specified line-length, if possible.
         """
-        if len(line) > self.config['line_length']:
+        wrap_mode = self.config.get('multi_line_output', 0)
+        if len(line) > self.config['line_length'] and wrap_mode != settings.WrapModes.NOQA:
             for splitter in ("import", "."):
                 exp = r"\b" + re.escape(splitter) + r"\b"
                 if re.search(exp, line) and not line.strip().startswith(splitter):
@@ -320,6 +321,9 @@ class SortImports(object):
                     if self.config['use_parentheses']:
                         return "{0}{1} (\n{2})".format(line, splitter, cont_line)
                     return "{0}{1} \\\n{2}".format(line, splitter, cont_line)
+        elif len(line) > self.config['line_length'] and wrap_mode == settings.WrapModes.NOQA:
+            if "# NOQA" not in line:
+                return "{0}  # NOQA".format(line)
 
         return line
 
@@ -611,6 +615,23 @@ class SortImports(object):
     def _output_vertical_grid_grouped(self, statement, imports, white_space, indent, line_length, comments):
         return self._output_vertical_grid_common(statement, imports, white_space, indent, line_length, comments) + "\n)"
 
+    def _output_noqa(self, statement, imports, white_space, indent, line_length, comments):
+        retval = '{0}{1}'.format(statement, ' '.join(imports))
+        comment_str = ' '.join(comments)
+        if comments:
+            if len(retval) + 4 + len(comment_str) <= line_length:
+                return '{0}  # {1}'.format(retval, comment_str)
+        else:
+            if len(retval) <= line_length:
+                return retval
+        if comments:
+            if "NOQA" in comments:
+                return '{0}  # {1}'.format(retval, comment_str)
+            else:
+                return '{0}  # NOQA {1}'.format(retval, comment_str)
+        else:
+            return '{0}  # NOQA'.format(retval)
+
     @staticmethod
     def _strip_comments(line, comments=None):
         """Removes comments from import line."""
diff --git a/isort/settings.py b/isort/settings.py
index ca2168e4..f1fd1eb6 100644
--- a/isort/settings.py
+++ b/isort/settings.py
@@ -37,7 +37,7 @@ except ImportError:
 MAX_CONFIG_SEARCH_DEPTH = 25 # The number of parent directories isort will look for a config file within
 DEFAULT_SECTIONS = ("FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER")
 
-WrapModes = ('GRID', 'VERTICAL', 'HANGING_INDENT', 'VERTICAL_HANGING_INDENT', 'VERTICAL_GRID', 'VERTICAL_GRID_GROUPED')
+WrapModes = ('GRID', 'VERTICAL', 'HANGING_INDENT', 'VERTICAL_HANGING_INDENT', 'VERTICAL_GRID', 'VERTICAL_GRID_GROUPED', 'NOQA')
 WrapModes = namedtuple('WrapModes', WrapModes)(*range(len(WrapModes)))
 
 # Note that none of these lists must be complete as they are simply fallbacks for when included auto-detection fails.


Test patch:
diff --git a/test_isort.py b/test_isort.py
index cd9ea2ef..49ee5ff4 100644
--- a/test_isort.py
+++ b/test_isort.py
@@ -346,6 +346,19 @@ def test_output_modes():
                                                     "    lib17, lib18, lib20, lib21, lib22\n"
                                                     ")\n")
 
+    output_noqa = SortImports(file_contents=REALLY_LONG_IMPORT_WITH_COMMENT,
+                              multi_line_output=WrapModes.NOQA).output
+    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"  # NOQA
+
+
+def test_qa_comment_case():
+    test_input = "from veryveryveryveryveryveryveryveryveryveryvery import X  # NOQA"
+    test_output = SortImports(file_contents=test_input, line_length=40, multi_line_output=WrapModes.NOQA).output
+    assert test_output == "from veryveryveryveryveryveryveryveryveryveryvery import X  # NOQA\n"
+
+    test_input = "import veryveryveryveryveryveryveryveryveryveryvery  # NOQA"
+    test_output = SortImports(file_contents=test_input, line_length=40, multi_line_output=WrapModes.NOQA).output
+    assert test_output == "import veryveryveryveryveryveryveryveryveryveryvery  # NOQA\n"
 
 def test_length_sort():
     """Test setting isort to sort on length instead of alphabetically."""
@@ -736,6 +749,13 @@ def test_force_single_line_imports():
                            "from third_party import lib22\n")
 
 
+def test_force_single_line_long_imports():
+    test_input = ("from veryveryveryveryveryvery import small, big\n")
+    test_output = SortImports(file_contents=test_input, multi_line_output=WrapModes.NOQA,
+                              line_length=40, force_single_line=True).output
+    assert test_output == ("from veryveryveryveryveryvery import big\n"
+                           "from veryveryveryveryveryvery import small  # NOQA\n")
+
 def test_titled_imports():
     """Tests setting custom titled/commented import sections."""
     test_input = ("import sys\n"