mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-28 00:02:28 +00:00
python-3.6.zip added from Github
README.cosmo contains the necessary links.
This commit is contained in:
parent
75fc601ff5
commit
0c4c56ff39
4219 changed files with 1968626 additions and 0 deletions
4384
third_party/python/Tools/clinic/clinic.py
vendored
Executable file
4384
third_party/python/Tools/clinic/clinic.py
vendored
Executable file
File diff suppressed because it is too large
Load diff
791
third_party/python/Tools/clinic/clinic_test.py
vendored
Normal file
791
third_party/python/Tools/clinic/clinic_test.py
vendored
Normal file
|
@ -0,0 +1,791 @@
|
|||
# Argument Clinic
|
||||
# Copyright 2012-2013 by Larry Hastings.
|
||||
# Licensed to the PSF under a contributor agreement.
|
||||
#
|
||||
|
||||
import clinic
|
||||
from clinic import DSLParser
|
||||
import collections
|
||||
import inspect
|
||||
from test import support
|
||||
import sys
|
||||
import unittest
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class FakeConverter:
|
||||
def __init__(self, name, args):
|
||||
self.name = name
|
||||
self.args = args
|
||||
|
||||
|
||||
class FakeConverterFactory:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __call__(self, name, default, **kwargs):
|
||||
return FakeConverter(self.name, kwargs)
|
||||
|
||||
|
||||
class FakeConvertersDict:
|
||||
def __init__(self):
|
||||
self.used_converters = {}
|
||||
|
||||
def get(self, name, default):
|
||||
return self.used_converters.setdefault(name, FakeConverterFactory(name))
|
||||
|
||||
clinic.Clinic.presets_text = ''
|
||||
c = clinic.Clinic(language='C')
|
||||
|
||||
class FakeClinic:
|
||||
def __init__(self):
|
||||
self.converters = FakeConvertersDict()
|
||||
self.legacy_converters = FakeConvertersDict()
|
||||
self.language = clinic.CLanguage(None)
|
||||
self.filename = None
|
||||
self.block_parser = clinic.BlockParser('', self.language)
|
||||
self.modules = collections.OrderedDict()
|
||||
self.classes = collections.OrderedDict()
|
||||
clinic.clinic = self
|
||||
self.name = "FakeClinic"
|
||||
self.line_prefix = self.line_suffix = ''
|
||||
self.destinations = {}
|
||||
self.add_destination("block", "buffer")
|
||||
self.add_destination("file", "buffer")
|
||||
self.add_destination("suppress", "suppress")
|
||||
d = self.destinations.get
|
||||
self.field_destinations = collections.OrderedDict((
|
||||
('docstring_prototype', d('suppress')),
|
||||
('docstring_definition', d('block')),
|
||||
('methoddef_define', d('block')),
|
||||
('impl_prototype', d('block')),
|
||||
('parser_prototype', d('suppress')),
|
||||
('parser_definition', d('block')),
|
||||
('impl_definition', d('block')),
|
||||
))
|
||||
|
||||
def get_destination(self, name):
|
||||
d = self.destinations.get(name)
|
||||
if not d:
|
||||
sys.exit("Destination does not exist: " + repr(name))
|
||||
return d
|
||||
|
||||
def add_destination(self, name, type, *args):
|
||||
if name in self.destinations:
|
||||
sys.exit("Destination already exists: " + repr(name))
|
||||
self.destinations[name] = clinic.Destination(name, type, self, *args)
|
||||
|
||||
def is_directive(self, name):
|
||||
return name == "module"
|
||||
|
||||
def directive(self, name, args):
|
||||
self.called_directives[name] = args
|
||||
|
||||
_module_and_class = clinic.Clinic._module_and_class
|
||||
|
||||
class ClinicWholeFileTest(TestCase):
|
||||
def test_eol(self):
|
||||
# regression test:
|
||||
# clinic's block parser didn't recognize
|
||||
# the "end line" for the block if it
|
||||
# didn't end in "\n" (as in, the last)
|
||||
# byte of the file was '/'.
|
||||
# so it would spit out an end line for you.
|
||||
# and since you really already had one,
|
||||
# the last line of the block got corrupted.
|
||||
c = clinic.Clinic(clinic.CLanguage(None))
|
||||
raw = "/*[clinic]\nfoo\n[clinic]*/"
|
||||
cooked = c.parse(raw).splitlines()
|
||||
end_line = cooked[2].rstrip()
|
||||
# this test is redundant, it's just here explicitly to catch
|
||||
# the regression test so we don't forget what it looked like
|
||||
self.assertNotEqual(end_line, "[clinic]*/[clinic]*/")
|
||||
self.assertEqual(end_line, "[clinic]*/")
|
||||
|
||||
|
||||
|
||||
class ClinicGroupPermuterTest(TestCase):
|
||||
def _test(self, l, m, r, output):
|
||||
computed = clinic.permute_optional_groups(l, m, r)
|
||||
self.assertEqual(output, computed)
|
||||
|
||||
def test_range(self):
|
||||
self._test([['start']], ['stop'], [['step']],
|
||||
(
|
||||
('stop',),
|
||||
('start', 'stop',),
|
||||
('start', 'stop', 'step',),
|
||||
))
|
||||
|
||||
def test_add_window(self):
|
||||
self._test([['x', 'y']], ['ch'], [['attr']],
|
||||
(
|
||||
('ch',),
|
||||
('ch', 'attr'),
|
||||
('x', 'y', 'ch',),
|
||||
('x', 'y', 'ch', 'attr'),
|
||||
))
|
||||
|
||||
def test_ludicrous(self):
|
||||
self._test([['a1', 'a2', 'a3'], ['b1', 'b2']], ['c1'], [['d1', 'd2'], ['e1', 'e2', 'e3']],
|
||||
(
|
||||
('c1',),
|
||||
('b1', 'b2', 'c1'),
|
||||
('b1', 'b2', 'c1', 'd1', 'd2'),
|
||||
('a1', 'a2', 'a3', 'b1', 'b2', 'c1'),
|
||||
('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2'),
|
||||
('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2', 'e1', 'e2', 'e3'),
|
||||
))
|
||||
|
||||
def test_right_only(self):
|
||||
self._test([], [], [['a'],['b'],['c']],
|
||||
(
|
||||
(),
|
||||
('a',),
|
||||
('a', 'b'),
|
||||
('a', 'b', 'c')
|
||||
))
|
||||
|
||||
def test_have_left_options_but_required_is_empty(self):
|
||||
def fn():
|
||||
clinic.permute_optional_groups(['a'], [], [])
|
||||
self.assertRaises(AssertionError, fn)
|
||||
|
||||
|
||||
class ClinicLinearFormatTest(TestCase):
|
||||
def _test(self, input, output, **kwargs):
|
||||
computed = clinic.linear_format(input, **kwargs)
|
||||
self.assertEqual(output, computed)
|
||||
|
||||
def test_empty_strings(self):
|
||||
self._test('', '')
|
||||
|
||||
def test_solo_newline(self):
|
||||
self._test('\n', '\n')
|
||||
|
||||
def test_no_substitution(self):
|
||||
self._test("""
|
||||
abc
|
||||
""", """
|
||||
abc
|
||||
""")
|
||||
|
||||
def test_empty_substitution(self):
|
||||
self._test("""
|
||||
abc
|
||||
{name}
|
||||
def
|
||||
""", """
|
||||
abc
|
||||
def
|
||||
""", name='')
|
||||
|
||||
def test_single_line_substitution(self):
|
||||
self._test("""
|
||||
abc
|
||||
{name}
|
||||
def
|
||||
""", """
|
||||
abc
|
||||
GARGLE
|
||||
def
|
||||
""", name='GARGLE')
|
||||
|
||||
def test_multiline_substitution(self):
|
||||
self._test("""
|
||||
abc
|
||||
{name}
|
||||
def
|
||||
""", """
|
||||
abc
|
||||
bingle
|
||||
bungle
|
||||
|
||||
def
|
||||
""", name='bingle\nbungle\n')
|
||||
|
||||
class InertParser:
|
||||
def __init__(self, clinic):
|
||||
pass
|
||||
|
||||
def parse(self, block):
|
||||
pass
|
||||
|
||||
class CopyParser:
|
||||
def __init__(self, clinic):
|
||||
pass
|
||||
|
||||
def parse(self, block):
|
||||
block.output = block.input
|
||||
|
||||
|
||||
class ClinicBlockParserTest(TestCase):
|
||||
def _test(self, input, output):
|
||||
language = clinic.CLanguage(None)
|
||||
|
||||
blocks = list(clinic.BlockParser(input, language))
|
||||
writer = clinic.BlockPrinter(language)
|
||||
for block in blocks:
|
||||
writer.print_block(block)
|
||||
output = writer.f.getvalue()
|
||||
assert output == input, "output != input!\n\noutput " + repr(output) + "\n\n input " + repr(input)
|
||||
|
||||
def round_trip(self, input):
|
||||
return self._test(input, input)
|
||||
|
||||
def test_round_trip_1(self):
|
||||
self.round_trip("""
|
||||
verbatim text here
|
||||
lah dee dah
|
||||
""")
|
||||
def test_round_trip_2(self):
|
||||
self.round_trip("""
|
||||
verbatim text here
|
||||
lah dee dah
|
||||
/*[inert]
|
||||
abc
|
||||
[inert]*/
|
||||
def
|
||||
/*[inert checksum: 7b18d017f89f61cf17d47f92749ea6930a3f1deb]*/
|
||||
xyz
|
||||
""")
|
||||
|
||||
def _test_clinic(self, input, output):
|
||||
language = clinic.CLanguage(None)
|
||||
c = clinic.Clinic(language)
|
||||
c.parsers['inert'] = InertParser(c)
|
||||
c.parsers['copy'] = CopyParser(c)
|
||||
computed = c.parse(input)
|
||||
self.assertEqual(output, computed)
|
||||
|
||||
def test_clinic_1(self):
|
||||
self._test_clinic("""
|
||||
verbatim text here
|
||||
lah dee dah
|
||||
/*[copy input]
|
||||
def
|
||||
[copy start generated code]*/
|
||||
abc
|
||||
/*[copy end generated code: output=03cfd743661f0797 input=7b18d017f89f61cf]*/
|
||||
xyz
|
||||
""", """
|
||||
verbatim text here
|
||||
lah dee dah
|
||||
/*[copy input]
|
||||
def
|
||||
[copy start generated code]*/
|
||||
def
|
||||
/*[copy end generated code: output=7b18d017f89f61cf input=7b18d017f89f61cf]*/
|
||||
xyz
|
||||
""")
|
||||
|
||||
|
||||
class ClinicParserTest(TestCase):
|
||||
def test_trivial(self):
|
||||
parser = DSLParser(FakeClinic())
|
||||
block = clinic.Block("module os\nos.access")
|
||||
parser.parse(block)
|
||||
module, function = block.signatures
|
||||
self.assertEqual("access", function.name)
|
||||
self.assertEqual("os", module.name)
|
||||
|
||||
def test_ignore_line(self):
|
||||
block = self.parse("#\nmodule os\nos.access")
|
||||
module, function = block.signatures
|
||||
self.assertEqual("access", function.name)
|
||||
self.assertEqual("os", module.name)
|
||||
|
||||
def test_param(self):
|
||||
function = self.parse_function("module os\nos.access\n path: int")
|
||||
self.assertEqual("access", function.name)
|
||||
self.assertEqual(2, len(function.parameters))
|
||||
p = function.parameters['path']
|
||||
self.assertEqual('path', p.name)
|
||||
self.assertIsInstance(p.converter, clinic.int_converter)
|
||||
|
||||
def test_param_default(self):
|
||||
function = self.parse_function("module os\nos.access\n follow_symlinks: bool = True")
|
||||
p = function.parameters['follow_symlinks']
|
||||
self.assertEqual(True, p.default)
|
||||
|
||||
def test_param_with_continuations(self):
|
||||
function = self.parse_function("module os\nos.access\n follow_symlinks: \\\n bool \\\n =\\\n True")
|
||||
p = function.parameters['follow_symlinks']
|
||||
self.assertEqual(True, p.default)
|
||||
|
||||
def test_param_default_expression(self):
|
||||
function = self.parse_function("module os\nos.access\n follow_symlinks: int(c_default='MAXSIZE') = sys.maxsize")
|
||||
p = function.parameters['follow_symlinks']
|
||||
self.assertEqual(sys.maxsize, p.default)
|
||||
self.assertEqual("MAXSIZE", p.converter.c_default)
|
||||
|
||||
s = self.parse_function_should_fail("module os\nos.access\n follow_symlinks: int = sys.maxsize")
|
||||
self.assertEqual(s, "Error on line 0:\nWhen you specify a named constant ('sys.maxsize') as your default value,\nyou MUST specify a valid c_default.\n")
|
||||
|
||||
def test_param_no_docstring(self):
|
||||
function = self.parse_function("""
|
||||
module os
|
||||
os.access
|
||||
follow_symlinks: bool = True
|
||||
something_else: str = ''""")
|
||||
p = function.parameters['follow_symlinks']
|
||||
self.assertEqual(3, len(function.parameters))
|
||||
self.assertIsInstance(function.parameters['something_else'].converter, clinic.str_converter)
|
||||
|
||||
def test_param_default_parameters_out_of_order(self):
|
||||
s = self.parse_function_should_fail("""
|
||||
module os
|
||||
os.access
|
||||
follow_symlinks: bool = True
|
||||
something_else: str""")
|
||||
self.assertEqual(s, """Error on line 0:
|
||||
Can't have a parameter without a default ('something_else')
|
||||
after a parameter with a default!
|
||||
""")
|
||||
|
||||
def disabled_test_converter_arguments(self):
|
||||
function = self.parse_function("module os\nos.access\n path: path_t(allow_fd=1)")
|
||||
p = function.parameters['path']
|
||||
self.assertEqual(1, p.converter.args['allow_fd'])
|
||||
|
||||
def test_function_docstring(self):
|
||||
function = self.parse_function("""
|
||||
module os
|
||||
os.stat as os_stat_fn
|
||||
|
||||
path: str
|
||||
Path to be examined
|
||||
|
||||
Perform a stat system call on the given path.""")
|
||||
self.assertEqual("""
|
||||
stat($module, /, path)
|
||||
--
|
||||
|
||||
Perform a stat system call on the given path.
|
||||
|
||||
path
|
||||
Path to be examined
|
||||
""".strip(), function.docstring)
|
||||
|
||||
def test_explicit_parameters_in_docstring(self):
|
||||
function = self.parse_function("""
|
||||
module foo
|
||||
foo.bar
|
||||
x: int
|
||||
Documentation for x.
|
||||
y: int
|
||||
|
||||
This is the documentation for foo.
|
||||
|
||||
Okay, we're done here.
|
||||
""")
|
||||
self.assertEqual("""
|
||||
bar($module, /, x, y)
|
||||
--
|
||||
|
||||
This is the documentation for foo.
|
||||
|
||||
x
|
||||
Documentation for x.
|
||||
|
||||
Okay, we're done here.
|
||||
""".strip(), function.docstring)
|
||||
|
||||
def test_parser_regression_special_character_in_parameter_column_of_docstring_first_line(self):
|
||||
function = self.parse_function("""
|
||||
module os
|
||||
os.stat
|
||||
path: str
|
||||
This/used to break Clinic!
|
||||
""")
|
||||
self.assertEqual("stat($module, /, path)\n--\n\nThis/used to break Clinic!", function.docstring)
|
||||
|
||||
def test_c_name(self):
|
||||
function = self.parse_function("module os\nos.stat as os_stat_fn")
|
||||
self.assertEqual("os_stat_fn", function.c_basename)
|
||||
|
||||
def test_return_converter(self):
|
||||
function = self.parse_function("module os\nos.stat -> int")
|
||||
self.assertIsInstance(function.return_converter, clinic.int_return_converter)
|
||||
|
||||
def test_star(self):
|
||||
function = self.parse_function("module os\nos.access\n *\n follow_symlinks: bool = True")
|
||||
p = function.parameters['follow_symlinks']
|
||||
self.assertEqual(inspect.Parameter.KEYWORD_ONLY, p.kind)
|
||||
self.assertEqual(0, p.group)
|
||||
|
||||
def test_group(self):
|
||||
function = self.parse_function("module window\nwindow.border\n [\n ls : int\n ]\n /\n")
|
||||
p = function.parameters['ls']
|
||||
self.assertEqual(1, p.group)
|
||||
|
||||
def test_left_group(self):
|
||||
function = self.parse_function("""
|
||||
module curses
|
||||
curses.addch
|
||||
[
|
||||
y: int
|
||||
Y-coordinate.
|
||||
x: int
|
||||
X-coordinate.
|
||||
]
|
||||
ch: char
|
||||
Character to add.
|
||||
[
|
||||
attr: long
|
||||
Attributes for the character.
|
||||
]
|
||||
/
|
||||
""")
|
||||
for name, group in (
|
||||
('y', -1), ('x', -1),
|
||||
('ch', 0),
|
||||
('attr', 1),
|
||||
):
|
||||
p = function.parameters[name]
|
||||
self.assertEqual(p.group, group)
|
||||
self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
|
||||
self.assertEqual(function.docstring.strip(), """
|
||||
addch([y, x,] ch, [attr])
|
||||
|
||||
|
||||
y
|
||||
Y-coordinate.
|
||||
x
|
||||
X-coordinate.
|
||||
ch
|
||||
Character to add.
|
||||
attr
|
||||
Attributes for the character.
|
||||
""".strip())
|
||||
|
||||
def test_nested_groups(self):
|
||||
function = self.parse_function("""
|
||||
module curses
|
||||
curses.imaginary
|
||||
[
|
||||
[
|
||||
y1: int
|
||||
Y-coordinate.
|
||||
y2: int
|
||||
Y-coordinate.
|
||||
]
|
||||
x1: int
|
||||
X-coordinate.
|
||||
x2: int
|
||||
X-coordinate.
|
||||
]
|
||||
ch: char
|
||||
Character to add.
|
||||
[
|
||||
attr1: long
|
||||
Attributes for the character.
|
||||
attr2: long
|
||||
Attributes for the character.
|
||||
attr3: long
|
||||
Attributes for the character.
|
||||
[
|
||||
attr4: long
|
||||
Attributes for the character.
|
||||
attr5: long
|
||||
Attributes for the character.
|
||||
attr6: long
|
||||
Attributes for the character.
|
||||
]
|
||||
]
|
||||
/
|
||||
""")
|
||||
for name, group in (
|
||||
('y1', -2), ('y2', -2),
|
||||
('x1', -1), ('x2', -1),
|
||||
('ch', 0),
|
||||
('attr1', 1), ('attr2', 1), ('attr3', 1),
|
||||
('attr4', 2), ('attr5', 2), ('attr6', 2),
|
||||
):
|
||||
p = function.parameters[name]
|
||||
self.assertEqual(p.group, group)
|
||||
self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
|
||||
|
||||
self.assertEqual(function.docstring.strip(), """
|
||||
imaginary([[y1, y2,] x1, x2,] ch, [attr1, attr2, attr3, [attr4, attr5,
|
||||
attr6]])
|
||||
|
||||
|
||||
y1
|
||||
Y-coordinate.
|
||||
y2
|
||||
Y-coordinate.
|
||||
x1
|
||||
X-coordinate.
|
||||
x2
|
||||
X-coordinate.
|
||||
ch
|
||||
Character to add.
|
||||
attr1
|
||||
Attributes for the character.
|
||||
attr2
|
||||
Attributes for the character.
|
||||
attr3
|
||||
Attributes for the character.
|
||||
attr4
|
||||
Attributes for the character.
|
||||
attr5
|
||||
Attributes for the character.
|
||||
attr6
|
||||
Attributes for the character.
|
||||
""".strip())
|
||||
|
||||
def parse_function_should_fail(self, s):
|
||||
with support.captured_stdout() as stdout:
|
||||
with self.assertRaises(SystemExit):
|
||||
self.parse_function(s)
|
||||
return stdout.getvalue()
|
||||
|
||||
def test_disallowed_grouping__two_top_groups_on_left(self):
|
||||
s = self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.two_top_groups_on_left
|
||||
[
|
||||
group1 : int
|
||||
]
|
||||
[
|
||||
group2 : int
|
||||
]
|
||||
param: int
|
||||
""")
|
||||
self.assertEqual(s,
|
||||
('Error on line 0:\n'
|
||||
'Function two_top_groups_on_left has an unsupported group configuration. (Unexpected state 2.b)\n'))
|
||||
|
||||
def test_disallowed_grouping__two_top_groups_on_right(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.two_top_groups_on_right
|
||||
param: int
|
||||
[
|
||||
group1 : int
|
||||
]
|
||||
[
|
||||
group2 : int
|
||||
]
|
||||
""")
|
||||
|
||||
def test_disallowed_grouping__parameter_after_group_on_right(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.parameter_after_group_on_right
|
||||
param: int
|
||||
[
|
||||
[
|
||||
group1 : int
|
||||
]
|
||||
group2 : int
|
||||
]
|
||||
""")
|
||||
|
||||
def test_disallowed_grouping__group_after_parameter_on_left(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.group_after_parameter_on_left
|
||||
[
|
||||
group2 : int
|
||||
[
|
||||
group1 : int
|
||||
]
|
||||
]
|
||||
param: int
|
||||
""")
|
||||
|
||||
def test_disallowed_grouping__empty_group_on_left(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.empty_group
|
||||
[
|
||||
[
|
||||
]
|
||||
group2 : int
|
||||
]
|
||||
param: int
|
||||
""")
|
||||
|
||||
def test_disallowed_grouping__empty_group_on_right(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.empty_group
|
||||
param: int
|
||||
[
|
||||
[
|
||||
]
|
||||
group2 : int
|
||||
]
|
||||
""")
|
||||
|
||||
def test_no_parameters(self):
|
||||
function = self.parse_function("""
|
||||
module foo
|
||||
foo.bar
|
||||
|
||||
Docstring
|
||||
|
||||
""")
|
||||
self.assertEqual("bar($module, /)\n--\n\nDocstring", function.docstring)
|
||||
self.assertEqual(1, len(function.parameters)) # self!
|
||||
|
||||
def test_init_with_no_parameters(self):
|
||||
function = self.parse_function("""
|
||||
module foo
|
||||
class foo.Bar "unused" "notneeded"
|
||||
foo.Bar.__init__
|
||||
|
||||
Docstring
|
||||
|
||||
""", signatures_in_block=3, function_index=2)
|
||||
# self is not in the signature
|
||||
self.assertEqual("Bar()\n--\n\nDocstring", function.docstring)
|
||||
# but it *is* a parameter
|
||||
self.assertEqual(1, len(function.parameters))
|
||||
|
||||
def test_illegal_module_line(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.bar => int
|
||||
/
|
||||
""")
|
||||
|
||||
def test_illegal_c_basename(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.bar as 935
|
||||
/
|
||||
""")
|
||||
|
||||
def test_single_star(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.bar
|
||||
*
|
||||
*
|
||||
""")
|
||||
|
||||
def test_parameters_required_after_star_without_initial_parameters_or_docstring(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.bar
|
||||
*
|
||||
""")
|
||||
|
||||
def test_parameters_required_after_star_without_initial_parameters_with_docstring(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.bar
|
||||
*
|
||||
Docstring here.
|
||||
""")
|
||||
|
||||
def test_parameters_required_after_star_with_initial_parameters_without_docstring(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.bar
|
||||
this: int
|
||||
*
|
||||
""")
|
||||
|
||||
def test_parameters_required_after_star_with_initial_parameters_and_docstring(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.bar
|
||||
this: int
|
||||
*
|
||||
Docstring.
|
||||
""")
|
||||
|
||||
def test_single_slash(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.bar
|
||||
/
|
||||
/
|
||||
""")
|
||||
|
||||
def test_mix_star_and_slash(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.bar
|
||||
x: int
|
||||
y: int
|
||||
*
|
||||
z: int
|
||||
/
|
||||
""")
|
||||
|
||||
def test_parameters_not_permitted_after_slash_for_now(self):
|
||||
self.parse_function_should_fail("""
|
||||
module foo
|
||||
foo.bar
|
||||
/
|
||||
x: int
|
||||
""")
|
||||
|
||||
def test_function_not_at_column_0(self):
|
||||
function = self.parse_function("""
|
||||
module foo
|
||||
foo.bar
|
||||
x: int
|
||||
Nested docstring here, goeth.
|
||||
*
|
||||
y: str
|
||||
Not at column 0!
|
||||
""")
|
||||
self.assertEqual("""
|
||||
bar($module, /, x, *, y)
|
||||
--
|
||||
|
||||
Not at column 0!
|
||||
|
||||
x
|
||||
Nested docstring here, goeth.
|
||||
""".strip(), function.docstring)
|
||||
|
||||
def test_directive(self):
|
||||
c = FakeClinic()
|
||||
parser = DSLParser(c)
|
||||
parser.flag = False
|
||||
parser.directives['setflag'] = lambda : setattr(parser, 'flag', True)
|
||||
block = clinic.Block("setflag")
|
||||
parser.parse(block)
|
||||
self.assertTrue(parser.flag)
|
||||
|
||||
def test_legacy_converters(self):
|
||||
block = self.parse('module os\nos.access\n path: "s"')
|
||||
module, function = block.signatures
|
||||
self.assertIsInstance((function.parameters['path']).converter, clinic.str_converter)
|
||||
|
||||
def parse(self, text):
|
||||
c = FakeClinic()
|
||||
parser = DSLParser(c)
|
||||
block = clinic.Block(text)
|
||||
parser.parse(block)
|
||||
return block
|
||||
|
||||
def parse_function(self, text, signatures_in_block=2, function_index=1):
|
||||
block = self.parse(text)
|
||||
s = block.signatures
|
||||
self.assertEqual(len(s), signatures_in_block)
|
||||
assert isinstance(s[0], clinic.Module)
|
||||
assert isinstance(s[function_index], clinic.Function)
|
||||
return s[function_index]
|
||||
|
||||
def test_scaffolding(self):
|
||||
# test repr on special values
|
||||
self.assertEqual(repr(clinic.unspecified), '<Unspecified>')
|
||||
self.assertEqual(repr(clinic.NULL), '<Null>')
|
||||
|
||||
# test that fail fails
|
||||
with support.captured_stdout() as stdout:
|
||||
with self.assertRaises(SystemExit):
|
||||
clinic.fail('The igloos are melting!', filename='clown.txt', line_number=69)
|
||||
self.assertEqual(stdout.getvalue(), 'Error in file "clown.txt" on line 69:\nThe igloos are melting!\n')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
191
third_party/python/Tools/clinic/cpp.py
vendored
Normal file
191
third_party/python/Tools/clinic/cpp.py
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
import re
|
||||
import sys
|
||||
|
||||
def negate(condition):
|
||||
"""
|
||||
Returns a CPP conditional that is the opposite of the conditional passed in.
|
||||
"""
|
||||
if condition.startswith('!'):
|
||||
return condition[1:]
|
||||
return "!" + condition
|
||||
|
||||
class Monitor:
|
||||
"""
|
||||
A simple C preprocessor that scans C source and computes, line by line,
|
||||
what the current C preprocessor #if state is.
|
||||
|
||||
Doesn't handle everything--for example, if you have /* inside a C string,
|
||||
without a matching */ (also inside a C string), or with a */ inside a C
|
||||
string but on another line and with preprocessor macros in between...
|
||||
the parser will get lost.
|
||||
|
||||
Anyway this implementation seems to work well enough for the CPython sources.
|
||||
"""
|
||||
|
||||
is_a_simple_defined = re.compile(r'^defined\s*\(\s*[A-Za-z0-9_]+\s*\)$').match
|
||||
|
||||
def __init__(self, filename=None, *, verbose=False):
|
||||
self.stack = []
|
||||
self.in_comment = False
|
||||
self.continuation = None
|
||||
self.line_number = 0
|
||||
self.filename = filename
|
||||
self.verbose = verbose
|
||||
|
||||
def __repr__(self):
|
||||
return ''.join((
|
||||
'<Monitor ',
|
||||
str(id(self)),
|
||||
" line=", str(self.line_number),
|
||||
" condition=", repr(self.condition()),
|
||||
">"))
|
||||
|
||||
def status(self):
|
||||
return str(self.line_number).rjust(4) + ": " + self.condition()
|
||||
|
||||
def condition(self):
|
||||
"""
|
||||
Returns the current preprocessor state, as a single #if condition.
|
||||
"""
|
||||
return " && ".join(condition for token, condition in self.stack)
|
||||
|
||||
def fail(self, *a):
|
||||
if self.filename:
|
||||
filename = " " + self.filename
|
||||
else:
|
||||
filename = ''
|
||||
print("Error at" + filename, "line", self.line_number, ":")
|
||||
print(" ", ' '.join(str(x) for x in a))
|
||||
sys.exit(-1)
|
||||
|
||||
def close(self):
|
||||
if self.stack:
|
||||
self.fail("Ended file while still in a preprocessor conditional block!")
|
||||
|
||||
def write(self, s):
|
||||
for line in s.split("\n"):
|
||||
self.writeline(line)
|
||||
|
||||
def writeline(self, line):
|
||||
self.line_number += 1
|
||||
line = line.strip()
|
||||
|
||||
def pop_stack():
|
||||
if not self.stack:
|
||||
self.fail("#" + token + " without matching #if / #ifdef / #ifndef!")
|
||||
return self.stack.pop()
|
||||
|
||||
if self.continuation:
|
||||
line = self.continuation + line
|
||||
self.continuation = None
|
||||
|
||||
if not line:
|
||||
return
|
||||
|
||||
if line.endswith('\\'):
|
||||
self.continuation = line[:-1].rstrip() + " "
|
||||
return
|
||||
|
||||
# we have to ignore preprocessor commands inside comments
|
||||
#
|
||||
# we also have to handle this:
|
||||
# /* start
|
||||
# ...
|
||||
# */ /* <-- tricky!
|
||||
# ...
|
||||
# */
|
||||
# and this:
|
||||
# /* start
|
||||
# ...
|
||||
# */ /* also tricky! */
|
||||
if self.in_comment:
|
||||
if '*/' in line:
|
||||
# snip out the comment and continue
|
||||
#
|
||||
# GCC allows
|
||||
# /* comment
|
||||
# */ #include <stdio.h>
|
||||
# maybe other compilers too?
|
||||
_, _, line = line.partition('*/')
|
||||
self.in_comment = False
|
||||
|
||||
while True:
|
||||
if '/*' in line:
|
||||
if self.in_comment:
|
||||
self.fail("Nested block comment!")
|
||||
|
||||
before, _, remainder = line.partition('/*')
|
||||
comment, comment_ends, after = remainder.partition('*/')
|
||||
if comment_ends:
|
||||
# snip out the comment
|
||||
line = before.rstrip() + ' ' + after.lstrip()
|
||||
continue
|
||||
# comment continues to eol
|
||||
self.in_comment = True
|
||||
line = before.rstrip()
|
||||
break
|
||||
|
||||
# we actually have some // comments
|
||||
# (but block comments take precedence)
|
||||
before, line_comment, comment = line.partition('//')
|
||||
if line_comment:
|
||||
line = before.rstrip()
|
||||
|
||||
if not line.startswith('#'):
|
||||
return
|
||||
|
||||
line = line[1:].lstrip()
|
||||
assert line
|
||||
|
||||
fields = line.split()
|
||||
token = fields[0].lower()
|
||||
condition = ' '.join(fields[1:]).strip()
|
||||
|
||||
if_tokens = {'if', 'ifdef', 'ifndef'}
|
||||
all_tokens = if_tokens | {'elif', 'else', 'endif'}
|
||||
|
||||
if token not in all_tokens:
|
||||
return
|
||||
|
||||
# cheat a little here, to reuse the implementation of if
|
||||
if token == 'elif':
|
||||
pop_stack()
|
||||
token = 'if'
|
||||
|
||||
if token in if_tokens:
|
||||
if not condition:
|
||||
self.fail("Invalid format for #" + token + " line: no argument!")
|
||||
if token == 'if':
|
||||
if not self.is_a_simple_defined(condition):
|
||||
condition = "(" + condition + ")"
|
||||
else:
|
||||
fields = condition.split()
|
||||
if len(fields) != 1:
|
||||
self.fail("Invalid format for #" + token + " line: should be exactly one argument!")
|
||||
symbol = fields[0]
|
||||
condition = 'defined(' + symbol + ')'
|
||||
if token == 'ifndef':
|
||||
condition = '!' + condition
|
||||
|
||||
self.stack.append(("if", condition))
|
||||
if self.verbose:
|
||||
print(self.status())
|
||||
return
|
||||
|
||||
previous_token, previous_condition = pop_stack()
|
||||
|
||||
if token == 'else':
|
||||
self.stack.append(('else', negate(previous_condition)))
|
||||
elif token == 'endif':
|
||||
pass
|
||||
if self.verbose:
|
||||
print(self.status())
|
||||
|
||||
if __name__ == '__main__':
|
||||
for filename in sys.argv[1:]:
|
||||
with open(filename, "rt") as f:
|
||||
cpp = Monitor(filename, verbose=True)
|
||||
print()
|
||||
print(filename)
|
||||
for line_number, line in enumerate(f.read().split('\n'), 1):
|
||||
cpp.writeline(line)
|
Loading…
Add table
Add a link
Reference in a new issue