blob: 3d5dc4759d5018ff0bd0d16afab52d09f540f497 [file] [log] [blame]
Larry Hastings31826802013-10-19 00:09:25 -07001# Argument Clinic
2# Copyright 2012-2013 by Larry Hastings.
3# Licensed to the PSF under a contributor agreement.
Larry Hastings31826802013-10-19 00:09:25 -07004
Eric Snowee536b22019-09-11 19:49:45 +01005from test import support, test_tools
Victor Stinner65fc98e2018-09-03 23:17:20 +02006from unittest import TestCase
Larry Hastings31826802013-10-19 00:09:25 -07007import collections
8import inspect
Victor Stinner65fc98e2018-09-03 23:17:20 +02009import os.path
Larry Hastings1abd7082014-01-16 14:15:03 -080010import sys
Larry Hastings31826802013-10-19 00:09:25 -070011import unittest
Victor Stinner65fc98e2018-09-03 23:17:20 +020012
Eric Snowee536b22019-09-11 19:49:45 +010013test_tools.skip_if_missing('clinic')
14with test_tools.imports_under_tool('clinic'):
Victor Stinner65fc98e2018-09-03 23:17:20 +020015 import clinic
16 from clinic import DSLParser
Larry Hastings31826802013-10-19 00:09:25 -070017
Larry Hastings7726ac92014-01-31 22:03:12 -080018
Larry Hastings31826802013-10-19 00:09:25 -070019class FakeConverter:
20 def __init__(self, name, args):
21 self.name = name
22 self.args = args
23
24
25class FakeConverterFactory:
26 def __init__(self, name):
27 self.name = name
28
29 def __call__(self, name, default, **kwargs):
30 return FakeConverter(self.name, kwargs)
31
32
33class FakeConvertersDict:
34 def __init__(self):
35 self.used_converters = {}
36
37 def get(self, name, default):
38 return self.used_converters.setdefault(name, FakeConverterFactory(name))
39
Victor Stinner65fc98e2018-09-03 23:17:20 +020040c = clinic.Clinic(language='C', filename = "file")
Larry Hastingsbebf7352014-01-17 17:47:17 -080041
Larry Hastings31826802013-10-19 00:09:25 -070042class FakeClinic:
43 def __init__(self):
44 self.converters = FakeConvertersDict()
45 self.legacy_converters = FakeConvertersDict()
Larry Hastings7726ac92014-01-31 22:03:12 -080046 self.language = clinic.CLanguage(None)
Larry Hastings31826802013-10-19 00:09:25 -070047 self.filename = None
Victor Stinner65fc98e2018-09-03 23:17:20 +020048 self.destination_buffers = {}
Larry Hastings31826802013-10-19 00:09:25 -070049 self.block_parser = clinic.BlockParser('', self.language)
50 self.modules = collections.OrderedDict()
Larry Hastings7726ac92014-01-31 22:03:12 -080051 self.classes = collections.OrderedDict()
Larry Hastings31826802013-10-19 00:09:25 -070052 clinic.clinic = self
53 self.name = "FakeClinic"
Larry Hastingsbebf7352014-01-17 17:47:17 -080054 self.line_prefix = self.line_suffix = ''
55 self.destinations = {}
56 self.add_destination("block", "buffer")
57 self.add_destination("file", "buffer")
58 self.add_destination("suppress", "suppress")
59 d = self.destinations.get
60 self.field_destinations = collections.OrderedDict((
61 ('docstring_prototype', d('suppress')),
62 ('docstring_definition', d('block')),
63 ('methoddef_define', d('block')),
64 ('impl_prototype', d('block')),
65 ('parser_prototype', d('suppress')),
66 ('parser_definition', d('block')),
67 ('impl_definition', d('block')),
68 ))
69
70 def get_destination(self, name):
71 d = self.destinations.get(name)
72 if not d:
73 sys.exit("Destination does not exist: " + repr(name))
74 return d
75
76 def add_destination(self, name, type, *args):
77 if name in self.destinations:
78 sys.exit("Destination already exists: " + repr(name))
79 self.destinations[name] = clinic.Destination(name, type, self, *args)
Larry Hastings31826802013-10-19 00:09:25 -070080
81 def is_directive(self, name):
82 return name == "module"
83
84 def directive(self, name, args):
85 self.called_directives[name] = args
86
87 _module_and_class = clinic.Clinic._module_and_class
88
Larry Hastings90261132014-01-07 12:21:08 -080089class ClinicWholeFileTest(TestCase):
90 def test_eol(self):
91 # regression test:
92 # clinic's block parser didn't recognize
93 # the "end line" for the block if it
94 # didn't end in "\n" (as in, the last)
95 # byte of the file was '/'.
Martin Pantereb995702016-07-28 01:11:04 +000096 # so it would spit out an end line for you.
Larry Hastings90261132014-01-07 12:21:08 -080097 # and since you really already had one,
98 # the last line of the block got corrupted.
Victor Stinner65fc98e2018-09-03 23:17:20 +020099 c = clinic.Clinic(clinic.CLanguage(None), filename="file")
Larry Hastings90261132014-01-07 12:21:08 -0800100 raw = "/*[clinic]\nfoo\n[clinic]*/"
101 cooked = c.parse(raw).splitlines()
102 end_line = cooked[2].rstrip()
103 # this test is redundant, it's just here explicitly to catch
104 # the regression test so we don't forget what it looked like
105 self.assertNotEqual(end_line, "[clinic]*/[clinic]*/")
106 self.assertEqual(end_line, "[clinic]*/")
107
Larry Hastings31826802013-10-19 00:09:25 -0700108
109
110class ClinicGroupPermuterTest(TestCase):
111 def _test(self, l, m, r, output):
112 computed = clinic.permute_optional_groups(l, m, r)
113 self.assertEqual(output, computed)
114
115 def test_range(self):
116 self._test([['start']], ['stop'], [['step']],
117 (
118 ('stop',),
119 ('start', 'stop',),
120 ('start', 'stop', 'step',),
121 ))
122
123 def test_add_window(self):
124 self._test([['x', 'y']], ['ch'], [['attr']],
125 (
126 ('ch',),
127 ('ch', 'attr'),
128 ('x', 'y', 'ch',),
129 ('x', 'y', 'ch', 'attr'),
130 ))
131
132 def test_ludicrous(self):
133 self._test([['a1', 'a2', 'a3'], ['b1', 'b2']], ['c1'], [['d1', 'd2'], ['e1', 'e2', 'e3']],
134 (
135 ('c1',),
136 ('b1', 'b2', 'c1'),
137 ('b1', 'b2', 'c1', 'd1', 'd2'),
138 ('a1', 'a2', 'a3', 'b1', 'b2', 'c1'),
139 ('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2'),
140 ('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2', 'e1', 'e2', 'e3'),
141 ))
142
143 def test_right_only(self):
144 self._test([], [], [['a'],['b'],['c']],
145 (
146 (),
147 ('a',),
148 ('a', 'b'),
149 ('a', 'b', 'c')
150 ))
151
152 def test_have_left_options_but_required_is_empty(self):
153 def fn():
154 clinic.permute_optional_groups(['a'], [], [])
155 self.assertRaises(AssertionError, fn)
156
157
158class ClinicLinearFormatTest(TestCase):
159 def _test(self, input, output, **kwargs):
160 computed = clinic.linear_format(input, **kwargs)
161 self.assertEqual(output, computed)
162
163 def test_empty_strings(self):
164 self._test('', '')
165
166 def test_solo_newline(self):
167 self._test('\n', '\n')
168
169 def test_no_substitution(self):
170 self._test("""
171 abc
172 """, """
173 abc
174 """)
175
176 def test_empty_substitution(self):
177 self._test("""
178 abc
179 {name}
180 def
181 """, """
182 abc
183 def
184 """, name='')
185
186 def test_single_line_substitution(self):
187 self._test("""
188 abc
189 {name}
190 def
191 """, """
192 abc
193 GARGLE
194 def
195 """, name='GARGLE')
196
197 def test_multiline_substitution(self):
198 self._test("""
199 abc
200 {name}
201 def
202 """, """
203 abc
204 bingle
205 bungle
206
207 def
208 """, name='bingle\nbungle\n')
209
210class InertParser:
211 def __init__(self, clinic):
212 pass
213
214 def parse(self, block):
215 pass
216
217class CopyParser:
218 def __init__(self, clinic):
219 pass
220
221 def parse(self, block):
222 block.output = block.input
223
224
225class ClinicBlockParserTest(TestCase):
226 def _test(self, input, output):
Larry Hastings7726ac92014-01-31 22:03:12 -0800227 language = clinic.CLanguage(None)
Larry Hastings31826802013-10-19 00:09:25 -0700228
229 blocks = list(clinic.BlockParser(input, language))
230 writer = clinic.BlockPrinter(language)
231 for block in blocks:
232 writer.print_block(block)
233 output = writer.f.getvalue()
234 assert output == input, "output != input!\n\noutput " + repr(output) + "\n\n input " + repr(input)
235
236 def round_trip(self, input):
237 return self._test(input, input)
238
239 def test_round_trip_1(self):
240 self.round_trip("""
241 verbatim text here
242 lah dee dah
243""")
244 def test_round_trip_2(self):
245 self.round_trip("""
246 verbatim text here
247 lah dee dah
248/*[inert]
249abc
250[inert]*/
251def
252/*[inert checksum: 7b18d017f89f61cf17d47f92749ea6930a3f1deb]*/
253xyz
254""")
255
256 def _test_clinic(self, input, output):
Larry Hastings7726ac92014-01-31 22:03:12 -0800257 language = clinic.CLanguage(None)
Victor Stinner65fc98e2018-09-03 23:17:20 +0200258 c = clinic.Clinic(language, filename="file")
Larry Hastings31826802013-10-19 00:09:25 -0700259 c.parsers['inert'] = InertParser(c)
260 c.parsers['copy'] = CopyParser(c)
261 computed = c.parse(input)
262 self.assertEqual(output, computed)
263
264 def test_clinic_1(self):
265 self._test_clinic("""
266 verbatim text here
267 lah dee dah
Larry Hastings2a727912014-01-16 11:32:01 -0800268/*[copy input]
Larry Hastings31826802013-10-19 00:09:25 -0700269def
Larry Hastings2a727912014-01-16 11:32:01 -0800270[copy start generated code]*/
Larry Hastings31826802013-10-19 00:09:25 -0700271abc
Larry Hastings7726ac92014-01-31 22:03:12 -0800272/*[copy end generated code: output=03cfd743661f0797 input=7b18d017f89f61cf]*/
Larry Hastings31826802013-10-19 00:09:25 -0700273xyz
274""", """
275 verbatim text here
276 lah dee dah
Larry Hastings2a727912014-01-16 11:32:01 -0800277/*[copy input]
Larry Hastings31826802013-10-19 00:09:25 -0700278def
Larry Hastings2a727912014-01-16 11:32:01 -0800279[copy start generated code]*/
Larry Hastings31826802013-10-19 00:09:25 -0700280def
Larry Hastings7726ac92014-01-31 22:03:12 -0800281/*[copy end generated code: output=7b18d017f89f61cf input=7b18d017f89f61cf]*/
Larry Hastings31826802013-10-19 00:09:25 -0700282xyz
283""")
284
285
286class ClinicParserTest(TestCase):
287 def test_trivial(self):
288 parser = DSLParser(FakeClinic())
289 block = clinic.Block("module os\nos.access")
290 parser.parse(block)
291 module, function = block.signatures
292 self.assertEqual("access", function.name)
293 self.assertEqual("os", module.name)
294
295 def test_ignore_line(self):
296 block = self.parse("#\nmodule os\nos.access")
297 module, function = block.signatures
298 self.assertEqual("access", function.name)
299 self.assertEqual("os", module.name)
300
301 def test_param(self):
302 function = self.parse_function("module os\nos.access\n path: int")
303 self.assertEqual("access", function.name)
Larry Hastings7726ac92014-01-31 22:03:12 -0800304 self.assertEqual(2, len(function.parameters))
Larry Hastings31826802013-10-19 00:09:25 -0700305 p = function.parameters['path']
306 self.assertEqual('path', p.name)
307 self.assertIsInstance(p.converter, clinic.int_converter)
308
309 def test_param_default(self):
310 function = self.parse_function("module os\nos.access\n follow_symlinks: bool = True")
311 p = function.parameters['follow_symlinks']
312 self.assertEqual(True, p.default)
313
Larry Hastings1abd7082014-01-16 14:15:03 -0800314 def test_param_with_continuations(self):
315 function = self.parse_function("module os\nos.access\n follow_symlinks: \\\n bool \\\n =\\\n True")
316 p = function.parameters['follow_symlinks']
317 self.assertEqual(True, p.default)
318
319 def test_param_default_expression(self):
320 function = self.parse_function("module os\nos.access\n follow_symlinks: int(c_default='MAXSIZE') = sys.maxsize")
321 p = function.parameters['follow_symlinks']
322 self.assertEqual(sys.maxsize, p.default)
323 self.assertEqual("MAXSIZE", p.converter.c_default)
324
325 s = self.parse_function_should_fail("module os\nos.access\n follow_symlinks: int = sys.maxsize")
326 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")
327
Larry Hastings31826802013-10-19 00:09:25 -0700328 def test_param_no_docstring(self):
329 function = self.parse_function("""
330module os
331os.access
332 follow_symlinks: bool = True
Larry Hastings7726ac92014-01-31 22:03:12 -0800333 something_else: str = ''""")
Larry Hastings31826802013-10-19 00:09:25 -0700334 p = function.parameters['follow_symlinks']
Larry Hastings7726ac92014-01-31 22:03:12 -0800335 self.assertEqual(3, len(function.parameters))
Larry Hastings31826802013-10-19 00:09:25 -0700336 self.assertIsInstance(function.parameters['something_else'].converter, clinic.str_converter)
337
Larry Hastings7726ac92014-01-31 22:03:12 -0800338 def test_param_default_parameters_out_of_order(self):
339 s = self.parse_function_should_fail("""
340module os
341os.access
342 follow_symlinks: bool = True
343 something_else: str""")
344 self.assertEqual(s, """Error on line 0:
345Can't have a parameter without a default ('something_else')
346after a parameter with a default!
347""")
348
Larry Hastings31826802013-10-19 00:09:25 -0700349 def disabled_test_converter_arguments(self):
350 function = self.parse_function("module os\nos.access\n path: path_t(allow_fd=1)")
351 p = function.parameters['path']
352 self.assertEqual(1, p.converter.args['allow_fd'])
353
Larry Hastings31826802013-10-19 00:09:25 -0700354 def test_function_docstring(self):
355 function = self.parse_function("""
356module os
357os.stat as os_stat_fn
358
359 path: str
360 Path to be examined
361
362Perform a stat system call on the given path.""")
363 self.assertEqual("""
Larry Hastings2623c8c2014-02-08 22:15:29 -0800364stat($module, /, path)
365--
366
Larry Hastings31826802013-10-19 00:09:25 -0700367Perform a stat system call on the given path.
368
Larry Hastings31826802013-10-19 00:09:25 -0700369 path
370 Path to be examined
371""".strip(), function.docstring)
372
373 def test_explicit_parameters_in_docstring(self):
374 function = self.parse_function("""
375module foo
376foo.bar
377 x: int
378 Documentation for x.
379 y: int
380
381This is the documentation for foo.
382
383Okay, we're done here.
384""")
385 self.assertEqual("""
Larry Hastings2623c8c2014-02-08 22:15:29 -0800386bar($module, /, x, y)
387--
388
Larry Hastings31826802013-10-19 00:09:25 -0700389This is the documentation for foo.
390
Larry Hastings31826802013-10-19 00:09:25 -0700391 x
392 Documentation for x.
393
394Okay, we're done here.
395""".strip(), function.docstring)
396
397 def test_parser_regression_special_character_in_parameter_column_of_docstring_first_line(self):
398 function = self.parse_function("""
399module os
400os.stat
401 path: str
402This/used to break Clinic!
403""")
Larry Hastings2623c8c2014-02-08 22:15:29 -0800404 self.assertEqual("stat($module, /, path)\n--\n\nThis/used to break Clinic!", function.docstring)
Larry Hastings31826802013-10-19 00:09:25 -0700405
406 def test_c_name(self):
407 function = self.parse_function("module os\nos.stat as os_stat_fn")
408 self.assertEqual("os_stat_fn", function.c_basename)
409
410 def test_return_converter(self):
411 function = self.parse_function("module os\nos.stat -> int")
412 self.assertIsInstance(function.return_converter, clinic.int_return_converter)
413
414 def test_star(self):
415 function = self.parse_function("module os\nos.access\n *\n follow_symlinks: bool = True")
416 p = function.parameters['follow_symlinks']
417 self.assertEqual(inspect.Parameter.KEYWORD_ONLY, p.kind)
418 self.assertEqual(0, p.group)
419
420 def test_group(self):
421 function = self.parse_function("module window\nwindow.border\n [\n ls : int\n ]\n /\n")
422 p = function.parameters['ls']
423 self.assertEqual(1, p.group)
424
425 def test_left_group(self):
426 function = self.parse_function("""
427module curses
Larry Hastings6d2ea212014-01-05 02:50:45 -0800428curses.addch
Larry Hastings31826802013-10-19 00:09:25 -0700429 [
430 y: int
431 Y-coordinate.
432 x: int
433 X-coordinate.
434 ]
435 ch: char
436 Character to add.
437 [
438 attr: long
439 Attributes for the character.
440 ]
441 /
442""")
443 for name, group in (
444 ('y', -1), ('x', -1),
445 ('ch', 0),
446 ('attr', 1),
447 ):
448 p = function.parameters[name]
449 self.assertEqual(p.group, group)
450 self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
451 self.assertEqual(function.docstring.strip(), """
Larry Hastings6d2ea212014-01-05 02:50:45 -0800452addch([y, x,] ch, [attr])
453
454
Larry Hastings31826802013-10-19 00:09:25 -0700455 y
456 Y-coordinate.
457 x
458 X-coordinate.
459 ch
460 Character to add.
461 attr
462 Attributes for the character.
463 """.strip())
464
465 def test_nested_groups(self):
466 function = self.parse_function("""
467module curses
Larry Hastings6d2ea212014-01-05 02:50:45 -0800468curses.imaginary
Larry Hastings31826802013-10-19 00:09:25 -0700469 [
470 [
471 y1: int
472 Y-coordinate.
473 y2: int
474 Y-coordinate.
475 ]
476 x1: int
477 X-coordinate.
478 x2: int
479 X-coordinate.
480 ]
481 ch: char
482 Character to add.
483 [
484 attr1: long
485 Attributes for the character.
486 attr2: long
487 Attributes for the character.
488 attr3: long
489 Attributes for the character.
490 [
491 attr4: long
492 Attributes for the character.
493 attr5: long
494 Attributes for the character.
495 attr6: long
496 Attributes for the character.
497 ]
498 ]
499 /
500""")
501 for name, group in (
502 ('y1', -2), ('y2', -2),
503 ('x1', -1), ('x2', -1),
504 ('ch', 0),
505 ('attr1', 1), ('attr2', 1), ('attr3', 1),
506 ('attr4', 2), ('attr5', 2), ('attr6', 2),
507 ):
508 p = function.parameters[name]
509 self.assertEqual(p.group, group)
510 self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
511
512 self.assertEqual(function.docstring.strip(), """
Larry Hastings2623c8c2014-02-08 22:15:29 -0800513imaginary([[y1, y2,] x1, x2,] ch, [attr1, attr2, attr3, [attr4, attr5,
514 attr6]])
Larry Hastings6d2ea212014-01-05 02:50:45 -0800515
516
Larry Hastings31826802013-10-19 00:09:25 -0700517 y1
518 Y-coordinate.
519 y2
520 Y-coordinate.
521 x1
522 X-coordinate.
523 x2
524 X-coordinate.
525 ch
526 Character to add.
527 attr1
528 Attributes for the character.
529 attr2
530 Attributes for the character.
531 attr3
532 Attributes for the character.
533 attr4
534 Attributes for the character.
535 attr5
536 Attributes for the character.
537 attr6
538 Attributes for the character.
539 """.strip())
540
541 def parse_function_should_fail(self, s):
542 with support.captured_stdout() as stdout:
543 with self.assertRaises(SystemExit):
544 self.parse_function(s)
545 return stdout.getvalue()
546
547 def test_disallowed_grouping__two_top_groups_on_left(self):
548 s = self.parse_function_should_fail("""
549module foo
550foo.two_top_groups_on_left
551 [
552 group1 : int
553 ]
554 [
555 group2 : int
556 ]
557 param: int
558 """)
559 self.assertEqual(s,
560 ('Error on line 0:\n'
Larry Hastings7726ac92014-01-31 22:03:12 -0800561 'Function two_top_groups_on_left has an unsupported group configuration. (Unexpected state 2.b)\n'))
Larry Hastings31826802013-10-19 00:09:25 -0700562
563 def test_disallowed_grouping__two_top_groups_on_right(self):
564 self.parse_function_should_fail("""
565module foo
566foo.two_top_groups_on_right
567 param: int
568 [
569 group1 : int
570 ]
571 [
572 group2 : int
573 ]
574 """)
575
576 def test_disallowed_grouping__parameter_after_group_on_right(self):
577 self.parse_function_should_fail("""
578module foo
579foo.parameter_after_group_on_right
580 param: int
581 [
582 [
583 group1 : int
584 ]
585 group2 : int
586 ]
587 """)
588
589 def test_disallowed_grouping__group_after_parameter_on_left(self):
590 self.parse_function_should_fail("""
591module foo
592foo.group_after_parameter_on_left
593 [
594 group2 : int
595 [
596 group1 : int
597 ]
598 ]
599 param: int
600 """)
601
602 def test_disallowed_grouping__empty_group_on_left(self):
603 self.parse_function_should_fail("""
604module foo
605foo.empty_group
606 [
607 [
608 ]
609 group2 : int
610 ]
611 param: int
612 """)
613
614 def test_disallowed_grouping__empty_group_on_right(self):
615 self.parse_function_should_fail("""
616module foo
617foo.empty_group
618 param: int
619 [
620 [
621 ]
622 group2 : int
623 ]
624 """)
625
626 def test_no_parameters(self):
627 function = self.parse_function("""
628module foo
629foo.bar
630
631Docstring
632
633""")
Larry Hastings2623c8c2014-02-08 22:15:29 -0800634 self.assertEqual("bar($module, /)\n--\n\nDocstring", function.docstring)
Larry Hastings7726ac92014-01-31 22:03:12 -0800635 self.assertEqual(1, len(function.parameters)) # self!
Larry Hastings31826802013-10-19 00:09:25 -0700636
Larry Hastings2623c8c2014-02-08 22:15:29 -0800637 def test_init_with_no_parameters(self):
638 function = self.parse_function("""
639module foo
640class foo.Bar "unused" "notneeded"
641foo.Bar.__init__
642
643Docstring
644
645""", signatures_in_block=3, function_index=2)
646 # self is not in the signature
647 self.assertEqual("Bar()\n--\n\nDocstring", function.docstring)
648 # but it *is* a parameter
649 self.assertEqual(1, len(function.parameters))
650
Larry Hastingsdfcd4672013-10-27 02:49:39 -0700651 def test_illegal_module_line(self):
652 self.parse_function_should_fail("""
653module foo
654foo.bar => int
655 /
656""")
657
658 def test_illegal_c_basename(self):
659 self.parse_function_should_fail("""
660module foo
661foo.bar as 935
662 /
663""")
664
Larry Hastings31826802013-10-19 00:09:25 -0700665 def test_single_star(self):
666 self.parse_function_should_fail("""
667module foo
668foo.bar
669 *
670 *
671""")
672
673 def test_parameters_required_after_star_without_initial_parameters_or_docstring(self):
674 self.parse_function_should_fail("""
675module foo
676foo.bar
677 *
678""")
679
680 def test_parameters_required_after_star_without_initial_parameters_with_docstring(self):
681 self.parse_function_should_fail("""
682module foo
683foo.bar
684 *
685Docstring here.
686""")
687
688 def test_parameters_required_after_star_with_initial_parameters_without_docstring(self):
689 self.parse_function_should_fail("""
690module foo
691foo.bar
692 this: int
693 *
694""")
695
696 def test_parameters_required_after_star_with_initial_parameters_and_docstring(self):
697 self.parse_function_should_fail("""
698module foo
699foo.bar
700 this: int
701 *
702Docstring.
703""")
704
705 def test_single_slash(self):
706 self.parse_function_should_fail("""
707module foo
708foo.bar
709 /
710 /
711""")
712
713 def test_mix_star_and_slash(self):
714 self.parse_function_should_fail("""
715module foo
716foo.bar
717 x: int
718 y: int
719 *
720 z: int
721 /
722""")
723
724 def test_parameters_not_permitted_after_slash_for_now(self):
725 self.parse_function_should_fail("""
726module foo
727foo.bar
728 /
729 x: int
730""")
731
732 def test_function_not_at_column_0(self):
733 function = self.parse_function("""
734 module foo
735 foo.bar
736 x: int
737 Nested docstring here, goeth.
738 *
739 y: str
740 Not at column 0!
741""")
742 self.assertEqual("""
Larry Hastings2623c8c2014-02-08 22:15:29 -0800743bar($module, /, x, *, y)
744--
745
Larry Hastings31826802013-10-19 00:09:25 -0700746Not at column 0!
747
Larry Hastings31826802013-10-19 00:09:25 -0700748 x
749 Nested docstring here, goeth.
750""".strip(), function.docstring)
751
Larry Hastings31826802013-10-19 00:09:25 -0700752 def test_directive(self):
753 c = FakeClinic()
754 parser = DSLParser(c)
755 parser.flag = False
756 parser.directives['setflag'] = lambda : setattr(parser, 'flag', True)
757 block = clinic.Block("setflag")
758 parser.parse(block)
759 self.assertTrue(parser.flag)
760
761 def test_legacy_converters(self):
762 block = self.parse('module os\nos.access\n path: "s"')
763 module, function = block.signatures
764 self.assertIsInstance((function.parameters['path']).converter, clinic.str_converter)
765
766 def parse(self, text):
767 c = FakeClinic()
768 parser = DSLParser(c)
769 block = clinic.Block(text)
770 parser.parse(block)
771 return block
772
Larry Hastings2623c8c2014-02-08 22:15:29 -0800773 def parse_function(self, text, signatures_in_block=2, function_index=1):
Larry Hastings31826802013-10-19 00:09:25 -0700774 block = self.parse(text)
775 s = block.signatures
Larry Hastings2623c8c2014-02-08 22:15:29 -0800776 self.assertEqual(len(s), signatures_in_block)
Larry Hastings31826802013-10-19 00:09:25 -0700777 assert isinstance(s[0], clinic.Module)
Larry Hastings2623c8c2014-02-08 22:15:29 -0800778 assert isinstance(s[function_index], clinic.Function)
779 return s[function_index]
Larry Hastings31826802013-10-19 00:09:25 -0700780
781 def test_scaffolding(self):
782 # test repr on special values
783 self.assertEqual(repr(clinic.unspecified), '<Unspecified>')
784 self.assertEqual(repr(clinic.NULL), '<Null>')
785
786 # test that fail fails
787 with support.captured_stdout() as stdout:
788 with self.assertRaises(SystemExit):
789 clinic.fail('The igloos are melting!', filename='clown.txt', line_number=69)
790 self.assertEqual(stdout.getvalue(), 'Error in file "clown.txt" on line 69:\nThe igloos are melting!\n')
791
792
Serhiy Storchaka837c7dc2018-12-25 10:17:28 +0200793class ClinicExternalTest(TestCase):
794 maxDiff = None
795
796 def test_external(self):
797 source = support.findfile('clinic.test')
798 with open(source, 'r', encoding='utf-8') as f:
799 original = f.read()
800 with support.temp_dir() as testdir:
801 testfile = os.path.join(testdir, 'clinic.test.c')
802 with open(testfile, 'w', encoding='utf-8') as f:
803 f.write(original)
804 clinic.parse_file(testfile, force=True)
805 with open(testfile, 'r', encoding='utf-8') as f:
806 result = f.read()
807 self.assertEqual(result, original)
808
809
Larry Hastings31826802013-10-19 00:09:25 -0700810if __name__ == "__main__":
811 unittest.main()