Repository: Instagram/MonkeyType

Issue:
Stubs with positional-only args output too many slashes and cause a SyntaxError
`mod.py`:
```py
def add(a, b, /, c):
    return 0
```
`main.py`:
```py
from mod import add
add(1, 2, c=3)
```
```sh
$ monkeytype run ./main.py

$ monkeytype stub mod
def add(a: int, /, b: int, /, /, c: int) -> int: ...

$ monkeytype apply mod
ERROR: Failed applying stub with libcst:
Syntax Error @ 1:1.
parser error: error at 1:28: expected one of ), *, **, NAME

def add(a: int, /, b: int, /, /, c: int) -> int: ...
^
```

Discussion:

Patch:
diff --git a/monkeytype/stubs.py b/monkeytype/stubs.py
index 2723fb5..6523797 100644
--- a/monkeytype/stubs.py
+++ b/monkeytype/stubs.py
@@ -452,10 +452,10 @@ def render_signature(
 
         formatted_params.append(formatted)
 
-        if render_pos_only_separator:
-            # There were only positional-only parameters, hence the
-            # flag was not reset to 'False'
-            formatted_params.append("/")
+    if render_pos_only_separator:
+        # There were only positional-only parameters, hence the
+        # flag was not reset to 'False'
+        formatted_params.append("/")
 
     rendered_return = ""
     if sig.return_annotation is not inspect.Signature.empty:


Test patch:
diff --git a/tests/test_stubs.py b/tests/test_stubs.py
index 5e51734..e7fa80e 100644
--- a/tests/test_stubs.py
+++ b/tests/test_stubs.py
@@ -560,7 +560,8 @@ class TestModuleStub:
     def test_render(self):
         cm_stub = _func_stub_from_callable(Dummy.a_class_method)
         im_stub = _func_stub_from_callable(Dummy.an_instance_method)
-        func_stubs = (cm_stub, im_stub)
+        sig_stub = _func_stub_from_callable(Dummy.has_complex_signature)
+        func_stubs = (cm_stub, im_stub, sig_stub)
         test_stub = ClassStub('Test', function_stubs=func_stubs)
         test2_stub = ClassStub('Test2', function_stubs=func_stubs)
         other_class_stubs = module_stub_for_method_with_typed_dict['tests.util'].class_stubs.values()
@@ -586,6 +587,20 @@ class TestModuleStub:
             'def an_instance_method(self, foo: Any, bar: Any) -> Optional[frame]: ...',
             '',
             '',
+            'def has_complex_signature(',
+            '    self,',
+            '    a: Any,',
+            '    b: Any,',
+            '    /,',
+            '    c: Any,',
+            '    d: Any = ...,',
+            '    *e: Any,',
+            '    f: Any,',
+            '    g: Any = ...,',
+            '    **h: Any',
+            ') -> Optional[frame]: ...',
+            '',
+            '',
             'class Dummy:',
             '    def an_instance_method(',
             '        self,',
@@ -598,12 +613,36 @@ class TestModuleStub:
             '    @classmethod',
             '    def a_class_method(foo: Any) -> Optional[frame]: ...',
             '    def an_instance_method(self, foo: Any, bar: Any) -> Optional[frame]: ...',
+            '    def has_complex_signature(',
+            '        self,',
+            '        a: Any,',
+            '        b: Any,',
+            '        /,',
+            '        c: Any,',
+            '        d: Any = ...,',
+            '        *e: Any,',
+            '        f: Any,',
+            '        g: Any = ...,',
+            '        **h: Any',
+            '    ) -> Optional[frame]: ...',
             '',
             '',
             'class Test2:',
             '    @classmethod',
             '    def a_class_method(foo: Any) -> Optional[frame]: ...',
             '    def an_instance_method(self, foo: Any, bar: Any) -> Optional[frame]: ...',
+            '    def has_complex_signature(',
+            '        self,',
+            '        a: Any,',
+            '        b: Any,',
+            '        /,',
+            '        c: Any,',
+            '        d: Any = ...,',
+            '        *e: Any,',
+            '        f: Any,',
+            '        g: Any = ...,',
+            '        **h: Any',
+            '    ) -> Optional[frame]: ...',
         ])
         assert mod_stub.render() == expected
 
diff --git a/tests/util.py b/tests/util.py
index 8ea223b..7cca51c 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -27,6 +27,20 @@ class Dummy:
     def an_instance_method(self, foo: Any, bar: Any) -> Optional[FrameType]:
         return inspect.currentframe()
 
+    def has_complex_signature(
+        self,
+        a: Any,
+        b: Any,
+        /,
+        c: Any,
+        d: Any = 0,
+        *e: Any,
+        f: Any,
+        g: Any = 0,
+        **h: Any,
+    ) -> Optional[FrameType]:
+        return inspect.currentframe()
+
     @property
     def a_property(self) -> Optional[FrameType]:
         return inspect.currentframe()