Repository: dropbox/stone

Issue:
Properties are not always optional
Hey Stone team! We found an issue with the typing stub code generated for properties. I believe this block of code should be deleted:
https://github.com/dropbox/stone/blob/e695746e0836dd54d942d6b8a9271d2d3fd8a429/stone/backends/python_type_stubs.py#L369-L371

The effect of this block is to always add `Optional[]` around the return type of a property getter. (The logic looks backwards, but for nullable and void types there *already* is an `Optional[]`.)

But if you look at the runtime code generated, you'll see that for a non-nullable default-less property, `None` is never returned, it raises an exception if the value isn't set:
https://github.com/dropbox/stone/blob/e695746e0836dd54d942d6b8a9271d2d3fd8a429/stone/backends/python_types.py#L625-L634

I will propose a PR to fix this. I have tested the PR in the Dropbox server repo, and it caused only two errors, and those look genuine.

CC: @ilevkivskyi 

Discussion:

Patch:
diff --git a/stone/backends/python_type_stubs.py b/stone/backends/python_type_stubs.py
index f29058b..6f38967 100644
--- a/stone/backends/python_type_stubs.py
+++ b/stone/backends/python_type_stubs.py
@@ -363,12 +363,7 @@ class PythonTypeStubsBackend(CodeBackend):
         for field in struct.all_fields:
             field_name_reserved_check = fmt_func(field.name, check_reserved=True)
             setter_field_type = self.map_stone_type_to_pep484_type(ns, field.data_type)
-
-            # The field might not have been set since it is optional in the constructor.
             getter_field_type = setter_field_type
-            if not is_nullable_type(field.data_type) and not is_void_type(field.data_type):
-                self.import_tracker._register_typing_import('Optional')
-                getter_field_type = 'Optional[{}]'.format(setter_field_type)
 
             to_emit.extend(
                 self.property_template.format(


Test patch:
diff --git a/test/test_python_client.py b/test/test_python_client.py
index a706112..41a020b 100644
--- a/test/test_python_client.py
+++ b/test/test_python_client.py
@@ -162,7 +162,7 @@ class TestGeneratedPythonClient(unittest.TestCase):
         self.assertEqual(result, expected)
 
     def test_route_with_auth_mode3(self):
-            # type: () -> None
+        # type: () -> None
 
         route1 = ApiRoute('get_metadata', 1, None)
         route1.set_attributes(None, ':route:`get_metadata:2`', Void(), Void(), Void(),
diff --git a/test/test_python_type_stubs.py b/test/test_python_type_stubs.py
index 82d5af7..57525e6 100644
--- a/test/test_python_type_stubs.py
+++ b/test/test_python_type_stubs.py
@@ -258,7 +258,7 @@ class TestPythonTypeStubs(unittest.TestCase):
                              f1: Optional[bool] = ...) -> None: ...
 
                 @property
-                def f1(self) -> Optional[bool]: ...
+                def f1(self) -> bool: ...
 
                 @f1.setter
                 def f1(self, val: bool) -> None: ...
@@ -281,7 +281,7 @@ class TestPythonTypeStubs(unittest.TestCase):
                              f4: Optional[Dict[Text, int]] = ...) -> None: ...
 
                 @property
-                def f2(self) -> Optional[List[int]]: ...
+                def f2(self) -> List[int]: ...
 
                 @f2.setter
                 def f2(self, val: List[int]) -> None: ...
@@ -291,7 +291,7 @@ class TestPythonTypeStubs(unittest.TestCase):
 
 
                 @property
-                def f3(self) -> Optional[datetime.datetime]: ...
+                def f3(self) -> datetime.datetime: ...
 
                 @f3.setter
                 def f3(self, val: datetime.datetime) -> None: ...
@@ -301,7 +301,7 @@ class TestPythonTypeStubs(unittest.TestCase):
 
 
                 @property
-                def f4(self) -> Optional[Dict[Text, int]]: ...
+                def f4(self) -> Dict[Text, int]: ...
 
                 @f4.setter
                 def f4(self, val: Dict[Text, int]) -> None: ...
@@ -345,7 +345,7 @@ class TestPythonTypeStubs(unittest.TestCase):
                              nullable_list: Optional[List[int]] = ...) -> None: ...
 
                 @property
-                def list_of_nullables(self) -> Optional[List[Optional[int]]]: ...
+                def list_of_nullables(self) -> List[Optional[int]]: ...
 
                 @list_of_nullables.setter
                 def list_of_nullables(self, val: List[Optional[int]]) -> None: ...
@@ -498,7 +498,7 @@ class TestPythonTypeStubs(unittest.TestCase):
                              f1: Optional[bool] = ...) -> None: ...
 
                 @property
-                def f1(self) -> Optional[bool]: ...
+                def f1(self) -> bool: ...
 
                 @f1.setter
                 def f1(self, val: bool) -> None: ...