Skip to content

Commit

Permalink
Fix #5197.
Browse files Browse the repository at this point in the history
  • Loading branch information
tjprescott committed Mar 7, 2023
1 parent 2439409 commit 1d332b2
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 9 deletions.
1 change: 1 addition & 0 deletions packages/python-packages/api-stub-generator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Fix incorrect type annotation.
Update to follow best practices for accessing '__annotations__'.
Fixed issue where class decorators were not displayed.
Fixed issue where ivars appeared as cvars.

## Version 0.3.6 (2022-10-27)
Suppressed unwanted base class methods in DPG libraries.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ def _should_include_function(self, func_obj):
return function_module and function_module.startswith(self.pkg_root_namespace) and not function_module.endswith("_model_base")
return False

def _handle_class_variable(self, child_obj, name, *, type_string=None, value=None):
# Add any public class level variables
def _handle_variable(self, child_obj, name, *, type_string=None, value=None):
allowed_types = (str, int, dict, list, float, bool)
if not isinstance(child_obj, allowed_types):
return
Expand All @@ -124,14 +123,17 @@ def _handle_class_variable(self, child_obj, name, *, type_string=None, value=Non
if type_string:
var_match[0].type = type_string
else:
is_ivar = True
if type_string:
is_ivar = not type_string.startswith("ClassVar")
self.child_nodes.append(
VariableNode(
namespace=self.namespace,
parent_node=self,
name=name,
type_name=type_string,
value=value,
is_ivar=False
is_ivar=is_ivar
)
)

Expand Down Expand Up @@ -222,7 +224,7 @@ def _inspect(self):
)
else:
type_string = get_qualified_name(item_type, self.namespace)
self._handle_class_variable(child_obj, item_name, type_string=type_string)
self._handle_variable(child_obj, item_name, type_string=type_string)

# now that we've looked at the specific dunder properties we are
# willing to include, anything with a leading underscore should be ignored.
Expand Down Expand Up @@ -253,7 +255,7 @@ def _inspect(self):
# Add instance properties
self.child_nodes.append(PropertyNode(self.namespace, self, name, child_obj))
else:
self._handle_class_variable(child_obj, name, value=str(child_obj))
self._handle_variable(child_obj, name, value=str(child_obj))

def _parse_ivars(self):
# This method will add instance variables by parsing docstring
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
AliasNewType,
AliasUnion,
ClassWithDecorators,
ClassWithIvarsAndCvars,
FakeTypedDict,
FakeObject,
GenericStack,
Expand Down Expand Up @@ -40,6 +41,18 @@ class TestClassParsing:

pkg_namespace = "apistubgentest.models"

def test_class_with_ivars_and_cvars(self):
obj = ClassWithIvarsAndCvars
class_node = ClassNode(name=obj.__name__, namespace=obj.__name__, parent_node=None, obj=obj, pkg_root_namespace=self.pkg_namespace)
actuals = _render_lines(_tokenize(class_node))
expected = [
"class ClassWithIvarsAndCvars:",
'ivar captain: str = "Picard"',
"ivar damage: int",
"cvar stats: ClassVar[Dict[str, int]] = {}"
]
_check_all(actuals, expected, obj)

def test_class_with_decorators(self):
obj = ClassWithDecorators
class_node = ClassNode(name=obj.__name__, namespace=obj.__name__, parent_node=None, obj=obj, pkg_root_namespace=self.pkg_namespace)
Expand Down Expand Up @@ -75,7 +88,7 @@ def test_object(self):
actuals = _render_lines(_tokenize(class_node))
expected = [
"class FakeObject:",
'cvar PUBLIC_CONST: str = "SOMETHING"',
'ivar PUBLIC_CONST: str = "SOMETHING"',
'ivar age: int',
'ivar name: str',
'ivar union: Union[bool, PetEnumPy3MetaclassAlt]'
Expand All @@ -90,8 +103,8 @@ def test_public_private(self):
actuals = _render_lines(_tokenize(class_node))
expected = [
"class PublicPrivateClass:",
"cvar public_dict: dict = {'a': 'b'}",
'cvar public_var: str = "SOMEVAL"',
"ivar public_dict: dict = {'a': 'b'}",
'ivar public_var: str = "SOMEVAL"',
]
_check_all(actuals, expected, obj)
assert actuals[4].lstrip() == "def __init__(self)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
AliasNewType,
AliasUnion,
ClassWithDecorators,
ClassWithIvarsAndCvars,
DocstringClass,
FakeError,
FakeObject,
Expand Down Expand Up @@ -41,6 +42,7 @@
"AliasNewType",
"AliasUnion",
"ClassWithDecorators",
"ClassWithIvarsAndCvars",
"DataClassSimple",
"DataClassWithFields",
"DataClassDynamic",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from collections.abc import Sequence
from enum import Enum, EnumMeta
import functools
from typing import Any, overload, Dict, TypedDict, Union, Optional, Generic, TypeVar, NewType, TypeAlias
from typing import Any, overload, Dict, TypedDict, Union, Optional, Generic, TypeVar, NewType, ClassVar

from ._mixin import MixinWithOverloads

Expand Down Expand Up @@ -45,10 +45,18 @@ def __init__(self, id, *args, **kwargs):
cls.__init__ = __init__
return cls


@add_id
class ClassWithDecorators:
pass


class ClassWithIvarsAndCvars:
captain: str = "Picard" # instance var w/ default
damage: int # instance var w/out default
stats: ClassVar[Dict[str, int]] = {} # class var


class PublicCaseInsensitiveEnumMeta(EnumMeta):
def __getitem__(self, name: str):
pass
Expand Down

0 comments on commit 1d332b2

Please sign in to comment.