Skip to content

Commit

Permalink
Merge pull request #166 from OpenMS/feat/newDocStrings
Browse files Browse the repository at this point in the history
new docstring style for functions
  • Loading branch information
jpfeuffer authored Oct 14, 2022
2 parents c4300f5 + 9b03fdb commit 6e38070
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 87 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ sdist
develop-eggs
.installed.cfg
.idea
.vscode

# Installer logs
pip-log.txt
Expand Down
2 changes: 1 addition & 1 deletion .gitpod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# and commit this file to your remote git repository to share the goodness with others.

tasks:
- init: pip install .
- init: pip install pytest black && pip install -e .
vscode:
extensions:
- ms-python.python
Expand Down
15 changes: 12 additions & 3 deletions autowrap/Code.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,17 @@ class Code(object):
def __init__(self):
self.content: List[Union[Code, str]] = []

def __len__(self):
return sum(len(c) if isinstance(c, Code) else 1 for c in self.content)

def extend(self, other: Code) -> None:
# keeps indentation
self.content.extend(other.content)

def addRawList(self, lst):
# keeps indentation
self.content.extend(lst)

def add(self, what: Union[str, bytes, Code], *a, **kw) -> Code:
# may increase indent!
if a: # if dict given
Expand Down Expand Up @@ -88,9 +95,11 @@ def _render(self, _indent="") -> List[str]:
if isinstance(content, basestring):
result.append(_indent + content)
else:
for line in content._render(_indent=_indent + " "):
newindent = _indent + " " * 4
for line in content._render(_indent=newindent):
result.append(line)
return result

def render(self) -> str:
return "\n".join(self._render())
def render(self, indent=0) -> str:
i = " " * indent
return "\n".join(self._render(_indent=i))
142 changes: 90 additions & 52 deletions autowrap/CodeGenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,9 +471,9 @@ def create_wrapper_for_enum(
else:
name = decl.name

doc = decl.cpp_decl.annotations.get("wrap-doc", "")
if doc:
doc = '"""\n ' + "\n ".join(doc) + '\n """'
doc = decl.cpp_decl.annotations.get("wrap-doc", None)
if doc is not None:
doc = '"""\n ' + doc.render(indent=4) + '\n """'

L.info("create wrapper for enum %s" % name)
code = Code()
Expand Down Expand Up @@ -543,7 +543,7 @@ def create_wrapper_for_enum(
|
| def getMapping(self):
| return dict([ (v, k) for k, v in self.__class__.__dict__.items()
+if isinstance(v, int) ])"""
+ if isinstance(v, int) ])"""
)
stub_code.add(
"""
Expand Down Expand Up @@ -604,13 +604,13 @@ def create_wrapper_for_class(
docstring += special_class_doc % locals()
if r_class.cpp_decl.annotations.get("wrap-inherits", "") != "":
docstring += (
" -- Inherits from %s\n"
" -- Inherits from %s\n"
% r_class.cpp_decl.annotations.get("wrap-inherits", "")
)

extra_doc = r_class.cpp_decl.annotations.get("wrap-doc", "")
for extra_doc_line in extra_doc:
docstring += "\n " + extra_doc_line
extra_doc = r_class.cpp_decl.annotations.get("wrap-doc", None)
if extra_doc is not None:
docstring += extra_doc.render(indent=4)

self.typestub_codes[cname] = typestub_code
typestub_code.add(
Expand Down Expand Up @@ -906,34 +906,59 @@ def _create_overloaded_method_decl(
if use_kwargs:
kwargs = ", **kwargs"

docstrings = "\n"
docstrings = Code()
signatures = []
for method in methods:
# Prepare docstring
docstrings += " " * 8 + " - Cython signature: %s" % method
extra_doc = method.cpp_decl.annotations.get("wrap-doc", "")
if len(extra_doc) > 0:
docstrings += "\n" + " " * 12 + extra_doc
docstrings += "\n"
## TODO refactor this part as something like getTypingSignature or getTypingSignatureParts
## or maybe save them for the after-the-next for-loop that generates them again
args = augment_arg_names(method)
py_typing_signature_parts = []
for arg_num, (t, n) in enumerate(args):
converter = self.cr.get(t)
py_typing_type = converter.matching_python_type_full(t)
py_typing_signature_parts.append("%s: %s " % (n, py_typing_type))
args_typestub_str = ", ".join(py_typing_signature_parts)
return_type = self.cr.get(method.result_type).matching_python_type_full(
method.result_type
)

if return_type:
return_type = "-> " + return_type
else:
return_type = ""

# Add autodoc docstring signatures first: https://github.com/sphinx-doc/sphinx/pull/7748
sig = f"{py_name}(self, {args_typestub_str}) {return_type}"
signatures.append(sig)
docstrings.add(sig)

docstrings.add("")

for method, sig in zip(methods, signatures):
# Now add Cython signatures with additional description for each overload (if available)
extra_doc = method.cpp_decl.annotations.get("wrap-doc", None)
if extra_doc is not None:
docstrings.add("- Overload: %s" % sig)
docstrings.add(extra_doc)
docstrings.add("")

docstring_as_str = docstrings.render(indent=8)
method_code.add(
"""
|
|def $py_name(self, *args $kwargs):
| \"\"\"$docstrings\"\"\"
| \"\"\"\n$docstring_as_str
| \"\"\"
""",
locals(),
)

first_iteration = True

for (dispatched_m_name, method) in zip(dispatched_m_names, methods):
for (dispatched_m_name, method, sig) in zip(
dispatched_m_names, methods, signatures
):
args = augment_arg_names(method)
py_typing_signature_parts = []
for arg_num, (t, n) in enumerate(args):
converter = self.cr.get(t)
py_typing_type = converter.matching_python_type_full(t)
py_typing_signature_parts.append("%s: %s " % (n, py_typing_type))
args_typestub_str = ", ".join(py_typing_signature_parts)
return_type = self.cr.get(method.result_type).matching_python_type_full(
method.result_type
)
Expand All @@ -943,18 +968,20 @@ def _create_overloaded_method_decl(
else:
return_type = ":"

# Prepare docstring for typestubs (TODO only prepare once?)
# Prepare docstring for typestubs
docstring = "Cython signature: %s" % method
extra_doc = method.cpp_decl.annotations.get("wrap-doc", "")
if len(extra_doc) > 0:
docstring += "\n" + " " * 8 + extra_doc
extra_doc = method.cpp_decl.annotations.get("wrap-doc", None)
if extra_doc is not None:
docstring += "\n" + extra_doc.render(indent=8)

typestub_code.add(
"""
|
|@overload
|def $py_name(self, $args_typestub_str) $return_type
| \"\"\"$docstring\"\"\"
|def $sig:
| \"\"\"
| $docstring
| \"\"\"
| ...
""",
locals(),
Expand Down Expand Up @@ -1163,21 +1190,34 @@ def _create_fun_decl_and_input_conversion(
py_signature_parts.insert(0, "self")
py_typing_signature_parts.insert(0, "self")

# Prepare docstring
docstring = "Cython signature: %s" % method
extra_doc = method.cpp_decl.annotations.get("wrap-doc", "")
if len(extra_doc) > 0:
docstring += "\n" + " " * 8 + extra_doc

py_signature = ", ".join(py_signature_parts)
py_typing_signature = ", ".join(py_typing_signature_parts)
return_type = self.cr.get(method.result_type).matching_python_type_full(
method.result_type
)
if return_type:
return_type = "-> " + return_type
else:
return_type = ""

# Prepare docstring
docstring = f"{py_name}({py_typing_signature}) {return_type}"
stubdocstring = "Cython signature: %s" % method

extra_doc = method.cpp_decl.annotations.get("wrap-doc", None)
if extra_doc is not None:
docstring += "\n" + extra_doc.render(indent=8)
stubdocstring += "\n" + extra_doc.render(indent=8)

if method.is_static:
code.add(
"""
|
|@staticmethod
|def $py_name($py_signature):
| \"\"\"$docstring\"\"\"
| \"\"\"
| $docstring
| \"\"\"
""",
locals(),
)
Expand All @@ -1186,28 +1226,24 @@ def _create_fun_decl_and_input_conversion(
"""
|
|def $py_name($py_signature):
| \"\"\"$docstring\"\"\"
| \"\"\"
| $docstring
| \"\"\"
""",
locals(),
)

stub = Code()

return_type = self.cr.get(method.result_type).matching_python_type_full(
method.result_type
)
if return_type:
return_type = "-> " + return_type + ":"
else:
return_type = ":"

if method.is_static:
stub.add(
"""
|
|@staticmethod
|def $py_name($py_typing_signature) $return_type
| \"\"\"$docstring\"\"\"
|def $py_name($py_typing_signature) $return_type:
| \"\"\"
| $stubdocstring
| \"\"\"
| ...
""",
locals(),
Expand All @@ -1216,8 +1252,10 @@ def _create_fun_decl_and_input_conversion(
stub.add(
"""
|
|def $py_name($py_typing_signature) $return_type
| \"\"\"$docstring\"\"\"
|def $py_name($py_typing_signature) $return_type:
| \"\"\"
| $stubdocstring
| \"\"\"
| ...
""",
locals(),
Expand Down Expand Up @@ -1793,9 +1831,9 @@ def create_special_setitem_method(self, mdcl):

# Prepare docstring
docstring = "Cython signature: %s" % mdcl
extra_doc = mdcl.cpp_decl.annotations.get("wrap-doc", "")
if len(extra_doc) > 0:
docstring += "\n" + " " * 8 + extra_doc
extra_doc = mdcl.cpp_decl.annotations.get("wrap-doc", None)
if extra_doc is not None:
docstring += "\n" + extra_doc.render(indent=8)

stub_code.add(
"""
Expand Down
27 changes: 24 additions & 3 deletions autowrap/PXDParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import os

from autowrap import logger
from autowrap.Code import Code as Code

from collections import defaultdict
from .tools import OrderKeepingDictionary
Expand Down Expand Up @@ -103,10 +104,12 @@ def _parse_multiline_annotations(lines: Collection[str]) -> AnnotDict:
value = line[3:].rstrip() # rstrip to keep indentation in docs
else:
value = line[1:].strip()

if (
key == "wrap-doc" or value
): # don't add empty non wrap-doc values
result[key].append(value)

try:
line = next(it).lstrip() # lstrip to keep empty lines in docs
except StopIteration:
Expand All @@ -116,6 +119,16 @@ def _parse_multiline_annotations(lines: Collection[str]) -> AnnotDict:
result[key] = True
else:
break

# make sure wrap-doc is always a Code object
if "wrap-doc" in result.keys():
doc = result.get("wrap-doc", [])
if isinstance(doc, basestring):
doc = [doc]

c = Code()
c.addRawList(doc)
result["wrap-doc"] = c
return result


Expand Down Expand Up @@ -166,13 +179,21 @@ def parse_line_annotations(
result[key] += value
except Exception as e:
raise ValueError("Cannot parse '{}'".format(line)) from e

# check for multi line annotations after method declaration
additional_annotations = _parse_multiline_annotations(lines[end:])
# add multi line doc string to result (overwrites single line wrap-doc, if exists)
if "wrap-doc" in additional_annotations.keys():
result["wrap-doc"] = "\n" + "\n".join(additional_annotations["wrap-doc"])

result["wrap-doc"] = additional_annotations["wrap-doc"]
else:
# make sure wrap-doc is always a Code object
if "wrap-doc" in result.keys():
doc = result.get("wrap-doc", [])
if isinstance(doc, basestring):
doc = [doc]

c = Code()
c.addRawList(doc)
result["wrap-doc"] = c
return result


Expand Down
1 change: 1 addition & 0 deletions tests/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def test():
c.add(inner)

result = c.render()
assert len(c) == 5
lines = [line.rstrip() for line in result.split("\n")]
assert lines[0] == "def fun(x):", repr(lines[0])
assert lines[1] == " if x == 3:", repr(lines[1])
Expand Down
8 changes: 4 additions & 4 deletions tests/test_code_generator_libcpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ def test_libcpp():
)
assert libcpp.__name__ == "libcpp"
print(dir(libcpp))
assert len(libcpp.LibCppTest.__doc__) == 214
assert len(libcpp.LibCppTest.twist.__doc__) == 124
assert len(libcpp.LibCppTest.gett.__doc__) == 66
assert len(libcpp.ABS_Impl1.__doc__) == 89
assert len(libcpp.LibCppTest.__doc__) == 213
assert len(libcpp.LibCppTest.twist.__doc__) == 111
assert len(libcpp.LibCppTest.gett.__doc__) == 72
assert len(libcpp.ABS_Impl1.__doc__) == 90

sub_libcpp_copy_constructors(libcpp)

Expand Down
4 changes: 2 additions & 2 deletions tests/test_code_generator_minimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ def output_conversion(self, cpp_type, input_cpp_var, output_py_var):

minimal = wrapped.Minimal()

assert len(minimal.compute.__doc__) == 297
assert len(minimal.compute.__doc__) == 536

assert len(minimal.run.__doc__) == 111
assert len(minimal.run.__doc__) == 143

# test members
assert minimal.m_accessible == 0
Expand Down
Loading

0 comments on commit 6e38070

Please sign in to comment.