Skip to content

Commit

Permalink
fix: retrieve keyword arguments for docstrings (#389)
Browse files Browse the repository at this point in the history
* fix: retrieve keyword arguments for docstrings

* fix: update offset with kwonlydefaults included

* Update docfx_yaml/extension.py

Co-authored-by: Tyler Bui-Palsulich <26876514+tbpg@users.noreply.github.com>

* fix: update to use original_args

* fix: address default index skip issue

* fix: update offset constraint

---------

Co-authored-by: Tyler Bui-Palsulich <26876514+tbpg@users.noreply.github.com>
  • Loading branch information
dandhlee and tbpg committed Sep 6, 2024
1 parent 28ddb34 commit bb6898e
Showing 1 changed file with 62 additions and 41 deletions.
103 changes: 62 additions & 41 deletions docfx_yaml/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -943,56 +943,77 @@ def _update_friendly_package_name(path):
if _type in [METHOD, FUNCTION, CLASS]:
argspec = inspect.getfullargspec(obj) # noqa
type_map = {}
if argspec.annotations:
for annotation in argspec.annotations:
if annotation == "return":

annotations = getattr(argspec, 'annotations', [])
for annotation in argspec.annotations:
if annotation == "return":
continue
try:
type_map[annotation] = _extract_type_name(
argspec.annotations[annotation])
except AttributeError:
print(f"Could not parse argument information for {annotation}.")
continue

# Retrieve arguments from various sources like `argspec.args` and
# `argspec.kwonlyargs` for positional/keyword arguments.
original_args = argspec.args
# Stores default information for kwonly arguments. Unlike
# inspect.defaults, which is a tuple of default values, kw defaults
# is a dict[name, defaultvalue].
kw_defaults = {}
if argspec.kwonlyargs:
original_args.extend(argspec.kwonlyargs)
if argspec.kwonlydefaults:
kw_defaults.update(argspec.kwonlydefaults)
arg_count += len(original_args)

# Retrieve the default values and convert to a list.
defaults = list(getattr(argspec, 'defaults', []))
# Counter used to extract default values.
default_index = -len(argspec.args) + len(defaults)
for arg in original_args:
# Ignore adding in entry for "self" or if docstring cannot be
# formed for current arg, such as docstring is missing or type
# annotation is not given.
if arg == 'cls' or arg not in type_map:
default_index += 1
continue

arg_map = {
'id': arg,
'var_type': type_map[arg],
}

if arg in argspec.args:
# default index will be between 0 and len(defaults) if we
# processed args without defaults, and now only have
# args with default values to assign.
if default_index < 0 or default_index >= len(defaults):
continue
try:
type_map[annotation] = _extract_type_name(
argspec.annotations[annotation])
except AttributeError:
print(f"Could not parse argument information for {annotation}.")
# Only add defaultValue when str(default) doesn't
# contain object address string, for example:
# (object at 0x) or <lambda> at 0x7fed4d57b5e0,
# otherwise inspect.getargspec method will return wrong
# defaults which contain object address for some,
# like sys.stdout.
default_string = str(defaults[default_index])
if 'at 0x' in default_string:
continue
arg_map['defaultValue'] = default_string
if arg in kw_defaults:
# Cast to string, otherwise different types may be stored as
# value instead.
arg_map['defaultValue'] = str(kw_defaults[arg])

# Add up the number of arguments. `argspec.args` contains a list of
# all the arguments from the function.
arg_count += len(argspec.args)
for arg in argspec.args:
arg_map = {}
# Ignore adding in entry for "self"
if arg != 'cls':
arg_map['id'] = arg
if arg in type_map:
arg_map['var_type'] = type_map[arg]
args.append(arg_map)
default_index += 1
args.append(arg_map)

if argspec.varargs:
args.append({'id': argspec.varargs})
if argspec.varkw:
args.append({'id': argspec.varkw})

if argspec.defaults:
# Attempt to add default values to arguments.
try:
for count, default in enumerate(argspec.defaults):
# Find the first index which default arguments start at.
# Every argument after this offset_count all have default values.
offset_count = len(argspec.defaults)
# Find the index of the current default value argument
index = len(args) + count - offset_count

# Only add defaultValue when str(default) doesn't
# contain object address string, for example:
# (object at 0x) or <lambda> at 0x7fed4d57b5e0,
# otherwise inspect.getargspec method will return wrong
# defaults which contain object address for some,
# like sys.stdout.
default_string = str(default)
if 'at 0x' not in default_string:
args[index]['defaultValue'] = default_string
# If we cannot find the argument, it is missing a type and was taken out intentionally.
except IndexError:
pass
try:
if len(lines) == 0:
lines = inspect.getdoc(obj)
Expand Down

0 comments on commit bb6898e

Please sign in to comment.