mirror of
https://github.com/python/cpython.git
synced 2025-10-09 16:34:44 +00:00
gh-94673: [c-analyzer] Add a Script to Identify Static Types (#94989)
issue: https://github.com/python/cpython/issues/94673
This commit is contained in:
parent
0daba82221
commit
7a1a85d640
5 changed files with 613 additions and 151 deletions
|
@ -600,17 +600,9 @@ StopIteration_traverse(PyStopIterationObject *self, visitproc visit, void *arg)
|
|||
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
|
||||
}
|
||||
|
||||
ComplexExtendsException(
|
||||
PyExc_Exception, /* base */
|
||||
StopIteration, /* name */
|
||||
StopIteration, /* prefix for *_init, etc */
|
||||
0, /* new */
|
||||
0, /* methods */
|
||||
StopIteration_members, /* members */
|
||||
0, /* getset */
|
||||
0, /* str */
|
||||
"Signal the end from iterator.__next__()."
|
||||
);
|
||||
ComplexExtendsException(PyExc_Exception, StopIteration, StopIteration,
|
||||
0, 0, StopIteration_members, 0, 0,
|
||||
"Signal the end from iterator.__next__().");
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from collections import namedtuple
|
||||
import csv
|
||||
import re
|
||||
import textwrap
|
||||
|
@ -225,7 +226,11 @@ WIDTH = 20
|
|||
def resolve_columns(specs):
|
||||
if isinstance(specs, str):
|
||||
specs = specs.replace(',', ' ').strip().split()
|
||||
return _resolve_colspecs(specs)
|
||||
resolved = []
|
||||
for raw in specs:
|
||||
column = ColumnSpec.from_raw(raw)
|
||||
resolved.append(column)
|
||||
return resolved
|
||||
|
||||
|
||||
def build_table(specs, *, sep=' ', defaultwidth=None):
|
||||
|
@ -233,37 +238,145 @@ def build_table(specs, *, sep=' ', defaultwidth=None):
|
|||
return _build_table(columns, sep=sep, defaultwidth=defaultwidth)
|
||||
|
||||
|
||||
_COLSPEC_RE = re.compile(textwrap.dedent(r'''
|
||||
^
|
||||
(?:
|
||||
\[
|
||||
(
|
||||
(?: [^\s\]] [^\]]* )?
|
||||
[^\s\]]
|
||||
) # <label>
|
||||
]
|
||||
)?
|
||||
( \w+ ) # <field>
|
||||
(?:
|
||||
class ColumnSpec(namedtuple('ColumnSpec', 'field label fmt')):
|
||||
|
||||
REGEX = re.compile(textwrap.dedent(r'''
|
||||
^
|
||||
(?:
|
||||
:
|
||||
( [<^>] ) # <align>
|
||||
( \d+ ) # <width1>
|
||||
)
|
||||
|
|
||||
\[
|
||||
(
|
||||
(?: [^\s\]] [^\]]* )?
|
||||
[^\s\]]
|
||||
) # <label>
|
||||
]
|
||||
)?
|
||||
( [-\w]+ ) # <field>
|
||||
(?:
|
||||
(?:
|
||||
:
|
||||
( \d+ ) # <width2>
|
||||
)?
|
||||
( [<^>] ) # <align>
|
||||
( \d+ )? # <width1>
|
||||
)
|
||||
|
|
||||
(?:
|
||||
:
|
||||
( .*? ) # <fmt>
|
||||
)?
|
||||
)
|
||||
)?
|
||||
$
|
||||
'''), re.VERBOSE)
|
||||
(?:
|
||||
:
|
||||
( \d+ ) # <width2>
|
||||
)?
|
||||
(?:
|
||||
:
|
||||
( .*? ) # <fmt>
|
||||
)?
|
||||
)
|
||||
)?
|
||||
$
|
||||
'''), re.VERBOSE)
|
||||
|
||||
@classmethod
|
||||
def from_raw(cls, raw):
|
||||
if not raw:
|
||||
raise ValueError('missing column spec')
|
||||
elif isinstance(raw, cls):
|
||||
return raw
|
||||
|
||||
if isinstance(raw, str):
|
||||
*values, _ = cls._parse(raw)
|
||||
else:
|
||||
*values, _ = cls._normalize(raw)
|
||||
if values is None:
|
||||
raise ValueError(f'unsupported column spec {raw!r}')
|
||||
return cls(*values)
|
||||
|
||||
@classmethod
|
||||
def parse(cls, specstr):
|
||||
parsed = cls._parse(specstr)
|
||||
if not parsed:
|
||||
return None
|
||||
*values, _ = parsed
|
||||
return cls(*values)
|
||||
|
||||
@classmethod
|
||||
def _parse(cls, specstr):
|
||||
m = cls.REGEX.match(specstr)
|
||||
if not m:
|
||||
return None
|
||||
(label, field,
|
||||
align, width1,
|
||||
width2, fmt,
|
||||
) = m.groups()
|
||||
if not label:
|
||||
label = field
|
||||
if fmt:
|
||||
assert not align and not width1, (specstr,)
|
||||
_parsed = _parse_fmt(fmt)
|
||||
if not _parsed:
|
||||
raise NotImplementedError
|
||||
elif width2:
|
||||
width, _ = _parsed
|
||||
if width != int(width2):
|
||||
raise NotImplementedError(specstr)
|
||||
elif width2:
|
||||
fmt = width2
|
||||
width = int(width2)
|
||||
else:
|
||||
assert not fmt, (fmt, specstr)
|
||||
if align:
|
||||
width = int(width1) if width1 else len(label)
|
||||
fmt = f'{align}{width}'
|
||||
else:
|
||||
width = None
|
||||
return field, label, fmt, width
|
||||
|
||||
@classmethod
|
||||
def _normalize(cls, spec):
|
||||
if len(spec) == 1:
|
||||
raw, = spec
|
||||
raise NotImplementedError
|
||||
return _resolve_column(raw)
|
||||
|
||||
if len(spec) == 4:
|
||||
label, field, width, fmt = spec
|
||||
if width:
|
||||
if not fmt:
|
||||
fmt = str(width)
|
||||
elif _parse_fmt(fmt)[0] != width:
|
||||
raise ValueError(f'width mismatch in {spec}')
|
||||
elif len(raw) == 3:
|
||||
label, field, fmt = spec
|
||||
if not field:
|
||||
label, field = None, label
|
||||
elif not isinstance(field, str) or not field.isidentifier():
|
||||
# XXX This doesn't seem right...
|
||||
fmt = f'{field}:{fmt}' if fmt else field
|
||||
label, field = None, label
|
||||
elif len(raw) == 2:
|
||||
label = None
|
||||
field, fmt = raw
|
||||
if not field:
|
||||
field, fmt = fmt, None
|
||||
elif not field.isidentifier() or fmt.isidentifier():
|
||||
label, field = field, fmt
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
fmt = f':{fmt}' if fmt else ''
|
||||
if label:
|
||||
return cls._parse(f'[{label}]{field}{fmt}')
|
||||
else:
|
||||
return cls._parse(f'{field}{fmt}')
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
if not self.fmt:
|
||||
return None
|
||||
parsed = _parse_fmt(self.fmt)
|
||||
if not parsed:
|
||||
return None
|
||||
width, _ = parsed
|
||||
return width
|
||||
|
||||
def resolve_width(self, default=None):
|
||||
return _resolve_width(self.width, self.fmt, self.label, default)
|
||||
|
||||
|
||||
def _parse_fmt(fmt):
|
||||
|
@ -272,100 +385,31 @@ def _parse_fmt(fmt):
|
|||
width = fmt[1:]
|
||||
if width.isdigit():
|
||||
return int(width), align
|
||||
return None, None
|
||||
elif fmt.isdigit():
|
||||
return int(fmt), '<'
|
||||
return None
|
||||
|
||||
|
||||
def _parse_colspec(raw):
|
||||
m = _COLSPEC_RE.match(raw)
|
||||
if not m:
|
||||
return None
|
||||
label, field, align, width1, width2, fmt = m.groups()
|
||||
if not label:
|
||||
label = field
|
||||
if width1:
|
||||
width = None
|
||||
fmt = f'{align}{width1}'
|
||||
elif width2:
|
||||
width = int(width2)
|
||||
if fmt:
|
||||
_width, _ = _parse_fmt(fmt)
|
||||
if _width == width:
|
||||
width = None
|
||||
else:
|
||||
width = None
|
||||
return field, label, width, fmt
|
||||
|
||||
|
||||
def _normalize_colspec(spec):
|
||||
if len(spec) == 1:
|
||||
raw, = spec
|
||||
return _resolve_column(raw)
|
||||
|
||||
if len(spec) == 4:
|
||||
label, field, width, fmt = spec
|
||||
if width:
|
||||
fmt = f'{width}:{fmt}' if fmt else width
|
||||
elif len(raw) == 3:
|
||||
label, field, fmt = spec
|
||||
if not field:
|
||||
label, field = None, label
|
||||
elif not isinstance(field, str) or not field.isidentifier():
|
||||
fmt = f'{field}:{fmt}' if fmt else field
|
||||
label, field = None, label
|
||||
elif len(raw) == 2:
|
||||
label = None
|
||||
field, fmt = raw
|
||||
if not field:
|
||||
field, fmt = fmt, None
|
||||
elif not field.isidentifier() or fmt.isidentifier():
|
||||
label, field = field, fmt
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
fmt = f':{fmt}' if fmt else ''
|
||||
if label:
|
||||
return _parse_colspec(f'[{label}]{field}{fmt}')
|
||||
else:
|
||||
return _parse_colspec(f'{field}{fmt}')
|
||||
|
||||
|
||||
def _resolve_colspec(raw):
|
||||
if isinstance(raw, str):
|
||||
spec = _parse_colspec(raw)
|
||||
else:
|
||||
spec = _normalize_colspec(raw)
|
||||
if spec is None:
|
||||
raise ValueError(f'unsupported column spec {raw!r}')
|
||||
return spec
|
||||
|
||||
|
||||
def _resolve_colspecs(columns):
|
||||
parsed = []
|
||||
for raw in columns:
|
||||
column = _resolve_colspec(raw)
|
||||
parsed.append(column)
|
||||
return parsed
|
||||
|
||||
|
||||
def _resolve_width(spec, defaultwidth):
|
||||
_, label, width, fmt = spec
|
||||
def _resolve_width(width, fmt, label, default):
|
||||
if width:
|
||||
if not isinstance(width, int):
|
||||
raise NotImplementedError
|
||||
return width
|
||||
elif width and fmt:
|
||||
width, _ = _parse_fmt(fmt)
|
||||
if width:
|
||||
return width
|
||||
elif fmt:
|
||||
parsed = _parse_fmt(fmt)
|
||||
if parsed:
|
||||
width, _ = parsed
|
||||
if width:
|
||||
return width
|
||||
|
||||
if not defaultwidth:
|
||||
if not default:
|
||||
return WIDTH
|
||||
elif not hasattr(defaultwidth, 'get'):
|
||||
return defaultwidth or WIDTH
|
||||
|
||||
defaultwidths = defaultwidth
|
||||
defaultwidth = defaultwidths.get(None) or WIDTH
|
||||
return defaultwidths.get(label) or defaultwidth
|
||||
elif hasattr(default, 'get'):
|
||||
defaults = default
|
||||
default = defaults.get(None) or WIDTH
|
||||
return defaults.get(label) or default
|
||||
else:
|
||||
return default or WIDTH
|
||||
|
||||
|
||||
def _build_table(columns, *, sep=' ', defaultwidth=None):
|
||||
|
@ -373,16 +417,13 @@ def _build_table(columns, *, sep=' ', defaultwidth=None):
|
|||
div = []
|
||||
rowfmt = []
|
||||
for spec in columns:
|
||||
label, field, _, colfmt = spec
|
||||
width = _resolve_width(spec, defaultwidth)
|
||||
if colfmt:
|
||||
colfmt = f':{colfmt}'
|
||||
else:
|
||||
colfmt = f':{width}'
|
||||
width = spec.resolve_width(defaultwidth)
|
||||
colfmt = spec.fmt
|
||||
colfmt = f':{spec.fmt}' if spec.fmt else f':{width}'
|
||||
|
||||
header.append(f' {{:^{width}}} '.format(label))
|
||||
header.append(f' {{:^{width}}} '.format(spec.label))
|
||||
div.append('-' * (width + 2))
|
||||
rowfmt.append(f' {{{field}{colfmt}}} ')
|
||||
rowfmt.append(f' {{{spec.field}{colfmt}}} ')
|
||||
return (
|
||||
sep.join(header),
|
||||
sep.join(div),
|
||||
|
|
|
@ -20,7 +20,7 @@ import c_parser.__main__ as c_parser
|
|||
import c_analyzer.__main__ as c_analyzer
|
||||
import c_analyzer as _c_analyzer
|
||||
from c_analyzer.info import UNKNOWN
|
||||
from . import _analyzer, _capi, _files, _parser, REPO_ROOT
|
||||
from . import _analyzer, _builtin_types, _capi, _files, _parser, REPO_ROOT
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -325,6 +325,47 @@ def cmd_capi(filenames=None, *,
|
|||
print(line)
|
||||
|
||||
|
||||
def _cli_builtin_types(parser):
|
||||
parser.add_argument('--format', dest='fmt', default='table')
|
||||
# parser.add_argument('--summary', dest='format',
|
||||
# action='store_const', const='summary')
|
||||
def process_format(args, *, argv=None):
|
||||
orig = args.fmt
|
||||
args.fmt = _builtin_types.resolve_format(args.fmt)
|
||||
if isinstance(args.fmt, str):
|
||||
if args.fmt not in _builtin_types._FORMATS:
|
||||
parser.error(f'unsupported format {orig!r}')
|
||||
|
||||
parser.add_argument('--include-modules', dest='showmodules',
|
||||
action='store_true')
|
||||
def process_modules(args, *, argv=None):
|
||||
pass
|
||||
|
||||
return [
|
||||
process_format,
|
||||
process_modules,
|
||||
]
|
||||
|
||||
|
||||
def cmd_builtin_types(fmt, *,
|
||||
showmodules=False,
|
||||
verbosity=VERBOSITY,
|
||||
):
|
||||
render = _builtin_types.get_renderer(fmt)
|
||||
types = _builtin_types.iter_builtin_types()
|
||||
match = _builtin_types.resolve_matcher(showmodules)
|
||||
if match:
|
||||
types = (t for t in types if match(t, log=lambda msg: logger.log(1, msg)))
|
||||
|
||||
lines = render(
|
||||
types,
|
||||
# verbose=verbosity > VERBOSITY,
|
||||
)
|
||||
print()
|
||||
for line in lines:
|
||||
print(line)
|
||||
|
||||
|
||||
# We do not define any other cmd_*() handlers here,
|
||||
# favoring those defined elsewhere.
|
||||
|
||||
|
@ -354,6 +395,11 @@ COMMANDS = {
|
|||
[_cli_capi],
|
||||
cmd_capi,
|
||||
),
|
||||
'builtin-types': (
|
||||
'show the builtin types',
|
||||
[_cli_builtin_types],
|
||||
cmd_builtin_types,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
|
365
Tools/c-analyzer/cpython/_builtin_types.py
Normal file
365
Tools/c-analyzer/cpython/_builtin_types.py
Normal file
|
@ -0,0 +1,365 @@
|
|||
from collections import namedtuple
|
||||
import os.path
|
||||
import re
|
||||
import textwrap
|
||||
|
||||
from c_common import tables
|
||||
from . import REPO_ROOT
|
||||
from ._files import iter_header_files, iter_filenames
|
||||
|
||||
|
||||
CAPI_PREFIX = os.path.join('Include', '')
|
||||
INTERNAL_PREFIX = os.path.join('Include', 'internal', '')
|
||||
|
||||
REGEX = re.compile(textwrap.dedent(rf'''
|
||||
(?:
|
||||
^
|
||||
(?:
|
||||
(?:
|
||||
(?:
|
||||
(?:
|
||||
(?:
|
||||
( static ) # <static>
|
||||
\s+
|
||||
|
|
||||
( extern ) # <extern>
|
||||
\s+
|
||||
)?
|
||||
PyTypeObject \s+
|
||||
)
|
||||
|
|
||||
(?:
|
||||
( PyAPI_DATA ) # <capi>
|
||||
\s* [(] \s* PyTypeObject \s* [)] \s*
|
||||
)
|
||||
)
|
||||
(\w+) # <name>
|
||||
\s*
|
||||
(?:
|
||||
(?:
|
||||
( = \s* {{ ) # <def>
|
||||
$
|
||||
)
|
||||
|
|
||||
( ; ) # <decl>
|
||||
)
|
||||
)
|
||||
|
|
||||
(?:
|
||||
# These are specific to Objects/exceptions.c:
|
||||
(?:
|
||||
SimpleExtendsException
|
||||
|
|
||||
MiddlingExtendsException
|
||||
|
|
||||
ComplexExtendsException
|
||||
)
|
||||
\( \w+ \s* , \s*
|
||||
( \w+ ) # <excname>
|
||||
\s* ,
|
||||
)
|
||||
)
|
||||
)
|
||||
'''), re.VERBOSE)
|
||||
|
||||
|
||||
def _parse_line(line):
|
||||
m = re.match(REGEX, line)
|
||||
if not m:
|
||||
return None
|
||||
(static, extern, capi,
|
||||
name,
|
||||
def_, decl,
|
||||
excname,
|
||||
) = m.groups()
|
||||
if def_:
|
||||
isdecl = False
|
||||
if extern or capi:
|
||||
raise NotImplementedError(line)
|
||||
kind = 'static' if static else None
|
||||
elif excname:
|
||||
name = f'_PyExc_{excname}'
|
||||
isdecl = False
|
||||
kind = 'static'
|
||||
else:
|
||||
isdecl = True
|
||||
if static:
|
||||
kind = 'static'
|
||||
elif extern:
|
||||
kind = 'extern'
|
||||
elif capi:
|
||||
kind = 'capi'
|
||||
else:
|
||||
kind = None
|
||||
return name, isdecl, kind
|
||||
|
||||
|
||||
class BuiltinTypeDecl(namedtuple('BuiltinTypeDecl', 'file lno name kind')):
|
||||
|
||||
KINDS = {
|
||||
'static',
|
||||
'extern',
|
||||
'capi',
|
||||
'forward',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_line(cls, line, filename, lno):
|
||||
# This is similar to ._capi.CAPIItem.from_line().
|
||||
parsed = _parse_line(line)
|
||||
if not parsed:
|
||||
return None
|
||||
name, isdecl, kind = parsed
|
||||
if not isdecl:
|
||||
return None
|
||||
return cls.from_parsed(name, kind, filename, lno)
|
||||
|
||||
@classmethod
|
||||
def from_parsed(cls, name, kind, filename, lno):
|
||||
if not kind:
|
||||
kind = 'forward'
|
||||
return cls.from_values(filename, lno, name, kind)
|
||||
|
||||
@classmethod
|
||||
def from_values(cls, filename, lno, name, kind):
|
||||
if kind not in cls.KINDS:
|
||||
raise ValueError(f'unsupported kind {kind!r}')
|
||||
self = cls(filename, lno, name, kind)
|
||||
if self.kind not in ('extern', 'capi') and self.api:
|
||||
raise NotImplementedError(self)
|
||||
elif self.kind == 'capi' and not self.api:
|
||||
raise NotImplementedError(self)
|
||||
return self
|
||||
|
||||
@property
|
||||
def relfile(self):
|
||||
return self.file[len(REPO_ROOT) + 1:]
|
||||
|
||||
@property
|
||||
def api(self):
|
||||
return self.relfile.startswith(CAPI_PREFIX)
|
||||
|
||||
@property
|
||||
def internal(self):
|
||||
return self.relfile.startswith(INTERNAL_PREFIX)
|
||||
|
||||
@property
|
||||
def private(self):
|
||||
if not self.name.startswith('_'):
|
||||
return False
|
||||
return self.api and not self.internal
|
||||
|
||||
@property
|
||||
def public(self):
|
||||
if self.kind != 'capi':
|
||||
return False
|
||||
return not self.internal and not self.private
|
||||
|
||||
|
||||
class BuiltinTypeInfo(namedtuple('BuiltinTypeInfo', 'file lno name static decl')):
|
||||
|
||||
@classmethod
|
||||
def from_line(cls, line, filename, lno, *, decls=None):
|
||||
parsed = _parse_line(line)
|
||||
if not parsed:
|
||||
return None
|
||||
name, isdecl, kind = parsed
|
||||
if isdecl:
|
||||
return None
|
||||
return cls.from_parsed(name, kind, filename, lno, decls=decls)
|
||||
|
||||
@classmethod
|
||||
def from_parsed(cls, name, kind, filename, lno, *, decls=None):
|
||||
if not kind:
|
||||
static = False
|
||||
elif kind == 'static':
|
||||
static = True
|
||||
else:
|
||||
raise NotImplementedError((filename, line, kind))
|
||||
decl = decls.get(name) if decls else None
|
||||
return cls(filename, lno, name, static, decl)
|
||||
|
||||
@property
|
||||
def relfile(self):
|
||||
return self.file[len(REPO_ROOT) + 1:]
|
||||
|
||||
@property
|
||||
def exported(self):
|
||||
return not self.static
|
||||
|
||||
@property
|
||||
def api(self):
|
||||
if not self.decl:
|
||||
return False
|
||||
return self.decl.api
|
||||
|
||||
@property
|
||||
def internal(self):
|
||||
if not self.decl:
|
||||
return False
|
||||
return self.decl.internal
|
||||
|
||||
@property
|
||||
def private(self):
|
||||
if not self.decl:
|
||||
return False
|
||||
return self.decl.private
|
||||
|
||||
@property
|
||||
def public(self):
|
||||
if not self.decl:
|
||||
return False
|
||||
return self.decl.public
|
||||
|
||||
@property
|
||||
def inmodule(self):
|
||||
return self.relfile.startswith('Modules' + os.path.sep)
|
||||
|
||||
def render_rowvalues(self, kinds):
|
||||
row = {
|
||||
'name': self.name,
|
||||
**{k: '' for k in kinds},
|
||||
'filename': f'{self.relfile}:{self.lno}',
|
||||
}
|
||||
if self.static:
|
||||
kind = 'static'
|
||||
else:
|
||||
if self.internal:
|
||||
kind = 'internal'
|
||||
elif self.private:
|
||||
kind = 'private'
|
||||
elif self.public:
|
||||
kind = 'public'
|
||||
else:
|
||||
kind = 'global'
|
||||
row['kind'] = kind
|
||||
row[kind] = kind
|
||||
return row
|
||||
|
||||
|
||||
def _ensure_decl(decl, decls):
|
||||
prev = decls.get(decl.name)
|
||||
if prev:
|
||||
if decl.kind == 'forward':
|
||||
return None
|
||||
if prev.kind != 'forward':
|
||||
if decl.kind == prev.kind and decl.file == prev.file:
|
||||
assert decl.lno != prev.lno, (decl, prev)
|
||||
return None
|
||||
raise NotImplementedError(f'duplicate {decl} (was {prev}')
|
||||
decls[decl.name] = decl
|
||||
|
||||
|
||||
def iter_builtin_types(filenames=None):
|
||||
decls = {}
|
||||
seen = set()
|
||||
for filename in iter_header_files():
|
||||
seen.add(filename)
|
||||
with open(filename) as infile:
|
||||
for lno, line in enumerate(infile, 1):
|
||||
decl = BuiltinTypeDecl.from_line(line, filename, lno)
|
||||
if not decl:
|
||||
continue
|
||||
_ensure_decl(decl, decls)
|
||||
srcfiles = []
|
||||
for filename in iter_filenames():
|
||||
if filename.endswith('.c'):
|
||||
srcfiles.append(filename)
|
||||
continue
|
||||
if filename in seen:
|
||||
continue
|
||||
with open(filename) as infile:
|
||||
for lno, line in enumerate(infile, 1):
|
||||
decl = BuiltinTypeDecl.from_line(line, filename, lno)
|
||||
if not decl:
|
||||
continue
|
||||
_ensure_decl(decl, decls)
|
||||
|
||||
for filename in srcfiles:
|
||||
with open(filename) as infile:
|
||||
localdecls = {}
|
||||
for lno, line in enumerate(infile, 1):
|
||||
parsed = _parse_line(line)
|
||||
if not parsed:
|
||||
continue
|
||||
name, isdecl, kind = parsed
|
||||
if isdecl:
|
||||
decl = BuiltinTypeDecl.from_parsed(name, kind, filename, lno)
|
||||
if not decl:
|
||||
raise NotImplementedError((filename, line))
|
||||
_ensure_decl(decl, localdecls)
|
||||
else:
|
||||
builtin = BuiltinTypeInfo.from_parsed(
|
||||
name, kind, filename, lno,
|
||||
decls=decls if name in decls else localdecls)
|
||||
if not builtin:
|
||||
raise NotImplementedError((filename, line))
|
||||
yield builtin
|
||||
|
||||
|
||||
def resolve_matcher(showmodules=False):
|
||||
def match(info, *, log=None):
|
||||
if not info.inmodule:
|
||||
return True
|
||||
if log is not None:
|
||||
log(f'ignored {info.name!r}')
|
||||
return False
|
||||
return match
|
||||
|
||||
|
||||
##################################
|
||||
# CLI rendering
|
||||
|
||||
def resolve_format(fmt):
|
||||
if not fmt:
|
||||
return 'table'
|
||||
elif isinstance(fmt, str) and fmt in _FORMATS:
|
||||
return fmt
|
||||
else:
|
||||
raise NotImplementedError(fmt)
|
||||
|
||||
|
||||
def get_renderer(fmt):
|
||||
fmt = resolve_format(fmt)
|
||||
if isinstance(fmt, str):
|
||||
try:
|
||||
return _FORMATS[fmt]
|
||||
except KeyError:
|
||||
raise ValueError(f'unsupported format {fmt!r}')
|
||||
else:
|
||||
raise NotImplementedError(fmt)
|
||||
|
||||
|
||||
def render_table(types):
|
||||
types = sorted(types, key=(lambda t: t.name))
|
||||
colspecs = tables.resolve_columns(
|
||||
'name:<33 static:^ global:^ internal:^ private:^ public:^ filename:<30')
|
||||
header, div, rowfmt = tables.build_table(colspecs)
|
||||
leader = ' ' * sum(c.width+2 for c in colspecs[:3]) + ' '
|
||||
yield leader + f'{"API":^29}'
|
||||
yield leader + '-' * 29
|
||||
yield header
|
||||
yield div
|
||||
kinds = [c[0] for c in colspecs[1:-1]]
|
||||
counts = {k: 0 for k in kinds}
|
||||
base = {k: '' for k in kinds}
|
||||
for t in types:
|
||||
row = t.render_rowvalues(kinds)
|
||||
kind = row['kind']
|
||||
yield rowfmt.format(**row)
|
||||
counts[kind] += 1
|
||||
yield ''
|
||||
yield f'total: {sum(counts.values()):>3}'
|
||||
for kind in kinds:
|
||||
yield f' {kind:>10}: {counts[kind]:>3}'
|
||||
|
||||
|
||||
def render_repr(types):
|
||||
for t in types:
|
||||
yield repr(t)
|
||||
|
||||
|
||||
_FORMATS = {
|
||||
'table': render_table,
|
||||
'repr': render_repr,
|
||||
}
|
|
@ -7,7 +7,7 @@ filename funcname name reason
|
|||
# global objects to fix in core code
|
||||
|
||||
#-----------------------
|
||||
# static types
|
||||
# exported builtin types (C-API)
|
||||
|
||||
Objects/boolobject.c - PyBool_Type -
|
||||
Objects/bytearrayobject.c - PyByteArrayIter_Type -
|
||||
|
@ -18,8 +18,6 @@ Objects/capsule.c - PyCapsule_Type -
|
|||
Objects/cellobject.c - PyCell_Type -
|
||||
Objects/classobject.c - PyInstanceMethod_Type -
|
||||
Objects/classobject.c - PyMethod_Type -
|
||||
Objects/codeobject.c - _PyLineIterator -
|
||||
Objects/codeobject.c - _PyPositionsIterator -
|
||||
Objects/codeobject.c - PyCode_Type -
|
||||
Objects/complexobject.c - PyComplex_Type -
|
||||
Objects/descrobject.c - PyClassMethodDescr_Type -
|
||||
|
@ -42,16 +40,12 @@ Objects/dictobject.c - PyDictValues_Type -
|
|||
Objects/dictobject.c - PyDict_Type -
|
||||
Objects/enumobject.c - PyEnum_Type -
|
||||
Objects/enumobject.c - PyReversed_Type -
|
||||
Objects/exceptions.c - _PyExc_BaseExceptionGroup -
|
||||
Objects/exceptions.c - _PyExc_EncodingWarning -
|
||||
Objects/fileobject.c - PyStdPrinter_Type -
|
||||
Objects/floatobject.c - FloatInfoType -
|
||||
Objects/floatobject.c - PyFloat_Type -
|
||||
Objects/frameobject.c - PyFrame_Type -
|
||||
Objects/funcobject.c - PyClassMethod_Type -
|
||||
Objects/funcobject.c - PyFunction_Type -
|
||||
Objects/funcobject.c - PyStaticMethod_Type -
|
||||
Objects/genericaliasobject.c - _Py_GenericAliasIterType -
|
||||
Objects/genericaliasobject.c - Py_GenericAliasType -
|
||||
Objects/genobject.c - PyAsyncGen_Type -
|
||||
Objects/genobject.c - PyCoro_Type -
|
||||
|
@ -63,13 +57,10 @@ Objects/genobject.c - _PyCoroWrapper_Type -
|
|||
Objects/interpreteridobject.c - _PyInterpreterID_Type -
|
||||
Objects/iterobject.c - PyCallIter_Type -
|
||||
Objects/iterobject.c - PySeqIter_Type -
|
||||
Objects/iterobject.c - _PyAnextAwaitable_Type -
|
||||
Objects/listobject.c - PyListIter_Type -
|
||||
Objects/listobject.c - PyListRevIter_Type -
|
||||
Objects/listobject.c - PyList_Type -
|
||||
Objects/longobject.c - Int_InfoType -
|
||||
Objects/longobject.c - PyLong_Type -
|
||||
Objects/memoryobject.c - _PyMemoryIter_Type -
|
||||
Objects/memoryobject.c - PyMemoryView_Type -
|
||||
Objects/memoryobject.c - _PyManagedBuffer_Type -
|
||||
Objects/methodobject.c - PyCFunction_Type -
|
||||
|
@ -91,7 +82,6 @@ Objects/rangeobject.c - PyRange_Type -
|
|||
Objects/setobject.c - PyFrozenSet_Type -
|
||||
Objects/setobject.c - PySetIter_Type -
|
||||
Objects/setobject.c - PySet_Type -
|
||||
Objects/setobject.c - _PySetDummy_Type -
|
||||
Objects/sliceobject.c - PyEllipsis_Type -
|
||||
Objects/sliceobject.c - PySlice_Type -
|
||||
Objects/tupleobject.c - PyTupleIter_Type -
|
||||
|
@ -99,11 +89,8 @@ Objects/tupleobject.c - PyTuple_Type -
|
|||
Objects/typeobject.c - PyBaseObject_Type -
|
||||
Objects/typeobject.c - PySuper_Type -
|
||||
Objects/typeobject.c - PyType_Type -
|
||||
Objects/unicodeobject.c - EncodingMapType -
|
||||
Objects/unicodeobject.c - PyUnicodeIter_Type -
|
||||
Objects/unicodeobject.c - PyUnicode_Type -
|
||||
Objects/unionobject.c - _PyUnion_Type -
|
||||
Objects/unionobject.c - _Py_UnionType -
|
||||
Objects/weakrefobject.c - _PyWeakref_CallableProxyType -
|
||||
Objects/weakrefobject.c - _PyWeakref_ProxyType -
|
||||
Objects/weakrefobject.c - _PyWeakref_RefType -
|
||||
|
@ -113,8 +100,23 @@ Python/bltinmodule.c - PyZip_Type -
|
|||
Python/context.c - PyContextToken_Type -
|
||||
Python/context.c - PyContextVar_Type -
|
||||
Python/context.c - PyContext_Type -
|
||||
Python/traceback.c - PyTraceBack_Type -
|
||||
|
||||
#-----------------------
|
||||
# other exported builtin types
|
||||
|
||||
# Not in a .h file:
|
||||
Objects/codeobject.c - _PyLineIterator -
|
||||
# Not in a .h file:
|
||||
Objects/codeobject.c - _PyPositionsIterator -
|
||||
Objects/genericaliasobject.c - _Py_GenericAliasIterType -
|
||||
# Not in a .h file:
|
||||
Objects/iterobject.c - _PyAnextAwaitable_Type -
|
||||
# Not in a .h file:
|
||||
Objects/memoryobject.c - _PyMemoryIter_Type -
|
||||
#Objects/unicodeobject.c - _PyUnicodeASCIIIter_Type -
|
||||
Objects/unionobject.c - _PyUnion_Type -
|
||||
Python/context.c - _PyContextTokenMissing_Type -
|
||||
Python/errors.c - UnraisableHookArgsType -
|
||||
Python/hamt.c - _PyHamtItems_Type -
|
||||
Python/hamt.c - _PyHamtKeys_Type -
|
||||
Python/hamt.c - _PyHamtValues_Type -
|
||||
|
@ -123,17 +125,32 @@ Python/hamt.c - _PyHamt_BitmapNode_Type -
|
|||
Python/hamt.c - _PyHamt_CollisionNode_Type -
|
||||
Python/hamt.c - _PyHamt_Type -
|
||||
Python/symtable.c - PySTEntry_Type -
|
||||
|
||||
#-----------------------
|
||||
# private static builtin types
|
||||
|
||||
Objects/setobject.c - _PySetDummy_Type -
|
||||
Objects/unicodeobject.c - EncodingMapType -
|
||||
#Objects/unicodeobject.c - PyFieldNameIter_Type -
|
||||
#Objects/unicodeobject.c - PyFormatterIter_Type -
|
||||
|
||||
#-----------------------
|
||||
# static builtin structseq
|
||||
|
||||
Objects/floatobject.c - FloatInfoType -
|
||||
Objects/longobject.c - Int_InfoType -
|
||||
Python/errors.c - UnraisableHookArgsType -
|
||||
Python/sysmodule.c - AsyncGenHooksType -
|
||||
Python/sysmodule.c - FlagsType -
|
||||
Python/sysmodule.c - Hash_InfoType -
|
||||
Python/sysmodule.c - VersionInfoType -
|
||||
Python/thread.c - ThreadInfoType -
|
||||
Python/traceback.c - PyTraceBack_Type -
|
||||
|
||||
#-----------------------
|
||||
# builtin exception types
|
||||
|
||||
Objects/exceptions.c - _PyExc_BaseException -
|
||||
Objects/exceptions.c - _PyExc_BaseExceptionGroup -
|
||||
Objects/exceptions.c - _PyExc_UnicodeEncodeError -
|
||||
Objects/exceptions.c - _PyExc_UnicodeDecodeError -
|
||||
Objects/exceptions.c - _PyExc_UnicodeTranslateError -
|
||||
|
@ -197,9 +214,11 @@ Objects/exceptions.c - _PyExc_ImportWarning -
|
|||
Objects/exceptions.c - _PyExc_UnicodeWarning -
|
||||
Objects/exceptions.c - _PyExc_BytesWarning -
|
||||
Objects/exceptions.c - _PyExc_ResourceWarning -
|
||||
Objects/exceptions.c - _PyExc_EncodingWarning -
|
||||
Objects/exceptions.c - PyExc_EnvironmentError -
|
||||
Objects/exceptions.c - PyExc_IOError -
|
||||
Objects/exceptions.c - PyExc_BaseException -
|
||||
Objects/exceptions.c - PyExc_BaseExceptionGroup -
|
||||
Objects/exceptions.c - PyExc_Exception -
|
||||
Objects/exceptions.c - PyExc_TypeError -
|
||||
Objects/exceptions.c - PyExc_StopAsyncIteration -
|
||||
|
@ -263,6 +282,7 @@ Objects/exceptions.c - PyExc_ImportWarning -
|
|||
Objects/exceptions.c - PyExc_UnicodeWarning -
|
||||
Objects/exceptions.c - PyExc_BytesWarning -
|
||||
Objects/exceptions.c - PyExc_ResourceWarning -
|
||||
Objects/exceptions.c - PyExc_EncodingWarning -
|
||||
|
||||
#-----------------------
|
||||
# singletons
|
||||
|
@ -354,8 +374,6 @@ Objects/unicodeobject.c - static_strings -
|
|||
# other
|
||||
|
||||
# initialized once
|
||||
Objects/exceptions.c - PyExc_BaseExceptionGroup -
|
||||
Objects/exceptions.c - PyExc_EncodingWarning -
|
||||
# XXX This should have been found by the analyzer but wasn't:
|
||||
Python/context.c - _token_missing -
|
||||
# XXX This should have been found by the analyzer but wasn't:
|
||||
|
|
Can't render this file because it has a wrong number of fields in line 4.
|
Loading…
Add table
Add a link
Reference in a new issue