Skip to content

Commit

Permalink
feat: Allow method_descriptors to be serialized as methods
Browse files Browse the repository at this point in the history
  • Loading branch information
Joel Gibson committed Apr 26, 2021
1 parent c4b58a6 commit e52eb0b
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 2 deletions.
30 changes: 28 additions & 2 deletions src/pytkdocs/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,18 @@ def is_method(self) -> bool:
function_type = type(lambda: None)
return self.parent_is_class() and isinstance(self.obj, function_type)

def is_method_descriptor(self) -> bool:
"""
Tell if this node's object is a method descriptor.
Built-in methods (e.g. those implemented in C/Rust) are often
method descriptors, rather than normal methods.
Returns:
If this node's object is a method descriptor.
"""
return inspect.ismethoddescriptor(self.obj)

def is_staticmethod(self) -> bool:
"""
Tell if this node's object is a staticmethod.
Expand Down Expand Up @@ -360,6 +372,8 @@ def get_object_documentation(self, dotted_path: str, members: Optional[Union[Set
root_object = self.get_staticmethod_documentation(leaf)
elif leaf.is_classmethod():
root_object = self.get_classmethod_documentation(leaf)
elif leaf.is_method_descriptor():
root_object = self.get_regular_method_documentation(leaf)
elif leaf.is_method():
root_object = self.get_regular_method_documentation(leaf)
elif leaf.is_function():
Expand Down Expand Up @@ -792,7 +806,7 @@ def get_regular_method_documentation(self, node: ObjectNode) -> Method:

def get_method_documentation(self, node: ObjectNode, properties: Optional[List[str]] = None) -> Method:
"""
Get the documentation for a method.
Get the documentation for a method or method descriptor.
Arguments:
node: The node representing the method and its parents.
Expand All @@ -803,6 +817,7 @@ def get_method_documentation(self, node: ObjectNode, properties: Optional[List[s
"""
method = node.obj
path = node.dotted_path
signature: Optional[inspect.Signature]
source: Optional[Source]

try:
Expand All @@ -819,12 +834,23 @@ def get_method_documentation(self, node: ObjectNode, properties: Optional[List[s
else:
properties.append("async")

try:
# for "built-in" functions, e.g. those implemented in C,
# inspect.signature() uses the __text_signature__ attribute, which
# provides a limited but still useful amount of signature information.
# "built-in" functions with no __text_signature__ will
# raise a ValueError().
signature = inspect.signature(method)
except ValueError as error:
self.errors.append(f"Couldn't read signature for '{path}': {error}")
signature = None

return Method(
name=node.name,
path=path,
file_path=node.file_path,
docstring=inspect.getdoc(method),
signature=inspect.signature(method),
signature=signature,
properties=properties or [],
source=source,
)
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/method_descriptor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""See https://docs.python.org/3/library/inspect.html#inspect.ismethoddescriptor for details"""

descriptor = int.__add__
11 changes: 11 additions & 0 deletions tests/test_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,3 +511,14 @@ def test_loading_cached_properties():
assert len(obj.children) == 1
assert obj.children[0].name == obj.children[0].docstring == "aaa"
assert "cached" in obj.children[0].properties


def test_method_descriptor():
"""Load a method descriptor."""
loader = Loader(new_path_syntax=True)
obj = loader.get_object_documentation("tests.fixtures.method_descriptor:descriptor")
assert obj.name == "descriptor"
assert obj.signature
assert len(obj.signature.parameters) == 2
assert obj.docstring
assert obj.category == "method"

0 comments on commit e52eb0b

Please sign in to comment.