Repository: hhatto/autopep8

Issue:
Imports are always moved to top of the file
Running autopep8 on 
```py
"""Module docstring"""
a = 5
import random
import itertools
```
results in 
```py
import itertools
import random
"""module docstring"""
a = 5
```
Instead of 
```py
"""module docstring"""
import itertools
import random
a = 5
```

Discussion:

Patch:
diff --git a/autopep8.py b/autopep8.py
index 8fc548a..e8c1b9e 100755
--- a/autopep8.py
+++ b/autopep8.py
@@ -82,6 +82,7 @@ COMPARE_NEGATIVE_REGEX = re.compile(r'\b(not)\s+([^][)(}{]+?)\s+(in|is)\s')
 COMPARE_NEGATIVE_REGEX_THROUGH = re.compile(r'\b(not\s+in|is\s+not)\s')
 BARE_EXCEPT_REGEX = re.compile(r'except\s*:')
 STARTSWITH_DEF_REGEX = re.compile(r'^(async\s+def|def)\s.*\):')
+DOCSTRING_START_REGEX = re.compile(r'^u?r?(?P<kind>["\']{3})')
 
 EXIT_CODE_OK = 0
 EXIT_CODE_ERROR = 1
@@ -1412,11 +1413,28 @@ def get_module_imports_on_top_of_file(source, import_line_index):
         return False
 
     allowed_try_keywords = ('try', 'except', 'else', 'finally')
+    in_docstring = False
+    docstring_kind = '"""'
     for cnt, line in enumerate(source):
+        if not in_docstring:
+            m = DOCSTRING_START_REGEX.match(line.lstrip())
+            if m is not None:
+                in_docstring = True
+                docstring_kind = m.group('kind')
+                remain = line[m.end(): m.endpos].rstrip()
+                if remain[-3:] == docstring_kind:  # one line doc
+                    in_docstring = False
+                continue
+        if in_docstring:
+            if line.rstrip()[-3:] == docstring_kind:
+                in_docstring = False
+            continue
+
         if not line.rstrip():
             continue
         elif line.startswith('#'):
             continue
+
         if line.startswith('import ') or line.startswith('from '):
             if cnt == import_line_index or is_future_import(line):
                 continue


Test patch:
diff --git a/test/test_autopep8.py b/test/test_autopep8.py
index 14981e8..b2ace52 100755
--- a/test/test_autopep8.py
+++ b/test/test_autopep8.py
@@ -2422,6 +2422,20 @@ a = 1     # (E305)
         with autopep8_context(line) as result:
             self.assertEqual(fixed, result)
 
+    def test_e402_with_module_doc(self):
+        line1 = '"""\nmodule doc\n"""\na = 1\nimport os\n'
+        fixed1 = '"""\nmodule doc\n"""\nimport os\na = 1\n'
+        line2 = '# comment\nr"""\nmodule doc\n"""\na = 1\nimport os\n'
+        fixed2 = '# comment\nr"""\nmodule doc\n"""\nimport os\na = 1\n'
+        line3 = "u'''one line module doc'''\na = 1\nimport os\n"
+        fixed3 = "u'''one line module doc'''\nimport os\na = 1\n"
+        line4 = "'''\n\"\"\"\ndoc'''\na = 1\nimport os\n"
+        fixed4 = "'''\n\"\"\"\ndoc'''\nimport os\na = 1\n"
+        for line, fixed in [(line1, fixed1), (line2, fixed2),
+                            (line3, fixed3), (line4, fixed4)]:
+            with autopep8_context(line) as result:
+                self.assertEqual(fixed, result)
+
     def test_e402_import_some_modules(self):
         line = """\
 a = 1
@@ -4882,7 +4896,7 @@ if True:
         target_line_index = 11
         result = get_module_imports_on_top_of_file(line.splitlines(),
                                                    target_line_index)
-        self.assertEqual(result, 5)
+        self.assertEqual(result, 10)
 
 
 class CommandLineTests(unittest.TestCase):