Repository: palantir/python-language-server (python)

Issue:
Formatting hanging indents raises ValueError
Example file:

```python
def foo():
    print('asdf',
    file=None
    )
```

Formatting this raises:
```
Traceback (most recent call last):
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pyls_jsonrpc/endpoint.py", line 113, in consume
    self._handle_request(message['id'], message['method'], message.get('params'))
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pyls_jsonrpc/endpoint.py", line 182, in _handle_request
    handler_result = handler(params)
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pyls_jsonrpc/dispatchers.py", line 23, in handler
    return method(**(params or {}))
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pyls/python_ls.py", line 347, in m_text_document__range_formatting
    return self.format_range(textDocument['uri'], range)
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pyls/python_ls.py", line 258, in format_range
    return self._hook('pyls_format_range', doc_uri, range=range)
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pyls/python_ls.py", line 155, in _hook
    return hook_handlers(config=self.config, workspace=workspace, document=doc, **kwargs)
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pluggy/hooks.py", line 286, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pluggy/manager.py", line 93, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pluggy/manager.py", line 337, in traced_hookexec
    return outcome.get_result()
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pluggy/callers.py", line 52, in from_call
    result = func()
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pluggy/manager.py", line 335, in <lambda>
    outcome = _Result.from_call(lambda: oldcall(hook, hook_impls, kwargs))
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pluggy/manager.py", line 87, in <lambda>
    firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pluggy/callers.py", line 208, in _multicall
    return outcome.get_result()
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pluggy/callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pyls/plugins/autopep8_format.py", line 26, in pyls_format_range
    return _format(config, document, line_range=line_range)
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/pyls/plugins/autopep8_format.py", line 34, in _format
    new_source = fix_code(document.source, options=options)
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/autopep8.py", line 3414, in fix_code
    return fix_lines(sio.readlines(), options=options)
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/autopep8.py", line 3489, in fix_lines
    fixed_source = fix.fix()
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/autopep8.py", line 600, in fix
    aggressive=self.options.aggressive))
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/autopep8.py", line 542, in _fix_source
    modified_lines = fix(result)
  File "/home/kb/apps/pls-venv/lib/python3.7/site-packages/autopep8.py", line 616, in _fix_reindent
    num_indent_spaces = int(result['info'].split()[1])
ValueError: invalid literal for int() with base 10: 'continuation'
```

Caused by palantir/python-language-server#719 replacing the autopep8 checker for `pycodestyle.continued_indentation`

Pull request:
Fix ValueError when formatting if continuation lines are incorrectly indented when using autopep8
This patch temporarily re-monkey-patches the _checks in pycodestyle to use autopep8's checker, and then patches things back when done. This should fix #771 

NB: the patching shouldn't need to be conditional (unlike in pycodestyle_lint.py) since autopep8 always requires pycodestyle to work.

Patch:
diff --git a/pyls/plugins/autopep8_format.py b/pyls/plugins/autopep8_format.py
index 1e4e4e1..ff29a11 100644
--- a/pyls/plugins/autopep8_format.py
+++ b/pyls/plugins/autopep8_format.py
@@ -1,6 +1,7 @@
 # Copyright 2018 Palantir Technologies, Inc.
 import logging
-from autopep8 import fix_code
+import pycodestyle
+from autopep8 import fix_code, continued_indentation as autopep8_c_i
 from pyls import hookimpl
 
 log = logging.getLogger(__name__)
@@ -31,8 +32,16 @@ def _format(config, document, line_range=None):
     if line_range:
         options['line_range'] = list(line_range)
 
+    # Temporarily re-monkey-patch the continued_indentation checker - #771
+    del pycodestyle._checks['logical_line'][pycodestyle.continued_indentation]
+    pycodestyle.register_check(autopep8_c_i)
+
     new_source = fix_code(document.source, options=options)
 
+    # Switch it back
+    del pycodestyle._checks['logical_line'][autopep8_c_i]
+    pycodestyle.register_check(pycodestyle.continued_indentation)
+
     if new_source == document.source:
         return []
 


Test patch:
diff --git a/test/plugins/test_autopep8_format.py b/test/plugins/test_autopep8_format.py
index 8138952..d23a1c4 100644
--- a/test/plugins/test_autopep8_format.py
+++ b/test/plugins/test_autopep8_format.py
@@ -15,6 +15,25 @@ def func():
 
 GOOD_DOC = """A = ['hello', 'world']\n"""
 
+INDENTED_DOC = """def foo():
+    print('asdf',
+    file=None
+    )
+
+bar = { 'foo': foo
+}
+"""
+
+CORRECT_INDENTED_DOC = """def foo():
+    print('asdf',
+          file=None
+          )
+
+
+bar = {'foo': foo
+       }
+"""
+
 
 def test_format(config, workspace):
     doc = Document(DOC_URI, workspace, DOC)
@@ -42,3 +61,11 @@ def test_range_format(config, workspace):
 def test_no_change(config, workspace):
     doc = Document(DOC_URI, workspace, GOOD_DOC)
     assert not pyls_format_document(config, doc)
+
+
+def test_hanging_indentation(config, workspace):
+    doc = Document(DOC_URI, workspace, INDENTED_DOC)
+    res = pyls_format_document(config, doc)
+
+    assert len(res) == 1
+    assert res[0]['newText'] == CORRECT_INDENTED_DOC