blob: cd21000c050c371791e74af5f82f5fed2409cda7 [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.
4#
5
6import builtins
7import clinic
8from clinic import DSLParser
9import collections
10import inspect
11from test import support
Larry Hastings1abd7082014-01-16 14:15:03 -080012import sys
Larry Hastings31826802013-10-19 00:09:25 -070013import unittest
14from unittest import TestCase
15
Larry Hastings7726ac92014-01-31 22:03:12 -080016
Larry Hastings31826802013-10-19 00:09:25 -070017class FakeConverter:
18 def __init__(self, name, args):
19 self.name = name
20 self.args = args
21
22
23class FakeConverterFactory:
24 def __init__(self, name):
25 self.name = name
26
27 def __call__(self, name, default, **kwargs):
28 return FakeConverter(self.name, kwargs)
29
30
31class FakeConvertersDict:
32 def __init__(self):
33 self.used_converters = {}
34
35 def get(self, name, default):
36 return self.used_converters.setdefault(name, FakeConverterFactory(name))
37
Larry Hastingsbebf7352014-01-17 17:47:17 -080038clinic.Clinic.presets_text = ''
39c = clinic.Clinic(language='C')
40
Larry Hastings31826802013-10-19 00:09:25 -070041class FakeClinic:
42 def __init__(self):
43 self.converters = FakeConvertersDict()
44 self.legacy_converters = FakeConvertersDict()
Larry Hastings7726ac92014-01-31 22:03:12 -080045 self.language = clinic.CLanguage(None)
Larry Hastings31826802013-10-19 00:09:25 -070046 self.filename = None
47 self.block_parser = clinic.BlockParser('', self.language)
48 self.modules = collections.OrderedDict()
Larry Hastings7726ac92014-01-31 22:03:12 -080049 self.classes = collections.OrderedDict()
Larry Hastings31826802013-10-19 00:09:25 -070050 clinic.clinic = self
51 self.name = "FakeClinic"
Larry Hastingsbebf7352014-01-17 17:47:17 -080052 self.line_prefix = self.line_suffix = ''
53 self.destinations = {}
54 self.add_destination("block", "buffer")
55 self.add_destination("file", "buffer")
56 self.add_destination("suppress", "suppress")
57 d = self.destinations.get
58 self.field_destinations = collections.OrderedDict((
59 ('docstring_prototype', d('suppress')),
60 ('docstring_definition', d('block')),
61 ('methoddef_define', d('block')),
62 ('impl_prototype', d('block')),
63 ('parser_prototype', d('suppress')),
64 ('parser_definition', d('block')),
65 ('impl_definition', d('block')),
66 ))
67
68 def get_destination(self, name):
69 d = self.destinations.get(name)
70 if not d:
71 sys.exit("Destination does not exist: " + repr(name))
72 return d
73
74 def add_destination(self, name, type, *args):
75 if name in self.destinations:
76 sys.exit("Destination already exists: " + repr(name))
77 self.destinations[name] = clinic.Destination(name, type, self, *args)
Larry Hastings31826802013-10-19 00:09:25 -070078
79 def is_directive(self, name):
80 return name == "module"
81
82 def directive(self, name, args):
83 self.called_directives[name] = args
84
85 _module_and_class = clinic.Clinic._module_and_class
86
Larry Hastings90261132014-01-07 12:21:08 -080087class ClinicWholeFileTest(TestCase):
88 def test_eol(self):
89 # regression test:
90 # clinic's block parser didn't recognize
91 # the "end line" for the block if it
92 # didn't end in "\n" (as in, the last)
93 # byte of the file was '/'.
94 # so it woudl spit out an end line for you.
95 # and since you really already had one,
96 # the last line of the block got corrupted.
Larry Hastings7726ac92014-01-31 22:03:12 -080097 c = clinic.Clinic(clinic.CLanguage(None))
Larry Hastings90261132014-01-07 12:21:08 -080098 raw = "/*[clinic]\nfoo\n[clinic]*/"
99 cooked = c.parse(raw).splitlines()
100 end_line = cooked[2].rstrip()
101 # this test is redundant, it's just here explicitly to catch
102 # the regression test so we don't forget what it looked like
103 self.assertNotEqual(end_line, "[clinic]*/[clinic]*/")
104 self.assertEqual(end_line, "[clinic]*/")
105
Larry Hastings31826802013-10-19 00:09:25 -0700106
107
108class ClinicGroupPermuterTest(TestCase):
109 def _test(self, l, m, r, output):
110 computed = clinic.permute_optional_groups(l, m, r)
111 self.assertEqual(output, computed)
112
113 def test_range(self):
114 self._test([['start']], ['stop'], [['step']],
115 (
116 ('stop',),
117 ('start', 'stop',),
118 ('start', 'stop', 'step',),
119 ))
120
121 def test_add_window(self):
122 self._test([['x', 'y']], ['ch'], [['attr']],
123 (
124 ('ch',),
125 ('ch', 'attr'),
126 ('x', 'y', 'ch',),
127 ('x', 'y', 'ch', 'attr'),
128 ))
129
130 def test_ludicrous(self):
131 self._test([['a1', 'a2', 'a3'], ['b1', 'b2']], ['c1'], [['d1', 'd2'], ['e1', 'e2', 'e3']],
132 (
133 ('c1',),
134 ('b1', 'b2', 'c1'),
135 ('b1', 'b2', 'c1', 'd1', 'd2'),
136 ('a1', 'a2', 'a3', 'b1', 'b2', 'c1'),
137 ('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2'),
138 ('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2', 'e1', 'e2', 'e3'),
139 ))
140
141 def test_right_only(self):
142 self._test([], [], [['a'],['b'],['c']],
143 (
144 (),
145 ('a',),
146 ('a', 'b'),
147 ('a', 'b', 'c')
148 ))
149
150 def test_have_left_options_but_required_is_empty(self):
151 def fn():
152 clinic.permute_optional_groups(['a'], [], [])
153 self.assertRaises(AssertionError, fn)
154
155
156class ClinicLinearFormatTest(TestCase):
157 def _test(self, input, output, **kwargs):
158 computed = clinic.linear_format(input, **kwargs)
159 self.assertEqual(output, computed)
160
161 def test_empty_strings(self):
162 self._test('', '')
163
164 def test_solo_newline(self):
165 self._test('\n', '\n')
166
167 def test_no_substitution(self):
168 self._test("""
169 abc
170 """, """
171 abc
172 """)
173
174 def test_empty_substitution(self):
175 self._test("""
176 abc
177 {name}
178 def
179 """, """
180 abc
181 def
182 """, name='')
183
184 def test_single_line_substitution(self):
185 self._test("""
186 abc
187 {name}
188 def
189 """, """
190 abc
191 GARGLE
192 def
193 """, name='GARGLE')
194
195 def test_multiline_substitution(self):
196 self._test("""
197 abc
198 {name}
199 def
200 """, """
201 abc
202 bingle
203 bungle
204
205 def
206 """, name='bingle\nbungle\n')
207
208class InertParser:
209 def __init__(self, clinic):
210 pass
211
212 def parse(self, block):
213 pass
214
215class CopyParser:
216 def __init__(self, clinic):
217 pass
218
219 def parse(self, block):
220 block.output = block.input
221
222
223class ClinicBlockParserTest(TestCase):
224 def _test(self, input, output):
Larry Hastings7726ac92014-01-31 22:03:12 -0800225 language = clinic.CLanguage(None)
Larry Hastings31826802013-10-19 00:09:25 -0700226
227 blocks = list(clinic.BlockParser(input, language))
228 writer = clinic.BlockPrinter(language)
229 for block in blocks:
230 writer.print_block(block)
231 output = writer.f.getvalue()
232 assert output == input, "output != input!\n\noutput " + repr(output) + "\n\n input " + repr(input)
233
234 def round_trip(self, input):
235 return self._test(input, input)
236
237 def test_round_trip_1(self):
238 self.round_trip("""
239 verbatim text here
240 lah dee dah
241""")
242 def test_round_trip_2(self):
243 self.round_trip("""
244 verbatim text here
245 lah dee dah
246/*[inert]
247abc
248[inert]*/
249def
250/*[inert checksum: 7b18d017f89f61cf17d47f92749ea6930a3f1deb]*/
251xyz
252""")
253
254 def _test_clinic(self, input, output):
Larry Hastings7726ac92014-01-31 22:03:12 -0800255 language = clinic.CLanguage(None)
Larry Hastings31826802013-10-19 00:09:25 -0700256 c = clinic.Clinic(language)
257 c.parsers['inert'] = InertParser(c)
258 c.parsers['copy'] = CopyParser(c)
259 computed = c.parse(input)
260 self.assertEqual(output, computed)
261
262 def test_clinic_1(self):
263 self._test_clinic("""
264 verbatim text here
265 lah dee dah
Larry Hastings2a727912014-01-16 11:32:01 -0800266/*[copy input]
Larry Hastings31826802013-10-19 00:09:25 -0700267def
Larry Hastings2a727912014-01-16 11:32:01 -0800268[copy start generated code]*/
Larry Hastings31826802013-10-19 00:09:25 -0700269abc
Larry Hastings7726ac92014-01-31 22:03:12 -0800270/*[copy end generated code: output=03cfd743661f0797 input=7b18d017f89f61cf]*/
Larry Hastings31826802013-10-19 00:09:25 -0700271xyz
272""", """
273 verbatim text here
274 lah dee dah
Larry Hastings2a727912014-01-16 11:32:01 -0800275/*[copy input]
Larry Hastings31826802013-10-19 00:09:25 -0700276def
Larry Hastings2a727912014-01-16 11:32:01 -0800277[copy start generated code]*/
Larry Hastings31826802013-10-19 00:09:25 -0700278def
Larry Hastings7726ac92014-01-31 22:03:12 -0800279/*[copy end generated code: output=7b18d017f89f61cf input=7b18d017f89f61cf]*/
Larry Hastings31826802013-10-19 00:09:25 -0700280xyz
281""")
282
283
284class ClinicParserTest(TestCase):
285 def test_trivial(self):
286 parser = DSLParser(FakeClinic())
287 block = clinic.Block("module os\nos.access")
288 parser.parse(block)
289 module, function = block.signatures
290 self.assertEqual("access", function.name)
291 self.assertEqual("os", module.name)
292
293 def test_ignore_line(self):
294 block = self.parse("#\nmodule os\nos.access")
295 module, function = block.signatures
296 self.assertEqual("access", function.name)
297 self.assertEqual("os", module.name)
298
299 def test_param(self):
300 function = self.parse_function("module os\nos.access\n path: int")
301 self.assertEqual("access", function.name)
Larry Hastings7726ac92014-01-31 22:03:12 -0800302 self.assertEqual(2, len(function.parameters))
Larry Hastings31826802013-10-19 00:09:25 -0700303 p = function.parameters['path']
304 self.assertEqual('path', p.name)
305 self.assertIsInstance(p.converter, clinic.int_converter)
306
307 def test_param_default(self):
308 function = self.parse_function("module os\nos.access\n follow_symlinks: bool = True")
309 p = function.parameters['follow_symlinks']
310 self.assertEqual(True, p.default)
311
Larry Hastings1abd7082014-01-16 14:15:03 -0800312 def test_param_with_continuations(self):
313 function = self.parse_function("module os\nos.access\n follow_symlinks: \\\n bool \\\n =\\\n True")
314 p = function.parameters['follow_symlinks']
315 self.assertEqual(True, p.default)
316
317 def test_param_default_expression(self):
318 function = self.parse_function("module os\nos.access\n follow_symlinks: int(c_default='MAXSIZE') = sys.maxsize")
319 p = function.parameters['follow_symlinks']
320 self.assertEqual(sys.maxsize, p.default)
321 self.assertEqual("MAXSIZE", p.converter.c_default)
322
323 s = self.parse_function_should_fail("module os\nos.access\n follow_symlinks: int = sys.maxsize")
324 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")
325
Larry Hastings31826802013-10-19 00:09:25 -0700326 def test_param_no_docstring(self):
327 function = self.parse_function("""
328module os
329os.access
330 follow_symlinks: bool = True
Larry Hastings7726ac92014-01-31 22:03:12 -0800331 something_else: str = ''""")
Larry Hastings31826802013-10-19 00:09:25 -0700332 p = function.parameters['follow_symlinks']
Larry Hastings7726ac92014-01-31 22:03:12 -0800333 self.assertEqual(3, len(function.parameters))
Larry Hastings31826802013-10-19 00:09:25 -0700334 self.assertIsInstance(function.parameters['something_else'].converter, clinic.str_converter)
335
Larry Hastings7726ac92014-01-31 22:03:12 -0800336 def test_param_default_parameters_out_of_order(self):
337 s = self.parse_function_should_fail("""
338module os
339os.access
340 follow_symlinks: bool = True
341 something_else: str""")
342 self.assertEqual(s, """Error on line 0:
343Can't have a parameter without a default ('something_else')
344after a parameter with a default!
345""")
346
Larry Hastings31826802013-10-19 00:09:25 -0700347 def disabled_test_converter_arguments(self):
348 function = self.parse_function("module os\nos.access\n path: path_t(allow_fd=1)")
349 p = function.parameters['path']
350 self.assertEqual(1, p.converter.args['allow_fd'])
351
Larry Hastings31826802013-10-19 00:09:25 -0700352 def test_function_docstring(self):
353 function = self.parse_function("""
354module os
355os.stat as os_stat_fn
356
357 path: str
358 Path to be examined
359
360Perform a stat system call on the given path.""")
361 self.assertEqual("""
Larry Hastings2623c8c2014-02-08 22:15:29 -0800362stat($module, /, path)
363--
364
Larry Hastings31826802013-10-19 00:09:25 -0700365Perform a stat system call on the given path.
366
Larry Hastings31826802013-10-19 00:09:25 -0700367 path
368 Path to be examined
369""".strip(), function.docstring)
370
371 def test_explicit_parameters_in_docstring(self):
372 function = self.parse_function("""
373module foo
374foo.bar
375 x: int
376 Documentation for x.
377 y: int
378
379This is the documentation for foo.
380
381Okay, we're done here.
382""")
383 self.assertEqual("""
Larry Hastings2623c8c2014-02-08 22:15:29 -0800384bar($module, /, x, y)
385--
386
Larry Hastings31826802013-10-19 00:09:25 -0700387This is the documentation for foo.
388
Larry Hastings31826802013-10-19 00:09:25 -0700389 x
390 Documentation for x.
391
392Okay, we're done here.
393""".strip(), function.docstring)
394
395 def test_parser_regression_special_character_in_parameter_column_of_docstring_first_line(self):
396 function = self.parse_function("""
397module os
398os.stat
399 path: str
400This/used to break Clinic!
401""")
Larry Hastings2623c8c2014-02-08 22:15:29 -0800402 self.assertEqual("stat($module, /, path)\n--\n\nThis/used to break Clinic!", function.docstring)
Larry Hastings31826802013-10-19 00:09:25 -0700403
404 def test_c_name(self):
405 function = self.parse_function("module os\nos.stat as os_stat_fn")
406 self.assertEqual("os_stat_fn", function.c_basename)
407
408 def test_return_converter(self):
409 function = self.parse_function("module os\nos.stat -> int")
410 self.assertIsInstance(function.return_converter, clinic.int_return_converter)
411
412 def test_star(self):
413 function = self.parse_function("module os\nos.access\n *\n follow_symlinks: bool = True")
414 p = function.parameters['follow_symlinks']
415 self.assertEqual(inspect.Parameter.KEYWORD_ONLY, p.kind)
416 self.assertEqual(0, p.group)
417
418 def test_group(self):
419 function = self.parse_function("module window\nwindow.border\n [\n ls : int\n ]\n /\n")
420 p = function.parameters['ls']
421 self.assertEqual(1, p.group)
422
423 def test_left_group(self):
424 function = self.parse_function("""
425module curses
Larry Hastings6d2ea212014-01-05 02:50:45 -0800426curses.addch
Larry Hastings31826802013-10-19 00:09:25 -0700427 [
428 y: int
429 Y-coordinate.
430 x: int
431 X-coordinate.
432 ]
433 ch: char
434 Character to add.
435 [
436 attr: long
437 Attributes for the character.
438 ]
439 /
440""")
441 for name, group in (
442 ('y', -1), ('x', -1),
443 ('ch', 0),
444 ('attr', 1),
445 ):
446 p = function.parameters[name]
447 self.assertEqual(p.group, group)
448 self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
449 self.assertEqual(function.docstring.strip(), """
Larry Hastings6d2ea212014-01-05 02:50:45 -0800450addch([y, x,] ch, [attr])
451
452
Larry Hastings31826802013-10-19 00:09:25 -0700453 y
454 Y-coordinate.
455 x
456 X-coordinate.
457 ch
458 Character to add.
459 attr
460 Attributes for the character.
461 """.strip())
462
463 def test_nested_groups(self):
464 function = self.parse_function("""
465module curses
Larry Hastings6d2ea212014-01-05 02:50:45 -0800466curses.imaginary
Larry Hastings31826802013-10-19 00:09:25 -0700467 [
468 [
469 y1: int
470 Y-coordinate.
471 y2: int
472 Y-coordinate.
473 ]
474 x1: int
475 X-coordinate.
476 x2: int
477 X-coordinate.
478 ]
479 ch: char
480 Character to add.
481 [
482 attr1: long
483 Attributes for the character.
484 attr2: long
485 Attributes for the character.
486 attr3: long
487 Attributes for the character.
488 [
489 attr4: long
490 Attributes for the character.
491 attr5: long
492 Attributes for the character.
493 attr6: long
494 Attributes for the character.
495 ]
496 ]
497 /
498""")
499 for name, group in (
500 ('y1', -2), ('y2', -2),
501 ('x1', -1), ('x2', -1),
502 ('ch', 0),
503 ('attr1', 1), ('attr2', 1), ('attr3', 1),
504 ('attr4', 2), ('attr5', 2), ('attr6', 2),
505 ):
506 p = function.parameters[name]
507 self.assertEqual(p.group, group)
508 self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
509
510 self.assertEqual(function.docstring.strip(), """
Larry Hastings2623c8c2014-02-08 22:15:29 -0800511imaginary([[y1, y2,] x1, x2,] ch, [attr1, attr2, attr3, [attr4, attr5,
512 attr6]])
Larry Hastings6d2ea212014-01-05 02:50:45 -0800513
514
Larry Hastings31826802013-10-19 00:09:25 -0700515 y1
516 Y-coordinate.
517 y2
518 Y-coordinate.
519 x1
520 X-coordinate.
521 x2
522 X-coordinate.
523 ch
524 Character to add.
525 attr1
526 Attributes for the character.
527 attr2
528 Attributes for the character.
529 attr3
530 Attributes for the character.
531 attr4
532 Attributes for the character.
533 attr5
534 Attributes for the character.
535 attr6
536 Attributes for the character.
537 """.strip())
538
539 def parse_function_should_fail(self, s):
540 with support.captured_stdout() as stdout:
541 with self.assertRaises(SystemExit):
542 self.parse_function(s)
543 return stdout.getvalue()
544
545 def test_disallowed_grouping__two_top_groups_on_left(self):
546 s = self.parse_function_should_fail("""
547module foo
548foo.two_top_groups_on_left
549 [
550 group1 : int
551 ]
552 [
553 group2 : int
554 ]
555 param: int
556 """)
557 self.assertEqual(s,
558 ('Error on line 0:\n'
Larry Hastings7726ac92014-01-31 22:03:12 -0800559 'Function two_top_groups_on_left has an unsupported group configuration. (Unexpected state 2.b)\n'))
Larry Hastings31826802013-10-19 00:09:25 -0700560
561 def test_disallowed_grouping__two_top_groups_on_right(self):
562 self.parse_function_should_fail("""
563module foo
564foo.two_top_groups_on_right
565 param: int
566 [
567 group1 : int
568 ]
569 [
570 group2 : int
571 ]
572 """)
573
574 def test_disallowed_grouping__parameter_after_group_on_right(self):
575 self.parse_function_should_fail("""
576module foo
577foo.parameter_after_group_on_right
578 param: int
579 [
580 [
581 group1 : int
582 ]
583 group2 : int
584 ]
585 """)
586
587 def test_disallowed_grouping__group_after_parameter_on_left(self):
588 self.parse_function_should_fail("""
589module foo
590foo.group_after_parameter_on_left
591 [
592 group2 : int
593 [
594 group1 : int
595 ]
596 ]
597 param: int
598 """)
599
600 def test_disallowed_grouping__empty_group_on_left(self):
601 self.parse_function_should_fail("""
602module foo
603foo.empty_group
604 [
605 [
606 ]
607 group2 : int
608 ]
609 param: int
610 """)
611
612 def test_disallowed_grouping__empty_group_on_right(self):
613 self.parse_function_should_fail("""
614module foo
615foo.empty_group
616 param: int
617 [
618 [
619 ]
620 group2 : int
621 ]
622 """)
623
624 def test_no_parameters(self):
625 function = self.parse_function("""
626module foo
627foo.bar
628
629Docstring
630
631""")
Larry Hastings2623c8c2014-02-08 22:15:29 -0800632 self.assertEqual("bar($module, /)\n--\n\nDocstring", function.docstring)
Larry Hastings7726ac92014-01-31 22:03:12 -0800633 self.assertEqual(1, len(function.parameters)) # self!
Larry Hastings31826802013-10-19 00:09:25 -0700634
Larry Hastings2623c8c2014-02-08 22:15:29 -0800635 def test_init_with_no_parameters(self):
636 function = self.parse_function("""
637module foo
638class foo.Bar "unused" "notneeded"
639foo.Bar.__init__
640
641Docstring
642
643""", signatures_in_block=3, function_index=2)
644 # self is not in the signature
645 self.assertEqual("Bar()\n--\n\nDocstring", function.docstring)
646 # but it *is* a parameter
647 self.assertEqual(1, len(function.parameters))
648
Larry Hastingsdfcd4672013-10-27 02:49:39 -0700649 def test_illegal_module_line(self):
650 self.parse_function_should_fail("""
651module foo
652foo.bar => int
653 /
654""")
655
656 def test_illegal_c_basename(self):
657 self.parse_function_should_fail("""
658module foo
659foo.bar as 935
660 /
661""")
662
Larry Hastings31826802013-10-19 00:09:25 -0700663 def test_single_star(self):
664 self.parse_function_should_fail("""
665module foo
666foo.bar
667 *
668 *
669""")
670
671 def test_parameters_required_after_star_without_initial_parameters_or_docstring(self):
672 self.parse_function_should_fail("""
673module foo
674foo.bar
675 *
676""")
677
678 def test_parameters_required_after_star_without_initial_parameters_with_docstring(self):
679 self.parse_function_should_fail("""
680module foo
681foo.bar
682 *
683Docstring here.
684""")
685
686 def test_parameters_required_after_star_with_initial_parameters_without_docstring(self):
687 self.parse_function_should_fail("""
688module foo
689foo.bar
690 this: int
691 *
692""")
693
694 def test_parameters_required_after_star_with_initial_parameters_and_docstring(self):
695 self.parse_function_should_fail("""
696module foo
697foo.bar
698 this: int
699 *
700Docstring.
701""")
702
703 def test_single_slash(self):
704 self.parse_function_should_fail("""
705module foo
706foo.bar
707 /
708 /
709""")
710
711 def test_mix_star_and_slash(self):
712 self.parse_function_should_fail("""
713module foo
714foo.bar
715 x: int
716 y: int
717 *
718 z: int
719 /
720""")
721
722 def test_parameters_not_permitted_after_slash_for_now(self):
723 self.parse_function_should_fail("""
724module foo
725foo.bar
726 /
727 x: int
728""")
729
730 def test_function_not_at_column_0(self):
731 function = self.parse_function("""
732 module foo
733 foo.bar
734 x: int
735 Nested docstring here, goeth.
736 *
737 y: str
738 Not at column 0!
739""")
740 self.assertEqual("""
Larry Hastings2623c8c2014-02-08 22:15:29 -0800741bar($module, /, x, *, y)
742--
743
Larry Hastings31826802013-10-19 00:09:25 -0700744Not at column 0!
745
Larry Hastings31826802013-10-19 00:09:25 -0700746 x
747 Nested docstring here, goeth.
748""".strip(), function.docstring)
749
750 def test_parser_regression_special_character_in_parameter_column_of_docstring_first_line(self):
751 function = self.parse_function("""
752module os
753os.stat
754 path: str
755This/used to break Clinic!
756""")
Larry Hastings2623c8c2014-02-08 22:15:29 -0800757 self.assertEqual("stat($module, /, path)\n--\n\nThis/used to break Clinic!", function.docstring)
Larry Hastings31826802013-10-19 00:09:25 -0700758
759 def test_directive(self):
760 c = FakeClinic()
761 parser = DSLParser(c)
762 parser.flag = False
763 parser.directives['setflag'] = lambda : setattr(parser, 'flag', True)
764 block = clinic.Block("setflag")
765 parser.parse(block)
766 self.assertTrue(parser.flag)
767
768 def test_legacy_converters(self):
769 block = self.parse('module os\nos.access\n path: "s"')
770 module, function = block.signatures
771 self.assertIsInstance((function.parameters['path']).converter, clinic.str_converter)
772
773 def parse(self, text):
774 c = FakeClinic()
775 parser = DSLParser(c)
776 block = clinic.Block(text)
777 parser.parse(block)
778 return block
779
Larry Hastings2623c8c2014-02-08 22:15:29 -0800780 def parse_function(self, text, signatures_in_block=2, function_index=1):
Larry Hastings31826802013-10-19 00:09:25 -0700781 block = self.parse(text)
782 s = block.signatures
Larry Hastings2623c8c2014-02-08 22:15:29 -0800783 self.assertEqual(len(s), signatures_in_block)
Larry Hastings31826802013-10-19 00:09:25 -0700784 assert isinstance(s[0], clinic.Module)
Larry Hastings2623c8c2014-02-08 22:15:29 -0800785 assert isinstance(s[function_index], clinic.Function)
786 return s[function_index]
Larry Hastings31826802013-10-19 00:09:25 -0700787
788 def test_scaffolding(self):
789 # test repr on special values
790 self.assertEqual(repr(clinic.unspecified), '<Unspecified>')
791 self.assertEqual(repr(clinic.NULL), '<Null>')
792
793 # test that fail fails
794 with support.captured_stdout() as stdout:
795 with self.assertRaises(SystemExit):
796 clinic.fail('The igloos are melting!', filename='clown.txt', line_number=69)
797 self.assertEqual(stdout.getvalue(), 'Error in file "clown.txt" on line 69:\nThe igloos are melting!\n')
798
799
800if __name__ == "__main__":
801 unittest.main()