Skip to content

Commit 8e1b1b2

Browse files
jmrgibsonJoel Gibson
andauthored
feat: Allow method descriptors to be serialized as methods
Co-authored-by: Joel Gibson <[email protected]> PR #103: #103
1 parent 6416a05 commit 8e1b1b2

File tree

3 files changed

+42
-2
lines changed

3 files changed

+42
-2
lines changed

src/pytkdocs/loader.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,18 @@ def is_method(self) -> bool:
174174
function_type = type(lambda: None)
175175
return self.parent_is_class() and isinstance(self.obj, function_type)
176176

177+
def is_method_descriptor(self) -> bool:
178+
"""
179+
Tell if this node's object is a method descriptor.
180+
181+
Built-in methods (e.g. those implemented in C/Rust) are often
182+
method descriptors, rather than normal methods.
183+
184+
Returns:
185+
If this node's object is a method descriptor.
186+
"""
187+
return inspect.ismethoddescriptor(self.obj)
188+
177189
def is_staticmethod(self) -> bool:
178190
"""
179191
Tell if this node's object is a staticmethod.
@@ -361,6 +373,8 @@ def get_object_documentation(self, dotted_path: str, members: Optional[Union[Set
361373
root_object = self.get_staticmethod_documentation(leaf)
362374
elif leaf.is_classmethod():
363375
root_object = self.get_classmethod_documentation(leaf)
376+
elif leaf.is_method_descriptor():
377+
root_object = self.get_regular_method_documentation(leaf)
364378
elif leaf.is_method():
365379
root_object = self.get_regular_method_documentation(leaf)
366380
elif leaf.is_function():
@@ -836,7 +850,7 @@ def get_regular_method_documentation(self, node: ObjectNode) -> Method:
836850

837851
def get_method_documentation(self, node: ObjectNode, properties: Optional[List[str]] = None) -> Method:
838852
"""
839-
Get the documentation for a method.
853+
Get the documentation for a method or method descriptor.
840854
841855
Arguments:
842856
node: The node representing the method and its parents.
@@ -847,6 +861,7 @@ def get_method_documentation(self, node: ObjectNode, properties: Optional[List[s
847861
"""
848862
method = node.obj
849863
path = node.dotted_path
864+
signature: Optional[inspect.Signature]
850865
source: Optional[Source]
851866

852867
try:
@@ -863,12 +878,23 @@ def get_method_documentation(self, node: ObjectNode, properties: Optional[List[s
863878
else:
864879
properties.append("async")
865880

881+
try:
882+
# for "built-in" functions, e.g. those implemented in C,
883+
# inspect.signature() uses the __text_signature__ attribute, which
884+
# provides a limited but still useful amount of signature information.
885+
# "built-in" functions with no __text_signature__ will
886+
# raise a ValueError().
887+
signature = inspect.signature(method)
888+
except ValueError as error:
889+
self.errors.append(f"Couldn't read signature for '{path}': {error}")
890+
signature = None
891+
866892
return Method(
867893
name=node.name,
868894
path=path,
869895
file_path=node.file_path,
870896
docstring=inspect.getdoc(method),
871-
signature=inspect.signature(method),
897+
signature=signature,
872898
properties=properties or [],
873899
source=source,
874900
)

tests/fixtures/method_descriptor.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""See https://docs.python.org/3/library/inspect.html#inspect.ismethoddescriptor for details"""
2+
3+
descriptor = int.__add__

tests/test_loader.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,3 +522,14 @@ def test_loading_cached_properties():
522522
assert len(obj.children) == 1
523523
assert obj.children[0].name == obj.children[0].docstring == "aaa"
524524
assert "cached" in obj.children[0].properties
525+
526+
527+
def test_method_descriptor():
528+
"""Load a method descriptor."""
529+
loader = Loader(new_path_syntax=True)
530+
obj = loader.get_object_documentation("tests.fixtures.method_descriptor:descriptor")
531+
assert obj.name == "descriptor"
532+
assert obj.signature
533+
assert len(obj.signature.parameters) == 2
534+
assert obj.docstring
535+
assert obj.category == "method"

0 commit comments

Comments
 (0)