blob: ba4ae3401056f48ba73acf198b809a69fb658cc5 [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
Victor Stinner65fc98e2018-09-03 23:17:20 +02005from test import support
6from 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
13
14clinic_path = os.path.join(os.path.dirname(__file__), '..', '..', 'Tools', 'clinic')
15clinic_path = os.path.normpath(clinic_path)
16if not os.path.exists(clinic_path):
17 raise unittest.SkipTest(f'{clinic_path!r} path does not exist')
18sys.path.append(clinic_path)
19try:
20 import clinic
21 from clinic import DSLParser
22finally:
23 del sys.path[-1]
Larry Hastings31826802013-10-19 00:09:25 -070024
Larry Hastings7726ac92014-01-31 22:03:12 -080025
Larry Hastings31826802013-10-19 00:09:25 -070026class FakeConverter:
27 def __init__(self, name, args):
28 self.name = name
29 self.args = args
30
31
32class FakeConverterFactory:
33 def __init__(self, name):
34 self.name = name
35
36 def __call__(self, name, default, **kwargs):
37 return FakeConverter(self.name, kwargs)
38
39
40class FakeConvertersDict:
41 def __init__(self):
42 self.used_converters = {}
43
44 def get(self, name, default):
45 return self.used_converters.setdefault(name, FakeConverterFactory(name))
46
Larry Hastingsbebf7352014-01-17 17:47:17 -080047clinic.Clinic.presets_text = ''
Victor Stinner65fc98e2018-09-03 23:17:20 +020048c = clinic.Clinic(language='C', filename = "file")
Larry Hastingsbebf7352014-01-17 17:47:17 -080049
Larry Hastings31826802013-10-19 00:09:25 -070050class FakeClinic:
51 def __init__(self):
52 self.converters = FakeConvertersDict()
53 self.legacy_converters = FakeConvertersDict()
Larry Hastings7726ac92014-01-31 22:03:12 -080054 self.language = clinic.CLanguage(None)
Larry Hastings31826802013-10-19 00:09:25 -070055 self.filename = None
Victor Stinner65fc98e2018-09-03 23:17:20 +020056 self.destination_buffers = {}
Larry Hastings31826802013-10-19 00:09:25 -070057 self.block_parser = clinic.BlockParser('', self.language)
58 self.modules = collections.OrderedDict()
Larry Hastings7726ac92014-01-31 22:03:12 -080059 self.classes = collections.OrderedDict()
Larry Hastings31826802013-10-19 00:09:25 -070060 clinic.clinic = self
61 self.name = "FakeClinic"
Larry Hastingsbebf7352014-01-17 17:47:17 -080062 self.line_prefix = self.line_suffix = ''
63 self.destinations = {}
64 self.add_destination("block", "buffer")
65 self.add_destination("file", "buffer")
66 self.add_destination("suppress", "suppress")
67 d = self.destinations.get
68 self.field_destinations = collections.OrderedDict((
69 ('docstring_prototype', d('suppress')),
70 ('docstring_definition', d('block')),
71 ('methoddef_define', d('block')),
72 ('impl_prototype', d('block')),
73 ('parser_prototype', d('suppress')),
74 ('parser_definition', d('block')),
75 ('impl_definition', d('block')),
76 ))
77
78 def get_destination(self, name):
79 d = self.destinations.get(name)
80 if not d:
81 sys.exit("Destination does not exist: " + repr(name))
82 return d
83
84 def add_destination(self, name, type, *args):
85 if name in self.destinations:
86 sys.exit("Destination already exists: " + repr(name))
87 self.destinations[name] = clinic.Destination(name, type, self, *args)
Larry Hastings31826802013-10-19 00:09:25 -070088
89 def is_directive(self, name):
90 return name == "module"
91
92 def directive(self, name, args):
93 self.called_directives[name] = args
94
95 _module_and_class = clinic.Clinic._module_and_class
96
Larry Hastings90261132014-01-07 12:21:08 -080097class ClinicWholeFileTest(TestCase):
98 def test_eol(self):
99 # regression test:
100 # clinic's block parser didn't recognize
101 # the "end line" for the block if it
102 # didn't end in "\n" (as in, the last)
103 # byte of the file was '/'.
Martin Pantereb995702016-07-28 01:11:04 +0000104 # so it would spit out an end line for you.
Larry Hastings90261132014-01-07 12:21:08 -0800105 # and since you really already had one,
106 # the last line of the block got corrupted.
Victor Stinner65fc98e2018-09-03 23:17:20 +0200107 c = clinic.Clinic(clinic.CLanguage(None), filename="file")
Larry Hastings90261132014-01-07 12:21:08 -0800108 raw = "/*[clinic]\nfoo\n[clinic]*/"
109 cooked = c.parse(raw).splitlines()
110 end_line = cooked[2].rstrip()
111 # this test is redundant, it's just here explicitly to catch
112 # the regression test so we don't forget what it looked like
113 self.assertNotEqual(end_line, "[clinic]*/[clinic]*/")
114 self.assertEqual(end_line, "[clinic]*/")
115
Larry Hastings31826802013-10-19 00:09:25 -0700116
117
118class ClinicGroupPermuterTest(TestCase):
119 def _test(self, l, m, r, output):
120 computed = clinic.permute_optional_groups(l, m, r)
121 self.assertEqual(output, computed)
122
123 def test_range(self):
124 self._test([['start']], ['stop'], [['step']],
125 (
126 ('stop',),
127 ('start', 'stop',),
128 ('start', 'stop', 'step',),
129 ))
130
131 def test_add_window(self):
132 self._test([['x', 'y']], ['ch'], [['attr']],
133 (
134 ('ch',),
135 ('ch', 'attr'),
136 ('x', 'y', 'ch',),
137 ('x', 'y', 'ch', 'attr'),
138 ))
139
140 def test_ludicrous(self):
141 self._test([['a1', 'a2', 'a3'], ['b1', 'b2']], ['c1'], [['d1', 'd2'], ['e1', 'e2', 'e3']],
142 (
143 ('c1',),
144 ('b1', 'b2', 'c1'),
145 ('b1', 'b2', 'c1', 'd1', 'd2'),
146 ('a1', 'a2', 'a3', 'b1', 'b2', 'c1'),
147 ('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2'),
148 ('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2', 'e1', 'e2', 'e3'),
149 ))
150
151 def test_right_only(self):
152 self._test([], [], [['a'],['b'],['c']],
153 (
154 (),
155 ('a',),
156 ('a', 'b'),
157 ('a', 'b', 'c')
158 ))
159
160 def test_have_left_options_but_required_is_empty(self):
161 def fn():
162 clinic.permute_optional_groups(['a'], [], [])
163 self.assertRaises(AssertionError, fn)
164
165
166class ClinicLinearFormatTest(TestCase):
167 def _test(self, input, output, **kwargs):
168 computed = clinic.linear_format(input, **kwargs)
169 self.assertEqual(output, computed)
170
171 def test_empty_strings(self):
172 self._test('', '')
173
174 def test_solo_newline(self):
175 self._test('\n', '\n')
176
177 def test_no_substitution(self):
178 self._test("""
179 abc
180 """, """
181 abc
182 """)
183
184 def test_empty_substitution(self):
185 self._test("""
186 abc
187 {name}
188 def
189 """, """
190 abc
191 def
192 """, name='')
193
194 def test_single_line_substitution(self):
195 self._test("""
196 abc
197 {name}
198 def
199 """, """
200 abc
201 GARGLE
202 def
203 """, name='GARGLE')
204
205 def test_multiline_substitution(self):
206 self._test("""
207 abc
208 {name}
209 def
210 """, """
211 abc
212 bingle
213 bungle
214
215 def
216 """, name='bingle\nbungle\n')
217
218class InertParser:
219 def __init__(self, clinic):
220 pass
221
222 def parse(self, block):
223 pass
224
225class CopyParser:
226 def __init__(self, clinic):
227 pass
228
229 def parse(self, block):
230 block.output = block.input
231
232
233class ClinicBlockParserTest(TestCase):
234 def _test(self, input, output):
Larry Hastings7726ac92014-01-31 22:03:12 -0800235 language = clinic.CLanguage(None)
Larry Hastings31826802013-10-19 00:09:25 -0700236
237 blocks = list(clinic.BlockParser(input, language))
238 writer = clinic.BlockPrinter(language)
239 for block in blocks:
240 writer.print_block(block)
241 output = writer.f.getvalue()
242 assert output == input, "output != input!\n\noutput " + repr(output) + "\n\n input " + repr(input)
243
244 def round_trip(self, input):
245 return self._test(input, input)
246
247 def test_round_trip_1(self):
248 self.round_trip("""
249 verbatim text here
250 lah dee dah
251""")
252 def test_round_trip_2(self):
253 self.round_trip("""
254 verbatim text here
255 lah dee dah
256/*[inert]
257abc
258[inert]*/
259def
260/*[inert checksum: 7b18d017f89f61cf17d47f92749ea6930a3f1deb]*/
261xyz
262""")
263
264 def _test_clinic(self, input, output):
Larry Hastings7726ac92014-01-31 22:03:12 -0800265 language = clinic.CLanguage(None)
Victor Stinner65fc98e2018-09-03 23:17:20 +0200266 c = clinic.Clinic(language, filename="file")
Larry Hastings31826802013-10-19 00:09:25 -0700267 c.parsers['inert'] = InertParser(c)
268 c.parsers['copy'] = CopyParser(c)
269 computed = c.parse(input)
270 self.assertEqual(output, computed)
271
272 def test_clinic_1(self):
273 self._test_clinic("""
274 verbatim text here
275 lah dee dah
Larry Hastings2a727912014-01-16 11:32:01 -0800276/*[copy input]
Larry Hastings31826802013-10-19 00:09:25 -0700277def
Larry Hastings2a727912014-01-16 11:32:01 -0800278[copy start generated code]*/
Larry Hastings31826802013-10-19 00:09:25 -0700279abc
Larry Hastings7726ac92014-01-31 22:03:12 -0800280/*[copy end generated code: output=03cfd743661f0797 input=7b18d017f89f61cf]*/
Larry Hastings31826802013-10-19 00:09:25 -0700281xyz
282""", """
283 verbatim text here
284 lah dee dah
Larry Hastings2a727912014-01-16 11:32:01 -0800285/*[copy input]
Larry Hastings31826802013-10-19 00:09:25 -0700286def
Larry Hastings2a727912014-01-16 11:32:01 -0800287[copy start generated code]*/
Larry Hastings31826802013-10-19 00:09:25 -0700288def
Larry Hastings7726ac92014-01-31 22:03:12 -0800289/*[copy end generated code: output=7b18d017f89f61cf input=7b18d017f89f61cf]*/
Larry Hastings31826802013-10-19 00:09:25 -0700290xyz
291""")
292
293
294class ClinicParserTest(TestCase):
295 def test_trivial(self):
296 parser = DSLParser(FakeClinic())
297 block = clinic.Block("module os\nos.access")
298 parser.parse(block)
299 module, function = block.signatures
300 self.assertEqual("access", function.name)
301 self.assertEqual("os", module.name)
302
303 def test_ignore_line(self):
304 block = self.parse("#\nmodule os\nos.access")
305 module, function = block.signatures
306 self.assertEqual("access", function.name)
307 self.assertEqual("os", module.name)
308
309 def test_param(self):
310 function = self.parse_function("module os\nos.access\n path: int")
311 self.assertEqual("access", function.name)
Larry Hastings7726ac92014-01-31 22:03:12 -0800312 self.assertEqual(2, len(function.parameters))
Larry Hastings31826802013-10-19 00:09:25 -0700313 p = function.parameters['path']
314 self.assertEqual('path', p.name)
315 self.assertIsInstance(p.converter, clinic.int_converter)
316
317 def test_param_default(self):
318 function = self.parse_function("module os\nos.access\n follow_symlinks: bool = True")
319 p = function.parameters['follow_symlinks']
320 self.assertEqual(True, p.default)
321
Larry Hastings1abd7082014-01-16 14:15:03 -0800322 def test_param_with_continuations(self):
323 function = self.parse_function("module os\nos.access\n follow_symlinks: \\\n bool \\\n =\\\n True")
324 p = function.parameters['follow_symlinks']
325 self.assertEqual(True, p.default)
326
327 def test_param_default_expression(self):
328 function = self.parse_function("module os\nos.access\n follow_symlinks: int(c_default='MAXSIZE') = sys.maxsize")
329 p = function.parameters['follow_symlinks']
330 self.assertEqual(sys.maxsize, p.default)
331 self.assertEqual("MAXSIZE", p.converter.c_default)
332
333 s = self.parse_function_should_fail("module os\nos.access\n follow_symlinks: int = sys.maxsize")
334 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")
335
Larry Hastings31826802013-10-19 00:09:25 -0700336 def test_param_no_docstring(self):
337 function = self.parse_function("""
338module os
339os.access
340 follow_symlinks: bool = True
Larry Hastings7726ac92014-01-31 22:03:12 -0800341 something_else: str = ''""")
Larry Hastings31826802013-10-19 00:09:25 -0700342 p = function.parameters['follow_symlinks']
Larry Hastings7726ac92014-01-31 22:03:12 -0800343 self.assertEqual(3, len(function.parameters))
Larry Hastings31826802013-10-19 00:09:25 -0700344 self.assertIsInstance(function.parameters['something_else'].converter, clinic.str_converter)
345
Larry Hastings7726ac92014-01-31 22:03:12 -0800346 def test_param_default_parameters_out_of_order(self):
347 s = self.parse_function_should_fail("""
348module os
349os.access
350 follow_symlinks: bool = True
351 something_else: str""")
352 self.assertEqual(s, """Error on line 0:
353Can't have a parameter without a default ('something_else')
354after a parameter with a default!
355""")
356
Larry Hastings31826802013-10-19 00:09:25 -0700357 def disabled_test_converter_arguments(self):
358 function = self.parse_function("module os\nos.access\n path: path_t(allow_fd=1)")
359 p = function.parameters['path']
360 self.assertEqual(1, p.converter.args['allow_fd'])
361
Larry Hastings31826802013-10-19 00:09:25 -0700362 def test_function_docstring(self):
363 function = self.parse_function("""
364module os
365os.stat as os_stat_fn
366
367 path: str
368 Path to be examined
369
370Perform a stat system call on the given path.""")
371 self.assertEqual("""
Larry Hastings2623c8c2014-02-08 22:15:29 -0800372stat($module, /, path)
373--
374
Larry Hastings31826802013-10-19 00:09:25 -0700375Perform a stat system call on the given path.
376
Larry Hastings31826802013-10-19 00:09:25 -0700377 path
378 Path to be examined
379""".strip(), function.docstring)
380
381 def test_explicit_parameters_in_docstring(self):
382 function = self.parse_function("""
383module foo
384foo.bar
385 x: int
386 Documentation for x.
387 y: int
388
389This is the documentation for foo.
390
391Okay, we're done here.
392""")
393 self.assertEqual("""
Larry Hastings2623c8c2014-02-08 22:15:29 -0800394bar($module, /, x, y)
395--
396
Larry Hastings31826802013-10-19 00:09:25 -0700397This is the documentation for foo.
398
Larry Hastings31826802013-10-19 00:09:25 -0700399 x
400 Documentation for x.
401
402Okay, we're done here.
403""".strip(), function.docstring)
404
405 def test_parser_regression_special_character_in_parameter_column_of_docstring_first_line(self):
406 function = self.parse_function("""
407module os
408os.stat
409 path: str
410This/used to break Clinic!
411""")
Larry Hastings2623c8c2014-02-08 22:15:29 -0800412 self.assertEqual("stat($module, /, path)\n--\n\nThis/used to break Clinic!", function.docstring)
Larry Hastings31826802013-10-19 00:09:25 -0700413
414 def test_c_name(self):
415 function = self.parse_function("module os\nos.stat as os_stat_fn")
416 self.assertEqual("os_stat_fn", function.c_basename)
417
418 def test_return_converter(self):
419 function = self.parse_function("module os\nos.stat -> int")
420 self.assertIsInstance(function.return_converter, clinic.int_return_converter)
421
422 def test_star(self):
423 function = self.parse_function("module os\nos.access\n *\n follow_symlinks: bool = True")
424 p = function.parameters['follow_symlinks']
425 self.assertEqual(inspect.Parameter.KEYWORD_ONLY, p.kind)
426 self.assertEqual(0, p.group)
427
428 def test_group(self):
429 function = self.parse_function("module window\nwindow.border\n [\n ls : int\n ]\n /\n")
430 p = function.parameters['ls']
431 self.assertEqual(1, p.group)
432
433 def test_left_group(self):
434 function = self.parse_function("""
435module curses
Larry Hastings6d2ea212014-01-05 02:50:45 -0800436curses.addch
Larry Hastings31826802013-10-19 00:09:25 -0700437 [
438 y: int
439 Y-coordinate.
440 x: int
441 X-coordinate.
442 ]
443 ch: char
444 Character to add.
445 [
446 attr: long
447 Attributes for the character.
448 ]
449 /
450""")
451 for name, group in (
452 ('y', -1), ('x', -1),
453 ('ch', 0),
454 ('attr', 1),
455 ):
456 p = function.parameters[name]
457 self.assertEqual(p.group, group)
458 self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
459 self.assertEqual(function.docstring.strip(), """
Larry Hastings6d2ea212014-01-05 02:50:45 -0800460addch([y, x,] ch, [attr])
461
462
Larry Hastings31826802013-10-19 00:09:25 -0700463 y
464 Y-coordinate.
465 x
466 X-coordinate.
467 ch
468 Character to add.
469 attr
470 Attributes for the character.
471 """.strip())
472
473 def test_nested_groups(self):
474 function = self.parse_function("""
475module curses
Larry Hastings6d2ea212014-01-05 02:50:45 -0800476curses.imaginary
Larry Hastings31826802013-10-19 00:09:25 -0700477 [
478 [
479 y1: int
480 Y-coordinate.
481 y2: int
482 Y-coordinate.
483 ]
484 x1: int
485 X-coordinate.
486 x2: int
487 X-coordinate.
488 ]
489 ch: char
490 Character to add.
491 [
492 attr1: long
493 Attributes for the character.
494 attr2: long
495 Attributes for the character.
496 attr3: long
497 Attributes for the character.
498 [
499 attr4: long
500 Attributes for the character.
501 attr5: long
502 Attributes for the character.
503 attr6: long
504 Attributes for the character.
505 ]
506 ]
507 /
508""")
509 for name, group in (
510 ('y1', -2), ('y2', -2),
511 ('x1', -1), ('x2', -1),
512 ('ch', 0),
513 ('attr1', 1), ('attr2', 1), ('attr3', 1),
514 ('attr4', 2), ('attr5', 2), ('attr6', 2),
515 ):
516 p = function.parameters[name]
517 self.assertEqual(p.group, group)
518 self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
519
520 self.assertEqual(function.docstring.strip(), """
Larry Hastings2623c8c2014-02-08 22:15:29 -0800521imaginary([[y1, y2,] x1, x2,] ch, [attr1, attr2, attr3, [attr4, attr5,
522 attr6]])
Larry Hastings6d2ea212014-01-05 02:50:45 -0800523
524
Larry Hastings31826802013-10-19 00:09:25 -0700525 y1
526 Y-coordinate.
527 y2
528 Y-coordinate.
529 x1
530 X-coordinate.
531 x2
532 X-coordinate.
533 ch
534 Character to add.
535 attr1
536 Attributes for the character.
537 attr2
538 Attributes for the character.
539 attr3
540 Attributes for the character.
541 attr4
542 Attributes for the character.
543 attr5
544 Attributes for the character.
545 attr6
546 Attributes for the character.
547 """.strip())
548
549 def parse_function_should_fail(self, s):
550 with support.captured_stdout() as stdout:
551 with self.assertRaises(SystemExit):
552 self.parse_function(s)
553 return stdout.getvalue()
554
555 def test_disallowed_grouping__two_top_groups_on_left(self):
556 s = self.parse_function_should_fail("""
557module foo
558foo.two_top_groups_on_left
559 [
560 group1 : int
561 ]
562 [
563 group2 : int
564 ]
565 param: int
566 """)
567 self.assertEqual(s,
568 ('Error on line 0:\n'
Larry Hastings7726ac92014-01-31 22:03:12 -0800569 'Function two_top_groups_on_left has an unsupported group configuration. (Unexpected state 2.b)\n'))
Larry Hastings31826802013-10-19 00:09:25 -0700570
571 def test_disallowed_grouping__two_top_groups_on_right(self):
572 self.parse_function_should_fail("""
573module foo
574foo.two_top_groups_on_right
575 param: int
576 [
577 group1 : int
578 ]
579 [
580 group2 : int
581 ]
582 """)
583
584 def test_disallowed_grouping__parameter_after_group_on_right(self):
585 self.parse_function_should_fail("""
586module foo
587foo.parameter_after_group_on_right
588 param: int
589 [
590 [
591 group1 : int
592 ]
593 group2 : int
594 ]
595 """)
596
597 def test_disallowed_grouping__group_after_parameter_on_left(self):
598 self.parse_function_should_fail("""
599module foo
600foo.group_after_parameter_on_left
601 [
602 group2 : int
603 [
604 group1 : int
605 ]
606 ]
607 param: int
608 """)
609
610 def test_disallowed_grouping__empty_group_on_left(self):
611 self.parse_function_should_fail("""
612module foo
613foo.empty_group
614 [
615 [
616 ]
617 group2 : int
618 ]
619 param: int
620 """)
621
622 def test_disallowed_grouping__empty_group_on_right(self):
623 self.parse_function_should_fail("""
624module foo
625foo.empty_group
626 param: int
627 [
628 [
629 ]
630 group2 : int
631 ]
632 """)
633
634 def test_no_parameters(self):
635 function = self.parse_function("""
636module foo
637foo.bar
638
639Docstring
640
641""")
Larry Hastings2623c8c2014-02-08 22:15:29 -0800642 self.assertEqual("bar($module, /)\n--\n\nDocstring", function.docstring)
Larry Hastings7726ac92014-01-31 22:03:12 -0800643 self.assertEqual(1, len(function.parameters)) # self!
Larry Hastings31826802013-10-19 00:09:25 -0700644
Larry Hastings2623c8c2014-02-08 22:15:29 -0800645 def test_init_with_no_parameters(self):
646 function = self.parse_function("""
647module foo
648class foo.Bar "unused" "notneeded"
649foo.Bar.__init__
650
651Docstring
652
653""", signatures_in_block=3, function_index=2)
654 # self is not in the signature
655 self.assertEqual("Bar()\n--\n\nDocstring", function.docstring)
656 # but it *is* a parameter
657 self.assertEqual(1, len(function.parameters))
658
Larry Hastingsdfcd4672013-10-27 02:49:39 -0700659 def test_illegal_module_line(self):
660 self.parse_function_should_fail("""
661module foo
662foo.bar => int
663 /
664""")
665
666 def test_illegal_c_basename(self):
667 self.parse_function_should_fail("""
668module foo
669foo.bar as 935
670 /
671""")
672
Larry Hastings31826802013-10-19 00:09:25 -0700673 def test_single_star(self):
674 self.parse_function_should_fail("""
675module foo
676foo.bar
677 *
678 *
679""")
680
681 def test_parameters_required_after_star_without_initial_parameters_or_docstring(self):
682 self.parse_function_should_fail("""
683module foo
684foo.bar
685 *
686""")
687
688 def test_parameters_required_after_star_without_initial_parameters_with_docstring(self):
689 self.parse_function_should_fail("""
690module foo
691foo.bar
692 *
693Docstring here.
694""")
695
696 def test_parameters_required_after_star_with_initial_parameters_without_docstring(self):
697 self.parse_function_should_fail("""
698module foo
699foo.bar
700 this: int
701 *
702""")
703
704 def test_parameters_required_after_star_with_initial_parameters_and_docstring(self):
705 self.parse_function_should_fail("""
706module foo
707foo.bar
708 this: int
709 *
710Docstring.
711""")
712
713 def test_single_slash(self):
714 self.parse_function_should_fail("""
715module foo
716foo.bar
717 /
718 /
719""")
720
721 def test_mix_star_and_slash(self):
722 self.parse_function_should_fail("""
723module foo
724foo.bar
725 x: int
726 y: int
727 *
728 z: int
729 /
730""")
731
732 def test_parameters_not_permitted_after_slash_for_now(self):
733 self.parse_function_should_fail("""
734module foo
735foo.bar
736 /
737 x: int
738""")
739
740 def test_function_not_at_column_0(self):
741 function = self.parse_function("""
742 module foo
743 foo.bar
744 x: int
745 Nested docstring here, goeth.
746 *
747 y: str
748 Not at column 0!
749""")
750 self.assertEqual("""
Larry Hastings2623c8c2014-02-08 22:15:29 -0800751bar($module, /, x, *, y)
752--
753
Larry Hastings31826802013-10-19 00:09:25 -0700754Not at column 0!
755
Larry Hastings31826802013-10-19 00:09:25 -0700756 x
757 Nested docstring here, goeth.
758""".strip(), function.docstring)
759
Larry Hastings31826802013-10-19 00:09:25 -0700760 def test_directive(self):
761 c = FakeClinic()
762 parser = DSLParser(c)
763 parser.flag = False
764 parser.directives['setflag'] = lambda : setattr(parser, 'flag', True)
765 block = clinic.Block("setflag")
766 parser.parse(block)
767 self.assertTrue(parser.flag)
768
769 def test_legacy_converters(self):
770 block = self.parse('module os\nos.access\n path: "s"')
771 module, function = block.signatures
772 self.assertIsInstance((function.parameters['path']).converter, clinic.str_converter)
773
774 def parse(self, text):
775 c = FakeClinic()
776 parser = DSLParser(c)
777 block = clinic.Block(text)
778 parser.parse(block)
779 return block
780
Larry Hastings2623c8c2014-02-08 22:15:29 -0800781 def parse_function(self, text, signatures_in_block=2, function_index=1):
Larry Hastings31826802013-10-19 00:09:25 -0700782 block = self.parse(text)
783 s = block.signatures
Larry Hastings2623c8c2014-02-08 22:15:29 -0800784 self.assertEqual(len(s), signatures_in_block)
Larry Hastings31826802013-10-19 00:09:25 -0700785 assert isinstance(s[0], clinic.Module)
Larry Hastings2623c8c2014-02-08 22:15:29 -0800786 assert isinstance(s[function_index], clinic.Function)
787 return s[function_index]
Larry Hastings31826802013-10-19 00:09:25 -0700788
789 def test_scaffolding(self):
790 # test repr on special values
791 self.assertEqual(repr(clinic.unspecified), '<Unspecified>')
792 self.assertEqual(repr(clinic.NULL), '<Null>')
793
794 # test that fail fails
795 with support.captured_stdout() as stdout:
796 with self.assertRaises(SystemExit):
797 clinic.fail('The igloos are melting!', filename='clown.txt', line_number=69)
798 self.assertEqual(stdout.getvalue(), 'Error in file "clown.txt" on line 69:\nThe igloos are melting!\n')
799
800
801if __name__ == "__main__":
802 unittest.main()