-
-
Notifications
You must be signed in to change notification settings - Fork 143
WiP: Add support for PEP 484 type hints #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a9ff196
5b5053d
0700277
9977ffe
aebd820
c9dbec0
22dd8ad
d7d061a
1cef049
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ build | |
*.eggs/* | ||
*.egg-info | ||
*.py[cod] | ||
/.eggs/ | ||
|
||
pdoc/_version.py | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -353,7 +353,9 @@ def recursive_htmls(mod): | |||||||||||||||||||||||||
from functools import lru_cache, reduce | ||||||||||||||||||||||||||
from itertools import tee, groupby | ||||||||||||||||||||||||||
from types import ModuleType | ||||||||||||||||||||||||||
from typing import Dict, Iterable, List, Set, Type, TypeVar, Union, Tuple, Generator, Callable | ||||||||||||||||||||||||||
from typing import Dict, List, Set, Tuple | ||||||||||||||||||||||||||
from typing import Optional, Union, Type, TypeVar | ||||||||||||||||||||||||||
from typing import Callable, Iterable, Generator | ||||||||||||||||||||||||||
from warnings import warn | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
from mako.lookup import TemplateLookup | ||||||||||||||||||||||||||
|
@@ -1310,6 +1312,52 @@ def _link_inheritance(self): | |||||||||||||||||||||||||
del self._super_members | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
class Parameter(inspect.Parameter): | ||||||||||||||||||||||||||
"""Representation of a function parameter, incl. type hint & default value.""" | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
POSITIONAL = ( | ||||||||||||||||||||||||||
inspect.Parameter.POSITIONAL_ONLY, | ||||||||||||||||||||||||||
inspect.Parameter.POSITIONAL_OR_KEYWORD, | ||||||||||||||||||||||||||
inspect.Parameter.VAR_POSITIONAL, | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
KEYWORD = ( | ||||||||||||||||||||||||||
inspect.Parameter.KEYWORD_ONLY, | ||||||||||||||||||||||||||
inspect.Parameter.VAR_KEYWORD | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
STARS = { | ||||||||||||||||||||||||||
inspect.Parameter.VAR_POSITIONAL: '*', | ||||||||||||||||||||||||||
inspect.Parameter.VAR_KEYWORD: '**', | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@property | ||||||||||||||||||||||||||
def should_show(self) -> bool: | ||||||||||||||||||||||||||
return _is_public(self.name) or self.default is Parameter.empty | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def __str__(self) -> str: | ||||||||||||||||||||||||||
r = Parameter.STARS.get(self.kind, '') | ||||||||||||||||||||||||||
r += self.name | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if self.annotation is not Parameter.empty: | ||||||||||||||||||||||||||
r += ': ' | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if isinstance(self.annotation, str): | ||||||||||||||||||||||||||
r += self.annotation | ||||||||||||||||||||||||||
elif hasattr(self.annotation, '__name__'): | ||||||||||||||||||||||||||
r += self.annotation.__name__ | ||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||
r += repr(self.annotation) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if self.default is not Parameter.empty: | ||||||||||||||||||||||||||
r += ' = ' + repr(self.default) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
return r | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def of_inspect(p: inspect.Parameter) -> 'Parameter': | ||||||||||||||||||||||||||
return Parameter(p.name, p.kind, default=p.default, annotation=p.annotation) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
class Function(Doc): | ||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
Representation of documentation for a function or method. | ||||||||||||||||||||||||||
|
@@ -1366,62 +1414,30 @@ def _is_async(self): | |||||||||||||||||||||||||
return False | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
@lru_cache() | ||||||||||||||||||||||||||
def params(self) -> List[str]: | ||||||||||||||||||||||||||
def params(self) -> List[Union[str, Parameter]]: | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm strongly opposed to breaking established API by returning something else than strings. It makes no sense, really, as any code that were to do something more than Maybe is better: def params(self, *, types: bool = False) -> List[str] with pdoc API-wise, I think it's easy enough to let the user do on their own if needed: inspect.signature(pdoc_func_obj.obj) In that way, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
pdoc/pdoc/templates/config.mako Lines 1 to 12 in a1f0a15
|
||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
Returns a list where each element is a nicely formatted | ||||||||||||||||||||||||||
parameter of this function. This includes argument lists, | ||||||||||||||||||||||||||
keyword arguments and default values, and it doesn't include any | ||||||||||||||||||||||||||
Returns a list representing each parameter of this function, with a | ||||||||||||||||||||||||||
nicely-formatted string conversion. This includes argument lists, | ||||||||||||||||||||||||||
keyword arguments, and default values, and it doesn't include any | ||||||||||||||||||||||||||
optional arguments whose names begin with an underscore. | ||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||
try: | ||||||||||||||||||||||||||
s = inspect.getfullargspec(inspect.unwrap(self.obj)) | ||||||||||||||||||||||||||
sig = inspect.signature(inspect.unwrap(self.obj)) | ||||||||||||||||||||||||||
except TypeError: | ||||||||||||||||||||||||||
# I guess this is for C builtin functions? | ||||||||||||||||||||||||||
return ["..."] | ||||||||||||||||||||||||||
return [Parameter('...', None, None)] | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
params = [] | ||||||||||||||||||||||||||
for i, param in enumerate(s.args): | ||||||||||||||||||||||||||
if s.defaults is not None and len(s.args) - i <= len(s.defaults): | ||||||||||||||||||||||||||
defind = len(s.defaults) - (len(s.args) - i) | ||||||||||||||||||||||||||
params.append("%s=%s" % (param, repr(s.defaults[defind]))) | ||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||
params.append(param) | ||||||||||||||||||||||||||
if s.varargs is not None: | ||||||||||||||||||||||||||
params.append("*%s" % s.varargs) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
kwonlyargs = getattr(s, "kwonlyargs", None) | ||||||||||||||||||||||||||
if kwonlyargs: | ||||||||||||||||||||||||||
if s.varargs is None: | ||||||||||||||||||||||||||
params.append("*") | ||||||||||||||||||||||||||
for param in kwonlyargs: | ||||||||||||||||||||||||||
try: | ||||||||||||||||||||||||||
params.append("%s=%s" % (param, repr(s.kwonlydefaults[param]))) | ||||||||||||||||||||||||||
except KeyError: | ||||||||||||||||||||||||||
params.append(param) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
keywords = getattr(s, "varkw", getattr(s, "keywords", None)) | ||||||||||||||||||||||||||
if keywords is not None: | ||||||||||||||||||||||||||
params.append("**%s" % keywords) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
# Remove "_private" params following catch-all *args and from the end | ||||||||||||||||||||||||||
iter_params = iter(params) | ||||||||||||||||||||||||||
params = [] | ||||||||||||||||||||||||||
for p in iter_params: | ||||||||||||||||||||||||||
params.append(p) | ||||||||||||||||||||||||||
if p.startswith('*'): | ||||||||||||||||||||||||||
break | ||||||||||||||||||||||||||
while len(params) > 1 and not _is_public(params[-2]) and '=' in params[-2]: | ||||||||||||||||||||||||||
params.pop(-2) | ||||||||||||||||||||||||||
for p in iter_params: | ||||||||||||||||||||||||||
if _is_public(p.lstrip('*')): | ||||||||||||||||||||||||||
params.append(p) | ||||||||||||||||||||||||||
while params and not _is_public(params[-1]) and '=' in params[-1]: | ||||||||||||||||||||||||||
params.pop(-1) | ||||||||||||||||||||||||||
if params and params[-1] == '*': | ||||||||||||||||||||||||||
params.pop(-1) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
# TODO: The only thing now missing for Python 3 are type annotations | ||||||||||||||||||||||||||
return params | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
parameters = [ Parameter.of_inspect(p) for p in sig.parameters.values() ] | ||||||||||||||||||||||||||
pos_params = [ p for p in parameters | ||||||||||||||||||||||||||
if p.should_show and p.kind in Parameter.POSITIONAL ] | ||||||||||||||||||||||||||
kw_params = [ p for p in parameters | ||||||||||||||||||||||||||
if p.should_show and p.kind in Parameter.KEYWORD ] | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if not pos_params and kw_params: | ||||||||||||||||||||||||||
return ['*'] + kw_params | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
return pos_params + kw_params | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
def __lt__(self, other): | ||||||||||||||||||||||||||
# Push __init__ to the top. | ||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -132,7 +132,7 @@ def test_html(self): | |
'>sys.version<', | ||
'B.CONST docstring', | ||
'B.var docstring', | ||
'b=1', | ||
'b: int = 1', | ||
'*args', | ||
'**kwargs', | ||
'__init__ docstring', | ||
|
@@ -264,7 +264,7 @@ def test_text(self): | |
'__init__ docstring', | ||
'instance_var', | ||
'instance var docstring', | ||
'b=1', | ||
'b: int = 1', | ||
'*args', | ||
'**kwargs', | ||
'B.f docstring', | ||
|
@@ -511,15 +511,15 @@ def test_context(self): | |
def test_Function_private_params(self): | ||
func = pdoc.Function('f', pdoc.Module(pdoc), | ||
lambda a, _a, _b=None: None) | ||
self.assertEqual(func.params(), ['a', '_a']) | ||
self.assertEqual([str(p) for p in func.params()], ['a', '_a']) | ||
|
||
func = pdoc.Function('f', pdoc.Module(pdoc), | ||
lambda _ok, a, _a, *args, _b=None, c=None, _d=None: None) | ||
self.assertEqual(func.params(), ['_ok', 'a', '_a', '*args', 'c=None']) | ||
self.assertEqual([str(p) for p in func.params()], ['_ok', 'a', '_a', '*args', 'c = None']) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PEP 8 suggests to use spaces around equals sign only when type is specified: def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ... |
||
|
||
func = pdoc.Function('f', pdoc.Module(pdoc), | ||
lambda a, b, *, _c=1: None) | ||
self.assertEqual(func.params(), ['a', 'b']) | ||
self.assertEqual([str(p) for p in func.params()], ['a', 'b']) | ||
|
||
def test_url(self): | ||
mod = pdoc.Module(pdoc.import_module(EXAMPLE_MODULE)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this line necessary with
*.eggs/*
already specified three lines above?