blob: 2c8633c299892b485e94657657d5fc6133870b8f [file] [log] [blame]
import re
import sys
from typing import Any, Optional, Tuple, Sequence, MutableSequence, List, MutableMapping
# Type Alias for Signatures
Sig = Tuple[str, str]
def parse_signature(sig: str) -> Optional[Tuple[str,
List[str],
List[str]]]:
m = re.match(r'([.a-zA-Z0-9_]+)\(([^)]*)\)', sig)
if not m:
return None
name = m.group(1)
name = name.split('.')[-1]
arg_string = m.group(2)
if not arg_string.strip():
return (name, [], [])
args = [arg.strip() for arg in arg_string.split(',')]
fixed = []
optional = []
i = 0
while i < len(args):
if args[i].startswith('[') or '=' in args[i]:
break
fixed.append(args[i].rstrip('['))
i += 1
if args[i - 1].endswith('['):
break
while i < len(args):
arg = args[i]
arg = arg.strip('[]')
arg = arg.split('=')[0]
optional.append(arg)
i += 1
return (name, fixed, optional)
def build_signature(fixed: Sequence[str],
optional: Sequence[str]) -> str:
args = [] # type: MutableSequence[str]
args.extend(fixed)
for arg in optional:
if arg.startswith('*'):
args.append(arg)
else:
args.append('%s=...' % arg)
sig = '(%s)' % ', '.join(args)
# Ad-hoc fixes.
sig = sig.replace('(self)', '')
return sig
def parse_all_signatures(lines: Sequence[str]) -> Tuple[List[Sig],
List[Sig]]:
sigs = []
class_sigs = []
for line in lines:
line = line.strip()
m = re.match(r'\.\. *(function|method|class) *:: *[a-zA-Z_]', line)
if m:
sig = line.split('::')[1].strip()
parsed = parse_signature(sig)
if parsed:
name, fixed, optional = parsed
if m.group(1) != 'class':
sigs.append((name, build_signature(fixed, optional)))
else:
class_sigs.append((name, build_signature(fixed, optional)))
return sorted(sigs), sorted(class_sigs)
def find_unique_signatures(sigs: Sequence[Sig]) -> List[Sig]:
sig_map = {} # type: MutableMapping[str, List[str]]
for name, sig in sigs:
sig_map.setdefault(name, []).append(sig)
result = []
for name, name_sigs in sig_map.items():
if len(set(name_sigs)) == 1:
result.append((name, name_sigs[0]))
return sorted(result)
def is_c_module(module):
return '__file__' not in module.__dict__ or module.__dict__['__file__'].endswith('.so')
def write_header(file, module_name, pyversion=(3, 5)):
if module_name:
if pyversion[0] >= 3:
version = '%d.%d' % (sys.version_info.major,
sys.version_info.minor)
else:
version = '2'
file.write('# Stubs for %s (Python %s)\n' % (module_name, version))
file.write(
'#\n'
'# NOTE: This dynamically typed stub was automatically generated by stubgen.\n\n')
def infer_sig_from_docstring(docstr, name):
if not docstr:
return None
docstr = docstr.lstrip()
m = re.match(r'%s(\([a-zA-Z0-9_=, ]*\))' % name, docstr)
if m:
return m.group(1)
else:
return None