blob: 244c5fecd3491562e723b653e772660df62b6eff [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
Victor Stinner65fc98e2018-09-03 23:17:20 +020047c = clinic.Clinic(language='C', filename = "file")
Larry Hastingsbebf7352014-01-17 17:47:17 -080048
Larry Hastings31826802013-10-19 00:09:25 -070049class FakeClinic:
50 def __init__(self):
51 self.converters = FakeConvertersDict()
52 self.legacy_converters = FakeConvertersDict()
Larry Hastings7726ac92014-01-31 22:03:12 -080053 self.language = clinic.CLanguage(None)
Larry Hastings31826802013-10-19 00:09:25 -070054 self.filename = None
Victor Stinner65fc98e2018-09-03 23:17:20 +020055 self.destination_buffers = {}
Larry Hastings31826802013-10-19 00:09:25 -070056 self.block_parser = clinic.BlockParser('', self.language)
57 self.modules = collections.OrderedDict()
Larry Hastings7726ac92014-01-31 22:03:12 -080058 self.classes = collections.OrderedDict()
Larry Hastings31826802013-10-19 00:09:25 -070059 clinic.clinic = self
60 self.name = "FakeClinic"
Larry Hastingsbebf7352014-01-17 17:47:17 -080061 self.line_prefix = self.line_suffix = ''
62 self.destinations = {}
63 self.add_destination("block", "buffer")
64 self.add_destination("file", "buffer")
65 self.add_destination("suppress", "suppress")
66 d = self.destinations.get
67 self.field_destinations = collections.OrderedDict((
68 ('docstring_prototype', d('suppress')),
69 ('docstring_definition', d('block')),
70 ('methoddef_define', d('block')),
71 ('impl_prototype', d('block')),
72 ('parser_prototype', d('suppress')),
73 ('parser_definition', d('block')),
74 ('impl_definition', d('block')),
75 ))
76
77 def get_destination(self, name):
78 d = self.destinations.get(name)
79 if not d:
80 sys.exit("Destination does not exist: " + repr(name))
81 return d
82
83 def add_destination(self, name, type, *args):
84 if name in self.destinations:
85 sys.exit("Destination already exists: " + repr(name))
86 self.destinations[name] = clinic.Destination(name, type, self, *args)
Larry Hastings31826802013-10-19 00:09:25 -070087
88 def is_directive(self, name):
89 return name == "module"
90
91 def directive(self, name, args):
92 self.called_directives[name] = args
93
94 _module_and_class = clinic.Clinic._module_and_class
95
Larry Hastings90261132014-01-07 12:21:08 -080096class ClinicWholeFileTest(TestCase):
97 def test_eol(self):
98 # regression test:
99 # clinic's block parser didn't recognize
100 # the "end line" for the block if it
101 # didn't end in "\n" (as in, the last)
102 # byte of the file was '/'.
Martin Pantereb995702016-07-28 01:11:04 +0000103 # so it would spit out an end line for you.
Larry Hastings90261132014-01-07 12:21:08 -0800104 # and since you really already had one,
105 # the last line of the block got corrupted.
Victor Stinner65fc98e2018-09-03 23:17:20 +0200106 c = clinic.Clinic(clinic.CLanguage(None), filename="file")
Larry Hastings90261132014-01-07 12:21:08 -0800107 raw = "/*[clinic]\nfoo\n[clinic]*/"
108 cooked = c.parse(raw).splitlines()
109 end_line = cooked[2].rstrip()
110 # this test is redundant, it's just here explicitly to catch
111 # the regression test so we don't forget what it looked like
112 self.assertNotEqual(end_line, "[clinic]*/[clinic]*/")
113 self.assertEqual(end_line, "[clinic]*/")
114
Larry Hastings31826802013-10-19 00:09:25 -0700115
116
117class ClinicGroupPermuterTest(TestCase):
118 def _test(self, l, m, r, output):
119 computed = clinic.permute_optional_groups(l, m, r)
120 self.assertEqual(output, computed)
121
122 def test_range(self):
123 self._test([['start']], ['stop'], [['step']],
124 (
125 ('stop',),
126 ('start', 'stop',),
127 ('start', 'stop', 'step',),
128 ))
129
130 def test_add_window(self):
131 self._test([['x', 'y']], ['ch'], [['attr']],
132 (
133 ('ch',),
134 ('ch', 'attr'),
135 ('x', 'y', 'ch',),
136 ('x', 'y', 'ch', 'attr'),
137 ))
138
139 def test_ludicrous(self):
140 self._test([['a1', 'a2', 'a3'], ['b1', 'b2']], ['c1'], [['d1', 'd2'], ['e1', 'e2', 'e3']],
141 (
142 ('c1',),
143 ('b1', 'b2', 'c1'),
144 ('b1', 'b2', 'c1', 'd1', 'd2'),
145 ('a1', 'a2', 'a3', 'b1', 'b2', 'c1'),
146 ('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2'),
147 ('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2', 'e1', 'e2', 'e3'),
148 ))
149
150 def test_right_only(self):
151 self._test([], [], [['a'],['b'],['c']],
152 (
153 (),
154 ('a',),
155 ('a', 'b'),
156 ('a', 'b', 'c')
157 ))
158
159 def test_have_left_options_but_required_is_empty(self):
160 def fn():
161 clinic.permute_optional_groups(['a'], [], [])
162 self.assertRaises(AssertionError, fn)
163
164
165class ClinicLinearFormatTest(TestCase):
166 def _test(self, input, output, **kwargs):
167 computed = clinic.linear_format(input, **kwargs)
168 self.assertEqual(output, computed)
169
170 def test_empty_strings(self):
171 self._test('', '')
172
173 def test_solo_newline(self):
174 self._test('\n', '\n')
175
176 def test_no_substitution(self):
177 self._test("""
178 abc
179 """, """
180 abc
181 """)
182
183 def test_empty_substitution(self):
184 self._test("""
185 abc
186 {name}
187 def
188 """, """
189 abc
190 def
191 """, name='')
192
193 def test_single_line_substitution(self):
194 self._test("""
195 abc
196 {name}
197 def
198 """, """
199 abc
200 GARGLE
201 def
202 """, name='GARGLE')
203
204 def test_multiline_substitution(self):
205 self._test("""
206 abc
207 {name}
208 def
209 """, """
210 abc
211 bingle
212 bungle
213
214 def
215 """, name='bingle\nbungle\n')
216
217class InertParser:
218 def __init__(self, clinic):
219 pass
220
221 def parse(self, block):
222 pass
223
224class CopyParser:
225 def __init__(self, clinic):
226 pass
227
228 def parse(self, block):
229 block.output = block.input
230
231
232class ClinicBlockParserTest(TestCase):
233 def _test(self, input, output):
Larry Hastings7726ac92014-01-31 22:03:12 -0800234 language = clinic.CLanguage(None)
Larry Hastings31826802013-10-19 00:09:25 -0700235
236 blocks = list(clinic.BlockParser(input, language))
237 writer = clinic.BlockPrinter(language)
238 for block in blocks:
239 writer.print_block(block)
240 output = writer.f.getvalue()
241 assert output == input, "output != input!\n\noutput " + repr(output) + "\n\n input " + repr(input)
242
243 def round_trip(self, input):
244 return self._test(input, input)
245
246 def test_round_trip_1(self):
247 self.round_trip("""
248 verbatim text here
249 lah dee dah
250""")
251 def test_round_trip_2(self):
252 self.round_trip("""
253 verbatim text here
254 lah dee dah
255/*[inert]
256abc
257[inert]*/
258def
259/*[inert checksum: 7b18d017f89f61cf17d47f92749ea6930a3f1deb]*/
260xyz
261""")
262
263 def _test_clinic(self, input, output):
Larry Hastings7726ac92014-01-31 22:03:12 -0800264 language = clinic.CLanguage(None)
Victor Stinner65fc98e2018-09-03 23:17:20 +0200265 c = clinic.Clinic(language, filename="file")
Larry Hastings31826802013-10-19 00:09:25 -0700266 c.parsers['inert'] = InertParser(c)
267 c.parsers['copy'] = CopyParser(c)
268 computed = c.parse(input)
269 self.assertEqual(output, computed)
270
271 def test_clinic_1(self):
272 self._test_clinic("""
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 -0700278abc
Larry Hastings7726ac92014-01-31 22:03:12 -0800279/*[copy end generated code: output=03cfd743661f0797 input=7b18d017f89f61cf]*/
Larry Hastings31826802013-10-19 00:09:25 -0700280xyz
281""", """
282 verbatim text here
283 lah dee dah
Larry Hastings2a727912014-01-16 11:32:01 -0800284/*[copy input]
Larry Hastings31826802013-10-19 00:09:25 -0700285def
Larry Hastings2a727912014-01-16 11:32:01 -0800286[copy start generated code]*/
Larry Hastings31826802013-10-19 00:09:25 -0700287def
Larry Hastings7726ac92014-01-31 22:03:12 -0800288/*[copy end generated code: output=7b18d017f89f61cf input=7b18d017f89f61cf]*/
Larry Hastings31826802013-10-19 00:09:25 -0700289xyz
290""")
291
292
293class ClinicParserTest(TestCase):
294 def test_trivial(self):
295 parser = DSLParser(FakeClinic())
296 block = clinic.Block("module os\nos.access")
297 parser.parse(block)
298 module, function = block.signatures
299 self.assertEqual("access", function.name)
300 self.assertEqual("os", module.name)
301
302 def test_ignore_line(self):
303 block = self.parse("#\nmodule os\nos.access")
304 module, function = block.signatures
305 self.assertEqual("access", function.name)
306 self.assertEqual("os", module.name)
307
308 def test_param(self):
309 function = self.parse_function("module os\nos.access\n path: int")
310 self.assertEqual("access", function.name)
Larry Hastings7726ac92014-01-31 22:03:12 -0800311 self.assertEqual(2, len(function.parameters))
Larry Hastings31826802013-10-19 00:09:25 -0700312 p = function.parameters['path']
313 self.assertEqual('path', p.name)
314 self.assertIsInstance(p.converter, clinic.int_converter)
315
316 def test_param_default(self):
317 function = self.parse_function("module os\nos.access\n follow_symlinks: bool = True")
318 p = function.parameters['follow_symlinks']
319 self.assertEqual(True, p.default)
320
Larry Hastings1abd7082014-01-16 14:15:03 -0800321 def test_param_with_continuations(self):
322 function = self.parse_function("module os\nos.access\n follow_symlinks: \\\n bool \\\n =\\\n True")
323 p = function.parameters['follow_symlinks']
324 self.assertEqual(True, p.default)
325
326 def test_param_default_expression(self):
327 function = self.parse_function("module os\nos.access\n follow_symlinks: int(c_default='MAXSIZE') = sys.maxsize")
328 p = function.parameters['follow_symlinks']
329 self.assertEqual(sys.maxsize, p.default)
330 self.assertEqual("MAXSIZE", p.converter.c_default)
331
332 s = self.parse_function_should_fail("module os\nos.access\n follow_symlinks: int = sys.maxsize")
333 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")
334
Larry Hastings31826802013-10-19 00:09:25 -0700335 def test_param_no_docstring(self):
336 function = self.parse_function("""
337module os
338os.access
339 follow_symlinks: bool = True
Larry Hastings7726ac92014-01-31 22:03:12 -0800340 something_else: str = ''""")
Larry Hastings31826802013-10-19 00:09:25 -0700341 p = function.parameters['follow_symlinks']
Larry Hastings7726ac92014-01-31 22:03:12 -0800342 self.assertEqual(3, len(function.parameters))
Larry Hastings31826802013-10-19 00:09:25 -0700343 self.assertIsInstance(function.parameters['something_else'].converter, clinic.str_converter)
344
Larry Hastings7726ac92014-01-31 22:03:12 -0800345 def test_param_default_parameters_out_of_order(self):
346 s = self.parse_function_should_fail("""
347module os
348os.access
349 follow_symlinks: bool = True
350 something_else: str""")
351 self.assertEqual(s, """Error on line 0:
352Can't have a parameter without a default ('something_else')
353after a parameter with a default!
354""")
355
Larry Hastings31826802013-10-19 00:09:25 -0700356 def disabled_test_converter_arguments(self):
357 function = self.parse_function("module os\nos.access\n path: path_t(allow_fd=1)")
358 p = function.parameters['path']
359 self.assertEqual(1, p.converter.args['allow_fd'])
360
Larry Hastings31826802013-10-19 00:09:25 -0700361 def test_function_docstring(self):
362 function = self.parse_function("""
363module os
364os.stat as os_stat_fn
365
366 path: str
367 Path to be examined
368
369Perform a stat system call on the given path.""")
370 self.assertEqual("""
Larry Hastings2623c8c2014-02-08 22:15:29 -0800371stat($module, /, path)
372--
373
Larry Hastings31826802013-10-19 00:09:25 -0700374Perform a stat system call on the given path.
375
Larry Hastings31826802013-10-19 00:09:25 -0700376 path
377 Path to be examined
378""".strip(), function.docstring)
379
380 def test_explicit_parameters_in_docstring(self):
381 function = self.parse_function("""
382module foo
383foo.bar
384 x: int
385 Documentation for x.
386 y: int
387
388This is the documentation for foo.
389
390Okay, we're done here.
391""")
392 self.assertEqual("""
Larry Hastings2623c8c2014-02-08 22:15:29 -0800393bar($module, /, x, y)
394--
395
Larry Hastings31826802013-10-19 00:09:25 -0700396This is the documentation for foo.
397
Larry Hastings31826802013-10-19 00:09:25 -0700398 x
399 Documentation for x.
400
401Okay, we're done here.
402""".strip(), function.docstring)
403
404 def test_parser_regression_special_character_in_parameter_column_of_docstring_first_line(self):
405 function = self.parse_function("""
406module os
407os.stat
408 path: str
409This/used to break Clinic!
410""")
Larry Hastings2623c8c2014-02-08 22:15:29 -0800411 self.assertEqual("stat($module, /, path)\n--\n\nThis/used to break Clinic!", function.docstring)
Larry Hastings31826802013-10-19 00:09:25 -0700412
413 def test_c_name(self):
414 function = self.parse_function("module os\nos.stat as os_stat_fn")
415 self.assertEqual("os_stat_fn", function.c_basename)
416
417 def test_return_converter(self):
418 function = self.parse_function("module os\nos.stat -> int")
419 self.assertIsInstance(function.return_converter, clinic.int_return_converter)
420
421 def test_star(self):
422 function = self.parse_function("module os\nos.access\n *\n follow_symlinks: bool = True")
423 p = function.parameters['follow_symlinks']
424 self.assertEqual(inspect.Parameter.KEYWORD_ONLY, p.kind)
425 self.assertEqual(0, p.group)
426
427 def test_group(self):
428 function = self.parse_function("module window\nwindow.border\n [\n ls : int\n ]\n /\n")
429 p = function.parameters['ls']
430 self.assertEqual(1, p.group)
431
432 def test_left_group(self):
433 function = self.parse_function("""
434module curses
Larry Hastings6d2ea212014-01-05 02:50:45 -0800435curses.addch
Larry Hastings31826802013-10-19 00:09:25 -0700436 [
437 y: int
438 Y-coordinate.
439 x: int
440 X-coordinate.
441 ]
442 ch: char
443 Character to add.
444 [
445 attr: long
446 Attributes for the character.
447 ]
448 /
449""")
450 for name, group in (
451 ('y', -1), ('x', -1),
452 ('ch', 0),
453 ('attr', 1),
454 ):
455 p = function.parameters[name]
456 self.assertEqual(p.group, group)
457 self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
458 self.assertEqual(function.docstring.strip(), """
Larry Hastings6d2ea212014-01-05 02:50:45 -0800459addch([y, x,] ch, [attr])
460
461
Larry Hastings31826802013-10-19 00:09:25 -0700462 y
463 Y-coordinate.
464 x
465 X-coordinate.
466 ch
467 Character to add.
468 attr
469 Attributes for the character.
470 """.strip())
471
472 def test_nested_groups(self):
473 function = self.parse_function("""
474module curses
Larry Hastings6d2ea212014-01-05 02:50:45 -0800475curses.imaginary
Larry Hastings31826802013-10-19 00:09:25 -0700476 [
477 [
478 y1: int
479 Y-coordinate.
480 y2: int
481 Y-coordinate.
482 ]
483 x1: int
484 X-coordinate.
485 x2: int
486 X-coordinate.
487 ]
488 ch: char
489 Character to add.
490 [
491 attr1: long
492 Attributes for the character.
493 attr2: long
494 Attributes for the character.
495 attr3: long
496 Attributes for the character.
497 [
498 attr4: long
499 Attributes for the character.
500 attr5: long
501 Attributes for the character.
502 attr6: long
503 Attributes for the character.
504 ]
505 ]
506 /
507""")
508 for name, group in (
509 ('y1', -2), ('y2', -2),
510 ('x1', -1), ('x2', -1),
511 ('ch', 0),
512 ('attr1', 1), ('attr2', 1), ('attr3', 1),
513 ('attr4', 2), ('attr5', 2), ('attr6', 2),
514 ):
515 p = function.parameters[name]
516 self.assertEqual(p.group, group)
517 self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
518
519 self.assertEqual(function.docstring.strip(), """
Larry Hastings2623c8c2014-02-08 22:15:29 -0800520imaginary([[y1, y2,] x1, x2,] ch, [attr1, attr2, attr3, [attr4, attr5,
521 attr6]])
Larry Hastings6d2ea212014-01-05 02:50:45 -0800522
523
Larry Hastings31826802013-10-19 00:09:25 -0700524 y1
525 Y-coordinate.
526 y2
527 Y-coordinate.
528 x1
529 X-coordinate.
530 x2
531 X-coordinate.
532 ch
533 Character to add.
534 attr1
535 Attributes for the character.
536 attr2
537 Attributes for the character.
538 attr3
539 Attributes for the character.
540 attr4
541 Attributes for the character.
542 attr5
543 Attributes for the character.
544 attr6
545 Attributes for the character.
546 """.strip())
547
548 def parse_function_should_fail(self, s):
549 with support.captured_stdout() as stdout:
550 with self.assertRaises(SystemExit):
551 self.parse_function(s)
552 return stdout.getvalue()
553
554 def test_disallowed_grouping__two_top_groups_on_left(self):
555 s = self.parse_function_should_fail("""
556module foo
557foo.two_top_groups_on_left
558 [
559 group1 : int
560 ]
561 [
562 group2 : int
563 ]
564 param: int
565 """)
566 self.assertEqual(s,
567 ('Error on line 0:\n'
Larry Hastings7726ac92014-01-31 22:03:12 -0800568 'Function two_top_groups_on_left has an unsupported group configuration. (Unexpected state 2.b)\n'))
Larry Hastings31826802013-10-19 00:09:25 -0700569
570 def test_disallowed_grouping__two_top_groups_on_right(self):
571 self.parse_function_should_fail("""
572module foo
573foo.two_top_groups_on_right
574 param: int
575 [
576 group1 : int
577 ]
578 [
579 group2 : int
580 ]
581 """)
582
583 def test_disallowed_grouping__parameter_after_group_on_right(self):
584 self.parse_function_should_fail("""
585module foo
586foo.parameter_after_group_on_right
587 param: int
588 [
589 [
590 group1 : int
591 ]
592 group2 : int
593 ]
594 """)
595
596 def test_disallowed_grouping__group_after_parameter_on_left(self):
597 self.parse_function_should_fail("""
598module foo
599foo.group_after_parameter_on_left
600 [
601 group2 : int
602 [
603 group1 : int
604 ]
605 ]
606 param: int
607 """)
608
609 def test_disallowed_grouping__empty_group_on_left(self):
610 self.parse_function_should_fail("""
611module foo
612foo.empty_group
613 [
614 [
615 ]
616 group2 : int
617 ]
618 param: int
619 """)
620
621 def test_disallowed_grouping__empty_group_on_right(self):
622 self.parse_function_should_fail("""
623module foo
624foo.empty_group
625 param: int
626 [
627 [
628 ]
629 group2 : int
630 ]
631 """)
632
633 def test_no_parameters(self):
634 function = self.parse_function("""
635module foo
636foo.bar
637
638Docstring
639
640""")
Larry Hastings2623c8c2014-02-08 22:15:29 -0800641 self.assertEqual("bar($module, /)\n--\n\nDocstring", function.docstring)
Larry Hastings7726ac92014-01-31 22:03:12 -0800642 self.assertEqual(1, len(function.parameters)) # self!
Larry Hastings31826802013-10-19 00:09:25 -0700643
Larry Hastings2623c8c2014-02-08 22:15:29 -0800644 def test_init_with_no_parameters(self):
645 function = self.parse_function("""
646module foo
647class foo.Bar "unused" "notneeded"
648foo.Bar.__init__
649
650Docstring
651
652""", signatures_in_block=3, function_index=2)
653 # self is not in the signature
654 self.assertEqual("Bar()\n--\n\nDocstring", function.docstring)
655 # but it *is* a parameter
656 self.assertEqual(1, len(function.parameters))
657
Larry Hastingsdfcd4672013-10-27 02:49:39 -0700658 def test_illegal_module_line(self):
659 self.parse_function_should_fail("""
660module foo
661foo.bar => int
662 /
663""")
664
665 def test_illegal_c_basename(self):
666 self.parse_function_should_fail("""
667module foo
668foo.bar as 935
669 /
670""")
671
Larry Hastings31826802013-10-19 00:09:25 -0700672 def test_single_star(self):
673 self.parse_function_should_fail("""
674module foo
675foo.bar
676 *
677 *
678""")
679
680 def test_parameters_required_after_star_without_initial_parameters_or_docstring(self):
681 self.parse_function_should_fail("""
682module foo
683foo.bar
684 *
685""")
686
687 def test_parameters_required_after_star_without_initial_parameters_with_docstring(self):
688 self.parse_function_should_fail("""
689module foo
690foo.bar
691 *
692Docstring here.
693""")
694
695 def test_parameters_required_after_star_with_initial_parameters_without_docstring(self):
696 self.parse_function_should_fail("""
697module foo
698foo.bar
699 this: int
700 *
701""")
702
703 def test_parameters_required_after_star_with_initial_parameters_and_docstring(self):
704 self.parse_function_should_fail("""
705module foo
706foo.bar
707 this: int
708 *
709Docstring.
710""")
711
712 def test_single_slash(self):
713 self.parse_function_should_fail("""
714module foo
715foo.bar
716 /
717 /
718""")
719
720 def test_mix_star_and_slash(self):
721 self.parse_function_should_fail("""
722module foo
723foo.bar
724 x: int
725 y: int
726 *
727 z: int
728 /
729""")
730
731 def test_parameters_not_permitted_after_slash_for_now(self):
732 self.parse_function_should_fail("""
733module foo
734foo.bar
735 /
736 x: int
737""")
738
739 def test_function_not_at_column_0(self):
740 function = self.parse_function("""
741 module foo
742 foo.bar
743 x: int
744 Nested docstring here, goeth.
745 *
746 y: str
747 Not at column 0!
748""")
749 self.assertEqual("""
Larry Hastings2623c8c2014-02-08 22:15:29 -0800750bar($module, /, x, *, y)
751--
752
Larry Hastings31826802013-10-19 00:09:25 -0700753Not at column 0!
754
Larry Hastings31826802013-10-19 00:09:25 -0700755 x
756 Nested docstring here, goeth.
757""".strip(), function.docstring)
758
Larry Hastings31826802013-10-19 00:09:25 -0700759 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
Serhiy Storchaka837c7dc2018-12-25 10:17:28 +0200800class ClinicExternalTest(TestCase):
801 maxDiff = None
802
803 def test_external(self):
804 source = support.findfile('clinic.test')
805 with open(source, 'r', encoding='utf-8') as f:
806 original = f.read()
807 with support.temp_dir() as testdir:
808 testfile = os.path.join(testdir, 'clinic.test.c')
809 with open(testfile, 'w', encoding='utf-8') as f:
810 f.write(original)
811 clinic.parse_file(testfile, force=True)
812 with open(testfile, 'r', encoding='utf-8') as f:
813 result = f.read()
814 self.assertEqual(result, original)
815
816
Larry Hastings31826802013-10-19 00:09:25 -0700817if __name__ == "__main__":
818 unittest.main()