Skip to content

Commit 7a1a85d

Browse files
gh-94673: [c-analyzer] Add a Script to Identify Static Types (#94989)
issue: #94673
1 parent 0daba82 commit 7a1a85d

File tree

5 files changed

+614
-152
lines changed

5 files changed

+614
-152
lines changed

Objects/exceptions.c

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -600,17 +600,9 @@ StopIteration_traverse(PyStopIterationObject *self, visitproc visit, void *arg)
600600
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
601601
}
602602

603-
ComplexExtendsException(
604-
PyExc_Exception, /* base */
605-
StopIteration, /* name */
606-
StopIteration, /* prefix for *_init, etc */
607-
0, /* new */
608-
0, /* methods */
609-
StopIteration_members, /* members */
610-
0, /* getset */
611-
0, /* str */
612-
"Signal the end from iterator.__next__()."
613-
);
603+
ComplexExtendsException(PyExc_Exception, StopIteration, StopIteration,
604+
0, 0, StopIteration_members, 0, 0,
605+
"Signal the end from iterator.__next__().");
614606

615607

616608
/*

Tools/c-analyzer/c_common/tables.py

Lines changed: 163 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from collections import namedtuple
12
import csv
23
import re
34
import textwrap
@@ -225,45 +226,157 @@ def _normalize_table_file_props(header, sep):
225226
def resolve_columns(specs):
226227
if isinstance(specs, str):
227228
specs = specs.replace(',', ' ').strip().split()
228-
return _resolve_colspecs(specs)
229+
resolved = []
230+
for raw in specs:
231+
column = ColumnSpec.from_raw(raw)
232+
resolved.append(column)
233+
return resolved
229234

230235

231236
def build_table(specs, *, sep=' ', defaultwidth=None):
232237
columns = resolve_columns(specs)
233238
return _build_table(columns, sep=sep, defaultwidth=defaultwidth)
234239

235240

236-
_COLSPEC_RE = re.compile(textwrap.dedent(r'''
237-
^
238-
(?:
239-
\[
240-
(
241-
(?: [^\s\]] [^\]]* )?
242-
[^\s\]]
243-
) # <label>
244-
]
245-
)?
246-
( \w+ ) # <field>
247-
(?:
241+
class ColumnSpec(namedtuple('ColumnSpec', 'field label fmt')):
242+
243+
REGEX = re.compile(textwrap.dedent(r'''
244+
^
248245
(?:
249-
:
250-
( [<^>] ) # <align>
251-
( \d+ ) # <width1>
252-
)
253-
|
246+
\[
247+
(
248+
(?: [^\s\]] [^\]]* )?
249+
[^\s\]]
250+
) # <label>
251+
]
252+
)?
253+
( [-\w]+ ) # <field>
254254
(?:
255255
(?:
256256
:
257-
( \d+ ) # <width2>
258-
)?
257+
( [<^>] ) # <align>
258+
( \d+ )? # <width1>
259+
)
260+
|
259261
(?:
260-
:
261-
( .*? ) # <fmt>
262-
)?
263-
)
264-
)?
265-
$
266-
'''), re.VERBOSE)
262+
(?:
263+
:
264+
( \d+ ) # <width2>
265+
)?
266+
(?:
267+
:
268+
( .*? ) # <fmt>
269+
)?
270+
)
271+
)?
272+
$
273+
'''), re.VERBOSE)
274+
275+
@classmethod
276+
def from_raw(cls, raw):
277+
if not raw:
278+
raise ValueError('missing column spec')
279+
elif isinstance(raw, cls):
280+
return raw
281+
282+
if isinstance(raw, str):
283+
*values, _ = cls._parse(raw)
284+
else:
285+
*values, _ = cls._normalize(raw)
286+
if values is None:
287+
raise ValueError(f'unsupported column spec {raw!r}')
288+
return cls(*values)
289+
290+
@classmethod
291+
def parse(cls, specstr):
292+
parsed = cls._parse(specstr)
293+
if not parsed:
294+
return None
295+
*values, _ = parsed
296+
return cls(*values)
297+
298+
@classmethod
299+
def _parse(cls, specstr):
300+
m = cls.REGEX.match(specstr)
301+
if not m:
302+
return None
303+
(label, field,
304+
align, width1,
305+
width2, fmt,
306+
) = m.groups()
307+
if not label:
308+
label = field
309+
if fmt:
310+
assert not align and not width1, (specstr,)
311+
_parsed = _parse_fmt(fmt)
312+
if not _parsed:
313+
raise NotImplementedError
314+
elif width2:
315+
width, _ = _parsed
316+
if width != int(width2):
317+
raise NotImplementedError(specstr)
318+
elif width2:
319+
fmt = width2
320+
width = int(width2)
321+
else:
322+
assert not fmt, (fmt, specstr)
323+
if align:
324+
width = int(width1) if width1 else len(label)
325+
fmt = f'{align}{width}'
326+
else:
327+
width = None
328+
return field, label, fmt, width
329+
330+
@classmethod
331+
def _normalize(cls, spec):
332+
if len(spec) == 1:
333+
raw, = spec
334+
raise NotImplementedError
335+
return _resolve_column(raw)
336+
337+
if len(spec) == 4:
338+
label, field, width, fmt = spec
339+
if width:
340+
if not fmt:
341+
fmt = str(width)
342+
elif _parse_fmt(fmt)[0] != width:
343+
raise ValueError(f'width mismatch in {spec}')
344+
elif len(raw) == 3:
345+
label, field, fmt = spec
346+
if not field:
347+
label, field = None, label
348+
elif not isinstance(field, str) or not field.isidentifier():
349+
# XXX This doesn't seem right...
350+
fmt = f'{field}:{fmt}' if fmt else field
351+
label, field = None, label
352+
elif len(raw) == 2:
353+
label = None
354+
field, fmt = raw
355+
if not field:
356+
field, fmt = fmt, None
357+
elif not field.isidentifier() or fmt.isidentifier():
358+
label, field = field, fmt
359+
else:
360+
raise NotImplementedError
361+
362+
fmt = f':{fmt}' if fmt else ''
363+
if label:
364+
return cls._parse(f'[{label}]{field}{fmt}')
365+
else:
366+
return cls._parse(f'{field}{fmt}')
367+
368+
@property
369+
def width(self):
370+
if not self.fmt:
371+
return None
372+
parsed = _parse_fmt(self.fmt)
373+
if not parsed:
374+
return None
375+
width, _ = parsed
376+
return width
377+
378+
def resolve_width(self, default=None):
379+
return _resolve_width(self.width, self.fmt, self.label, default)
267380

268381

269382
def _parse_fmt(fmt):
@@ -272,117 +385,45 @@ def _parse_fmt(fmt):
272385
width = fmt[1:]
273386
if width.isdigit():
274387
return int(width), align
275-
return None, None
388+
elif fmt.isdigit():
389+
return int(fmt), '<'
390+
return None
276391

277392

278-
def _parse_colspec(raw):
279-
m = _COLSPEC_RE.match(raw)
280-
if not m:
281-
return None
282-
label, field, align, width1, width2, fmt = m.groups()
283-
if not label:
284-
label = field
285-
if width1:
286-
width = None
287-
fmt = f'{align}{width1}'
288-
elif width2:
289-
width = int(width2)
290-
if fmt:
291-
_width, _ = _parse_fmt(fmt)
292-
if _width == width:
293-
width = None
294-
else:
295-
width = None
296-
return field, label, width, fmt
297-
298-
299-
def _normalize_colspec(spec):
300-
if len(spec) == 1:
301-
raw, = spec
302-
return _resolve_column(raw)
303-
304-
if len(spec) == 4:
305-
label, field, width, fmt = spec
306-
if width:
307-
fmt = f'{width}:{fmt}' if fmt else width
308-
elif len(raw) == 3:
309-
label, field, fmt = spec
310-
if not field:
311-
label, field = None, label
312-
elif not isinstance(field, str) or not field.isidentifier():
313-
fmt = f'{field}:{fmt}' if fmt else field
314-
label, field = None, label
315-
elif len(raw) == 2:
316-
label = None
317-
field, fmt = raw
318-
if not field:
319-
field, fmt = fmt, None
320-
elif not field.isidentifier() or fmt.isidentifier():
321-
label, field = field, fmt
322-
else:
323-
raise NotImplementedError
324-
325-
fmt = f':{fmt}' if fmt else ''
326-
if label:
327-
return _parse_colspec(f'[{label}]{field}{fmt}')
328-
else:
329-
return _parse_colspec(f'{field}{fmt}')
330-
331-
332-
def _resolve_colspec(raw):
333-
if isinstance(raw, str):
334-
spec = _parse_colspec(raw)
335-
else:
336-
spec = _normalize_colspec(raw)
337-
if spec is None:
338-
raise ValueError(f'unsupported column spec {raw!r}')
339-
return spec
340-
341-
342-
def _resolve_colspecs(columns):
343-
parsed = []
344-
for raw in columns:
345-
column = _resolve_colspec(raw)
346-
parsed.append(column)
347-
return parsed
348-
349-
350-
def _resolve_width(spec, defaultwidth):
351-
_, label, width, fmt = spec
393+
def _resolve_width(width, fmt, label, default):
352394
if width:
353395
if not isinstance(width, int):
354396
raise NotImplementedError
355397
return width
356-
elif width and fmt:
357-
width, _ = _parse_fmt(fmt)
358-
if width:
359-
return width
360-
361-
if not defaultwidth:
398+
elif fmt:
399+
parsed = _parse_fmt(fmt)
400+
if parsed:
401+
width, _ = parsed
402+
if width:
403+
return width
404+
405+
if not default:
362406
return WIDTH
363-
elif not hasattr(defaultwidth, 'get'):
364-
return defaultwidth or WIDTH
365-
366-
defaultwidths = defaultwidth
367-
defaultwidth = defaultwidths.get(None) or WIDTH
368-
return defaultwidths.get(label) or defaultwidth
407+
elif hasattr(default, 'get'):
408+
defaults = default
409+
default = defaults.get(None) or WIDTH
410+
return defaults.get(label) or default
411+
else:
412+
return default or WIDTH
369413

370414

371415
def _build_table(columns, *, sep=' ', defaultwidth=None):
372416
header = []
373417
div = []
374418
rowfmt = []
375419
for spec in columns:
376-
label, field, _, colfmt = spec
377-
width = _resolve_width(spec, defaultwidth)
378-
if colfmt:
379-
colfmt = f':{colfmt}'
380-
else:
381-
colfmt = f':{width}'
420+
width = spec.resolve_width(defaultwidth)
421+
colfmt = spec.fmt
422+
colfmt = f':{spec.fmt}' if spec.fmt else f':{width}'
382423

383-
header.append(f' {{:^{width}}} '.format(label))
424+
header.append(f' {{:^{width}}} '.format(spec.label))
384425
div.append('-' * (width + 2))
385-
rowfmt.append(f' {{{field}{colfmt}}} ')
426+
rowfmt.append(f' {{{spec.field}{colfmt}}} ')
386427
return (
387428
sep.join(header),
388429
sep.join(div),

0 commit comments

Comments
 (0)