Skip to content

Commit

Permalink
pythongh-113317: Finish splitting Argument Clinic into sub-files (pyt…
Browse files Browse the repository at this point in the history
…hon#117513)

Add libclinic.parser module and move the following classes and
functions there:

* Parser
* PythonParser
* create_parser_namespace()

Add libclinic.dsl_parser module and move the following classes,
functions and variables there:

* ConverterArgs
* DSLParser
* FunctionNames
* IndentStack
* ParamState
* StateKeeper
* eval_ast_expr()
* unsupported_special_methods

Add libclinic.app module and move the Clinic class there.

Add libclinic.cli module and move the following functions there:

* create_cli()
* main()
* parse_file()
* run_clinic()
  • Loading branch information
vstinner authored and diegorusso committed Apr 17, 2024
1 parent 7bc2f07 commit 8ce6853
Show file tree
Hide file tree
Showing 10 changed files with 2,238 additions and 2,194 deletions.
104 changes: 56 additions & 48 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,26 @@
test_tools.skip_if_missing('clinic')
with test_tools.imports_under_tool('clinic'):
import libclinic
from libclinic.converters import int_converter, str_converter
from libclinic import ClinicError, unspecified, NULL, fail
from libclinic.converters import int_converter, str_converter, self_converter
from libclinic.function import (
Module, Class, Function, FunctionKind, Parameter,
permute_optional_groups, permute_right_option_groups,
permute_left_option_groups)
import clinic
from clinic import DSLParser
from libclinic.clanguage import CLanguage
from libclinic.converter import converters, legacy_converters
from libclinic.return_converters import return_converters, int_return_converter
from libclinic.block_parser import Block, BlockParser
from libclinic.codegen import BlockPrinter, Destination
from libclinic.dsl_parser import DSLParser
from libclinic.cli import parse_file, Clinic


def _make_clinic(*, filename='clinic_tests', limited_capi=False):
clang = clinic.CLanguage(filename)
c = clinic.Clinic(clang, filename=filename, limited_capi=limited_capi)
c.block_parser = clinic.BlockParser('', clang)
clang = CLanguage(filename)
c = Clinic(clang, filename=filename, limited_capi=limited_capi)
c.block_parser = BlockParser('', clang)
return c


Expand All @@ -47,7 +55,7 @@ def _expect_failure(tc, parser, code, errmsg, *, filename=None, lineno=None,
if strip:
code = code.strip()
errmsg = re.escape(errmsg)
with tc.assertRaisesRegex(clinic.ClinicError, errmsg) as cm:
with tc.assertRaisesRegex(ClinicError, errmsg) as cm:
parser(code)
if filename is not None:
tc.assertEqual(cm.exception.filename, filename)
Expand All @@ -62,12 +70,12 @@ def restore_dict(converters, old_converters):


def save_restore_converters(testcase):
testcase.addCleanup(restore_dict, clinic.converters,
clinic.converters.copy())
testcase.addCleanup(restore_dict, clinic.legacy_converters,
clinic.legacy_converters.copy())
testcase.addCleanup(restore_dict, clinic.return_converters,
clinic.return_converters.copy())
testcase.addCleanup(restore_dict, converters,
converters.copy())
testcase.addCleanup(restore_dict, legacy_converters,
legacy_converters.copy())
testcase.addCleanup(restore_dict, return_converters,
return_converters.copy())


class ClinicWholeFileTest(TestCase):
Expand Down Expand Up @@ -140,11 +148,11 @@ def test_whitespace_before_stop_line(self):
self.expect_failure(raw, err, filename="test.c", lineno=2)

def test_parse_with_body_prefix(self):
clang = clinic.CLanguage(None)
clang = CLanguage(None)
clang.body_prefix = "//"
clang.start_line = "//[{dsl_name} start]"
clang.stop_line = "//[{dsl_name} stop]"
cl = clinic.Clinic(clang, filename="test.c", limited_capi=False)
cl = Clinic(clang, filename="test.c", limited_capi=False)
raw = dedent("""
//[clinic start]
//module test
Expand Down Expand Up @@ -660,8 +668,8 @@ def expect_parsing_failure(
self, *, filename, expected_error, verify=True, output=None
):
errmsg = re.escape(dedent(expected_error).strip())
with self.assertRaisesRegex(clinic.ClinicError, errmsg):
clinic.parse_file(filename, limited_capi=False)
with self.assertRaisesRegex(ClinicError, errmsg):
parse_file(filename, limited_capi=False)

def test_parse_file_no_extension(self) -> None:
self.expect_parsing_failure(
Expand Down Expand Up @@ -782,13 +790,13 @@ def test_multiline_substitution(self):

def test_text_before_block_marker(self):
regex = re.escape("found before '{marker}'")
with self.assertRaisesRegex(clinic.ClinicError, regex):
with self.assertRaisesRegex(ClinicError, regex):
libclinic.linear_format("no text before marker for you! {marker}",
marker="not allowed!")

def test_text_after_block_marker(self):
regex = re.escape("found after '{marker}'")
with self.assertRaisesRegex(clinic.ClinicError, regex):
with self.assertRaisesRegex(ClinicError, regex):
libclinic.linear_format("{marker} no text after marker for you!",
marker="not allowed!")

Expand All @@ -810,10 +818,10 @@ def parse(self, block):

class ClinicBlockParserTest(TestCase):
def _test(self, input, output):
language = clinic.CLanguage(None)
language = CLanguage(None)

blocks = list(clinic.BlockParser(input, language))
writer = clinic.BlockPrinter(language)
blocks = list(BlockParser(input, language))
writer = BlockPrinter(language)
c = _make_clinic()
for block in blocks:
writer.print_block(block, limited_capi=c.limited_capi, header_includes=c.includes)
Expand Down Expand Up @@ -841,8 +849,8 @@ def test_round_trip_2(self):
""")

def _test_clinic(self, input, output):
language = clinic.CLanguage(None)
c = clinic.Clinic(language, filename="file", limited_capi=False)
language = CLanguage(None)
c = Clinic(language, filename="file", limited_capi=False)
c.parsers['inert'] = InertParser(c)
c.parsers['copy'] = CopyParser(c)
computed = c.parse(input)
Expand Down Expand Up @@ -875,16 +883,16 @@ class ClinicParserTest(TestCase):
def parse(self, text):
c = _make_clinic()
parser = DSLParser(c)
block = clinic.Block(text)
block = 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)
assert isinstance(s[0], Module)
assert isinstance(s[function_index], Function)
return s[function_index]

def expect_failure(self, block, err, *,
Expand All @@ -899,7 +907,7 @@ def checkDocstring(self, fn, expected):

def test_trivial(self):
parser = DSLParser(_make_clinic())
block = clinic.Block("""
block = Block("""
module os
os.access
""")
Expand Down Expand Up @@ -1188,7 +1196,7 @@ def test_base_invalid_syntax(self):
Function 'stat' has an invalid parameter declaration:
\s+'invalid syntax: int = 42'
""").strip()
with self.assertRaisesRegex(clinic.ClinicError, err):
with self.assertRaisesRegex(ClinicError, err):
self.parse_function(block)

def test_param_default_invalid_syntax(self):
Expand Down Expand Up @@ -1220,7 +1228,7 @@ def test_return_converter(self):
module os
os.stat -> int
""")
self.assertIsInstance(function.return_converter, clinic.int_return_converter)
self.assertIsInstance(function.return_converter, int_return_converter)

def test_return_converter_invalid_syntax(self):
block = """
Expand Down Expand Up @@ -2036,7 +2044,7 @@ def test_directive(self):
parser = DSLParser(_make_clinic())
parser.flag = False
parser.directives['setflag'] = lambda : setattr(parser, 'flag', True)
block = clinic.Block("setflag")
block = Block("setflag")
parser.parse(block)
self.assertTrue(parser.flag)

Expand Down Expand Up @@ -2301,14 +2309,14 @@ def test_unused_param(self):

def test_scaffolding(self):
# test repr on special values
self.assertEqual(repr(clinic.unspecified), '<Unspecified>')
self.assertEqual(repr(clinic.NULL), '<Null>')
self.assertEqual(repr(unspecified), '<Unspecified>')
self.assertEqual(repr(NULL), '<Null>')

# test that fail fails
with support.captured_stdout() as stdout:
errmsg = 'The igloos are melting'
with self.assertRaisesRegex(clinic.ClinicError, errmsg) as cm:
clinic.fail(errmsg, filename='clown.txt', line_number=69)
with self.assertRaisesRegex(ClinicError, errmsg) as cm:
fail(errmsg, filename='clown.txt', line_number=69)
exc = cm.exception
self.assertEqual(exc.filename, 'clown.txt')
self.assertEqual(exc.lineno, 69)
Expand Down Expand Up @@ -3998,15 +4006,15 @@ def test_suffix_all_lines(self):

class ClinicReprTests(unittest.TestCase):
def test_Block_repr(self):
block = clinic.Block("foo")
block = Block("foo")
expected_repr = "<clinic.Block 'text' input='foo' output=None>"
self.assertEqual(repr(block), expected_repr)

block2 = clinic.Block("bar", "baz", [], "eggs", "spam")
block2 = Block("bar", "baz", [], "eggs", "spam")
expected_repr_2 = "<clinic.Block 'baz' input='bar' output='eggs'>"
self.assertEqual(repr(block2), expected_repr_2)

block3 = clinic.Block(
block3 = Block(
input="longboi_" * 100,
dsl_name="wow_so_long",
signatures=[],
Expand All @@ -4021,47 +4029,47 @@ def test_Block_repr(self):
def test_Destination_repr(self):
c = _make_clinic()

destination = clinic.Destination(
destination = Destination(
"foo", type="file", clinic=c, args=("eggs",)
)
self.assertEqual(
repr(destination), "<clinic.Destination 'foo' type='file' file='eggs'>"
)

destination2 = clinic.Destination("bar", type="buffer", clinic=c)
destination2 = Destination("bar", type="buffer", clinic=c)
self.assertEqual(repr(destination2), "<clinic.Destination 'bar' type='buffer'>")

def test_Module_repr(self):
module = clinic.Module("foo", _make_clinic())
module = Module("foo", _make_clinic())
self.assertRegex(repr(module), r"<clinic.Module 'foo' at \d+>")

def test_Class_repr(self):
cls = clinic.Class("foo", _make_clinic(), None, 'some_typedef', 'some_type_object')
cls = Class("foo", _make_clinic(), None, 'some_typedef', 'some_type_object')
self.assertRegex(repr(cls), r"<clinic.Class 'foo' at \d+>")

def test_FunctionKind_repr(self):
self.assertEqual(
repr(clinic.FunctionKind.INVALID), "<clinic.FunctionKind.INVALID>"
repr(FunctionKind.INVALID), "<clinic.FunctionKind.INVALID>"
)
self.assertEqual(
repr(clinic.FunctionKind.CLASS_METHOD), "<clinic.FunctionKind.CLASS_METHOD>"
repr(FunctionKind.CLASS_METHOD), "<clinic.FunctionKind.CLASS_METHOD>"
)

def test_Function_and_Parameter_reprs(self):
function = clinic.Function(
function = Function(
name='foo',
module=_make_clinic(),
cls=None,
c_basename=None,
full_name='foofoo',
return_converter=clinic.int_return_converter(),
kind=clinic.FunctionKind.METHOD_INIT,
return_converter=int_return_converter(),
kind=FunctionKind.METHOD_INIT,
coexist=False
)
self.assertEqual(repr(function), "<clinic.Function 'foo'>")

converter = clinic.self_converter('bar', 'bar', function)
parameter = clinic.Parameter(
converter = self_converter('bar', 'bar', function)
parameter = Parameter(
"bar",
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
function=function,
Expand Down
Loading

0 comments on commit 8ce6853

Please sign in to comment.