blob: 023a034e09761f13dbf6ccb793b7bad7948b49b9 [file] [log] [blame]
Larry Hastings31826802013-10-19 00:09:25 -07001#!/usr/bin/env python3
2#
3# Argument Clinic
4# Copyright 2012-2013 by Larry Hastings.
5# Licensed to the PSF under a contributor agreement.
6#
7
8import abc
9import ast
10import atexit
11import clinic
12import collections
13import contextlib
14import functools
15import hashlib
16import inspect
17import io
18import itertools
19import os
20import re
21import shlex
22import sys
23import tempfile
24import textwrap
25
Larry Hastings31826802013-10-19 00:09:25 -070026# TODO:
Larry Hastings31826802013-10-19 00:09:25 -070027#
28# soon:
29#
30# * allow mixing any two of {positional-only, positional-or-keyword,
31# keyword-only}
32# * dict constructor uses positional-only and keyword-only
33# * max and min use positional only with an optional group
34# and keyword-only
35#
Larry Hastings31826802013-10-19 00:09:25 -070036
Larry Hastingsebdcb502013-11-23 14:54:00 -080037version = '1'
38
Larry Hastings31826802013-10-19 00:09:25 -070039_empty = inspect._empty
40_void = inspect._void
41
42
43class Unspecified:
44 def __repr__(self):
45 return '<Unspecified>'
46
47unspecified = Unspecified()
48
49
50class Null:
51 def __repr__(self):
52 return '<Null>'
53
54NULL = Null()
55
56
57def _text_accumulator():
58 text = []
59 def output():
60 s = ''.join(text)
61 text.clear()
62 return s
63 return text, text.append, output
64
65
66def text_accumulator():
67 """
68 Creates a simple text accumulator / joiner.
69
70 Returns a pair of callables:
71 append, output
72 "append" appends a string to the accumulator.
73 "output" returns the contents of the accumulator
74 joined together (''.join(accumulator)) and
75 empties the accumulator.
76 """
77 text, append, output = _text_accumulator()
78 return append, output
79
80
81def fail(*args, filename=None, line_number=None):
82 joined = " ".join([str(a) for a in args])
83 add, output = text_accumulator()
84 add("Error")
85 if clinic:
86 if filename is None:
87 filename = clinic.filename
88 if clinic.block_parser and (line_number is None):
89 line_number = clinic.block_parser.line_number
90 if filename is not None:
91 add(' in file "' + filename + '"')
92 if line_number is not None:
93 add(" on line " + str(line_number))
94 add(':\n')
95 add(joined)
96 print(output())
97 sys.exit(-1)
98
99
100
101def quoted_for_c_string(s):
102 for old, new in (
103 ('"', '\\"'),
104 ("'", "\\'"),
105 ):
106 s = s.replace(old, new)
107 return s
108
Larry Hastingsdfcd4672013-10-27 02:49:39 -0700109is_legal_c_identifier = re.compile('^[A-Za-z_][A-Za-z0-9_]*$').match
110
111def is_legal_py_identifier(s):
112 return all(is_legal_c_identifier(field) for field in s.split('.'))
113
Larry Hastingsed4a1c52013-11-18 09:32:13 -0800114# added "module", "self", "cls", and "null" just to be safe
Larry Hastings31826802013-10-19 00:09:25 -0700115# (clinic will generate variables with these names)
116c_keywords = set("""
117asm auto break case char cls const continue default do double
Larry Hastingsed4a1c52013-11-18 09:32:13 -0800118else enum extern float for goto if inline int long module null
119register return self short signed sizeof static struct switch
120typedef typeof union unsigned void volatile while
Larry Hastings31826802013-10-19 00:09:25 -0700121""".strip().split())
122
Larry Hastingsdfcd4672013-10-27 02:49:39 -0700123def ensure_legal_c_identifier(s):
124 # for now, just complain if what we're given isn't legal
125 if not is_legal_c_identifier(s):
126 fail("Illegal C identifier: {}".format(s))
127 # but if we picked a C keyword, pick something else
Larry Hastings31826802013-10-19 00:09:25 -0700128 if s in c_keywords:
129 return s + "_value"
130 return s
131
132def rstrip_lines(s):
133 text, add, output = _text_accumulator()
134 for line in s.split('\n'):
135 add(line.rstrip())
136 add('\n')
137 text.pop()
138 return output()
139
140def linear_format(s, **kwargs):
141 """
142 Perform str.format-like substitution, except:
143 * The strings substituted must be on lines by
144 themselves. (This line is the "source line".)
145 * If the substitution text is empty, the source line
146 is removed in the output.
147 * If the substitution text is not empty:
148 * Each line of the substituted text is indented
149 by the indent of the source line.
150 * A newline will be added to the end.
151 """
152
153 add, output = text_accumulator()
154 for line in s.split('\n'):
155 indent, curly, trailing = line.partition('{')
156 if not curly:
157 add(line)
158 add('\n')
159 continue
160
161 name, curl, trailing = trailing.partition('}')
162 if not curly or name not in kwargs:
163 add(line)
164 add('\n')
165 continue
166
167 if trailing:
168 fail("Text found after {" + name + "} block marker! It must be on a line by itself.")
169 if indent.strip():
170 fail("Non-whitespace characters found before {" + name + "} block marker! It must be on a line by itself.")
171
172 value = kwargs[name]
173 if not value:
174 continue
175
176 value = textwrap.indent(rstrip_lines(value), indent)
177 add(value)
178 add('\n')
179
180 return output()[:-1]
181
Larry Hastingsebdcb502013-11-23 14:54:00 -0800182def version_splitter(s):
183 """Splits a version string into a tuple of integers.
184
185 The following ASCII characters are allowed, and employ
186 the following conversions:
187 a -> -3
188 b -> -2
189 c -> -1
190 (This permits Python-style version strings such as "1.4b3".)
191 """
192 version = []
193 accumulator = []
194 def flush():
195 if not accumulator:
196 raise ValueError('Malformed version string: ' + repr(s))
197 version.append(int(''.join(accumulator)))
198 accumulator.clear()
199
200 for c in s:
201 if c.isdigit():
202 accumulator.append(c)
203 elif c == '.':
204 flush()
205 elif c in 'abc':
206 flush()
207 version.append('abc'.index(c) - 3)
208 else:
209 raise ValueError('Illegal character ' + repr(c) + ' in version string ' + repr(s))
210 flush()
211 return tuple(version)
212
213def version_comparitor(version1, version2):
214 iterator = itertools.zip_longest(version_splitter(version1), version_splitter(version2), fillvalue=0)
215 for i, (a, b) in enumerate(iterator):
216 if a < b:
217 return -1
218 if a > b:
219 return 1
220 return 0
221
Larry Hastings31826802013-10-19 00:09:25 -0700222
223class CRenderData:
224 def __init__(self):
225
226 # The C statements to declare variables.
227 # Should be full lines with \n eol characters.
228 self.declarations = []
229
230 # The C statements required to initialize the variables before the parse call.
231 # Should be full lines with \n eol characters.
232 self.initializers = []
233
234 # The entries for the "keywords" array for PyArg_ParseTuple.
235 # Should be individual strings representing the names.
236 self.keywords = []
237
238 # The "format units" for PyArg_ParseTuple.
239 # Should be individual strings that will get
240 self.format_units = []
241
242 # The varargs arguments for PyArg_ParseTuple.
243 self.parse_arguments = []
244
245 # The parameter declarations for the impl function.
246 self.impl_parameters = []
247
248 # The arguments to the impl function at the time it's called.
249 self.impl_arguments = []
250
251 # For return converters: the name of the variable that
252 # should receive the value returned by the impl.
253 self.return_value = "return_value"
254
255 # For return converters: the code to convert the return
256 # value from the parse function. This is also where
257 # you should check the _return_value for errors, and
258 # "goto exit" if there are any.
259 self.return_conversion = []
260
261 # The C statements required to clean up after the impl call.
262 self.cleanup = []
263
264
265class Language(metaclass=abc.ABCMeta):
266
267 start_line = ""
268 body_prefix = ""
269 stop_line = ""
270 checksum_line = ""
271
272 @abc.abstractmethod
273 def render(self, block):
274 pass
275
276 def validate(self):
277 def assert_only_one(field, token='dsl_name'):
278 line = getattr(self, field)
279 token = '{' + token + '}'
280 if len(line.split(token)) != 2:
281 fail(self.__class__.__name__ + " " + field + " must contain " + token + " exactly once!")
282 assert_only_one('start_line')
283 assert_only_one('stop_line')
284 assert_only_one('checksum_line')
285 assert_only_one('checksum_line', 'checksum')
286
287 if len(self.body_prefix.split('{dsl_name}')) >= 3:
288 fail(self.__class__.__name__ + " body_prefix may contain " + token + " once at most!")
289
290
291
292class PythonLanguage(Language):
293
294 language = 'Python'
295 start_line = "#/*[{dsl_name}]"
296 body_prefix = "#"
297 stop_line = "#[{dsl_name}]*/"
298 checksum_line = "#/*[{dsl_name} checksum: {checksum}]*/"
299
300
301def permute_left_option_groups(l):
302 """
303 Given [1, 2, 3], should yield:
304 ()
305 (3,)
306 (2, 3)
307 (1, 2, 3)
308 """
309 yield tuple()
310 accumulator = []
311 for group in reversed(l):
312 accumulator = list(group) + accumulator
313 yield tuple(accumulator)
314
315
316def permute_right_option_groups(l):
317 """
318 Given [1, 2, 3], should yield:
319 ()
320 (1,)
321 (1, 2)
322 (1, 2, 3)
323 """
324 yield tuple()
325 accumulator = []
326 for group in l:
327 accumulator.extend(group)
328 yield tuple(accumulator)
329
330
331def permute_optional_groups(left, required, right):
332 """
333 Generator function that computes the set of acceptable
334 argument lists for the provided iterables of
335 argument groups. (Actually it generates a tuple of tuples.)
336
337 Algorithm: prefer left options over right options.
338
339 If required is empty, left must also be empty.
340 """
341 required = tuple(required)
342 result = []
343
344 if not required:
345 assert not left
346
347 accumulator = []
348 counts = set()
349 for r in permute_right_option_groups(right):
350 for l in permute_left_option_groups(left):
351 t = l + required + r
352 if len(t) in counts:
353 continue
354 counts.add(len(t))
355 accumulator.append(t)
356
357 accumulator.sort(key=len)
358 return tuple(accumulator)
359
360
361class CLanguage(Language):
362
363 language = 'C'
364 start_line = "/*[{dsl_name}]"
365 body_prefix = ""
366 stop_line = "[{dsl_name}]*/"
367 checksum_line = "/*[{dsl_name} checksum: {checksum}]*/"
368
369 def render(self, signatures):
370 function = None
371 for o in signatures:
372 if isinstance(o, Function):
373 if function:
374 fail("You may specify at most one function per block.\nFound a block containing at least two:\n\t" + repr(function) + " and " + repr(o))
375 function = o
376 return self.render_function(function)
377
378 def docstring_for_c_string(self, f):
379 text, add, output = _text_accumulator()
380 # turn docstring into a properly quoted C string
381 for line in f.docstring.split('\n'):
382 add('"')
383 add(quoted_for_c_string(line))
384 add('\\n"\n')
385
386 text.pop()
387 add('"')
388 return ''.join(text)
389
390 impl_prototype_template = "{c_basename}_impl({impl_parameters})"
391
392 @staticmethod
393 def template_base(*args):
394 flags = '|'.join(f for f in args if f)
395 return """
396PyDoc_STRVAR({c_basename}__doc__,
397{docstring});
398
399#define {methoddef_name} \\
Larry Hastingsebdcb502013-11-23 14:54:00 -0800400 {{"{name}", (PyCFunction){c_basename}, {methoddef_flags}, {c_basename}__doc__}},
401""".replace('{methoddef_flags}', flags)
Larry Hastings31826802013-10-19 00:09:25 -0700402
Larry Hastingsebdcb502013-11-23 14:54:00 -0800403 def meth_noargs_template(self, methoddef_flags=""):
404 return self.template_base("METH_NOARGS", methoddef_flags) + """
Larry Hastings31826802013-10-19 00:09:25 -0700405static {impl_return_type}
406{impl_prototype};
407
408static PyObject *
Larry Hastings3cceb382014-01-04 11:09:09 -0800409{c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored))
Larry Hastings31826802013-10-19 00:09:25 -0700410{{
411 PyObject *return_value = NULL;
412 {declarations}
413 {initializers}
414
415 {return_value} = {c_basename}_impl({impl_arguments});
416 {return_conversion}
417
418{exit_label}
419 {cleanup}
420 return return_value;
421}}
422
423static {impl_return_type}
424{impl_prototype}
425"""
426
Larry Hastingsebdcb502013-11-23 14:54:00 -0800427 def meth_o_template(self, methoddef_flags=""):
428 return self.template_base("METH_O", methoddef_flags) + """
Larry Hastings31826802013-10-19 00:09:25 -0700429static PyObject *
430{c_basename}({impl_parameters})
431"""
432
Larry Hastingsebdcb502013-11-23 14:54:00 -0800433 def meth_o_return_converter_template(self, methoddef_flags=""):
434 return self.template_base("METH_O", methoddef_flags) + """
Larry Hastings31826802013-10-19 00:09:25 -0700435static {impl_return_type}
436{impl_prototype};
437
438static PyObject *
439{c_basename}({impl_parameters})
440{{
441 PyObject *return_value = NULL;
442 {declarations}
443 {initializers}
444 _return_value = {c_basename}_impl({impl_arguments});
445 {return_conversion}
446
447{exit_label}
448 {cleanup}
449 return return_value;
450}}
451
452static {impl_return_type}
453{impl_prototype}
454"""
455
Larry Hastingsebdcb502013-11-23 14:54:00 -0800456 def option_group_template(self, methoddef_flags=""):
457 return self.template_base("METH_VARARGS", methoddef_flags) + """
Larry Hastings31826802013-10-19 00:09:25 -0700458static {impl_return_type}
459{impl_prototype};
460
461static PyObject *
Larry Hastingsebdcb502013-11-23 14:54:00 -0800462{c_basename}({self_type}{self_name}, PyObject *args)
Larry Hastings31826802013-10-19 00:09:25 -0700463{{
464 PyObject *return_value = NULL;
465 {declarations}
466 {initializers}
467
468 {option_group_parsing}
469 {return_value} = {c_basename}_impl({impl_arguments});
470 {return_conversion}
471
472{exit_label}
473 {cleanup}
474 return return_value;
475}}
476
477static {impl_return_type}
478{impl_prototype}
479"""
480
Larry Hastingsebdcb502013-11-23 14:54:00 -0800481 def keywords_template(self, methoddef_flags=""):
482 return self.template_base("METH_VARARGS|METH_KEYWORDS", methoddef_flags) + """
Larry Hastings31826802013-10-19 00:09:25 -0700483static {impl_return_type}
484{impl_prototype};
485
486static PyObject *
Larry Hastingsebdcb502013-11-23 14:54:00 -0800487{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
Larry Hastings31826802013-10-19 00:09:25 -0700488{{
489 PyObject *return_value = NULL;
490 static char *_keywords[] = {{{keywords}, NULL}};
491 {declarations}
492 {initializers}
493
494 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
495 "{format_units}:{name}", _keywords,
496 {parse_arguments}))
497 goto exit;
498 {return_value} = {c_basename}_impl({impl_arguments});
499 {return_conversion}
500
501{exit_label}
502 {cleanup}
503 return return_value;
504}}
505
506static {impl_return_type}
507{impl_prototype}
508"""
509
Larry Hastingsebdcb502013-11-23 14:54:00 -0800510 def positional_only_template(self, methoddef_flags=""):
511 return self.template_base("METH_VARARGS", methoddef_flags) + """
Larry Hastings31826802013-10-19 00:09:25 -0700512static {impl_return_type}
513{impl_prototype};
514
515static PyObject *
Larry Hastingsebdcb502013-11-23 14:54:00 -0800516{c_basename}({self_type}{self_name}, PyObject *args)
Larry Hastings31826802013-10-19 00:09:25 -0700517{{
518 PyObject *return_value = NULL;
519 {declarations}
520 {initializers}
521
522 if (!PyArg_ParseTuple(args,
523 "{format_units}:{name}",
524 {parse_arguments}))
525 goto exit;
526 {return_value} = {c_basename}_impl({impl_arguments});
527 {return_conversion}
528
529{exit_label}
530 {cleanup}
531 return return_value;
532}}
533
534static {impl_return_type}
535{impl_prototype}
536"""
537
538 @staticmethod
539 def group_to_variable_name(group):
540 adjective = "left_" if group < 0 else "right_"
541 return "group_" + adjective + str(abs(group))
542
543 def render_option_group_parsing(self, f, template_dict):
544 # positional only, grouped, optional arguments!
545 # can be optional on the left or right.
546 # here's an example:
547 #
548 # [ [ [ A1 A2 ] B1 B2 B3 ] C1 C2 ] D1 D2 D3 [ E1 E2 E3 [ F1 F2 F3 ] ]
549 #
550 # Here group D are required, and all other groups are optional.
551 # (Group D's "group" is actually None.)
552 # We can figure out which sets of arguments we have based on
553 # how many arguments are in the tuple.
554 #
555 # Note that you need to count up on both sides. For example,
556 # you could have groups C+D, or C+D+E, or C+D+E+F.
557 #
558 # What if the number of arguments leads us to an ambiguous result?
559 # Clinic prefers groups on the left. So in the above example,
560 # five arguments would map to B+C, not C+D.
561
562 add, output = text_accumulator()
563 parameters = list(f.parameters.values())
564
565 groups = []
566 group = None
567 left = []
568 right = []
569 required = []
570 last = unspecified
571
572 for p in parameters:
573 group_id = p.group
574 if group_id != last:
575 last = group_id
576 group = []
577 if group_id < 0:
578 left.append(group)
579 elif group_id == 0:
580 group = required
581 else:
582 right.append(group)
583 group.append(p)
584
585 count_min = sys.maxsize
586 count_max = -1
587
588 add("switch (PyTuple_Size(args)) {{\n")
589 for subset in permute_optional_groups(left, required, right):
590 count = len(subset)
591 count_min = min(count_min, count)
592 count_max = max(count_max, count)
593
594 group_ids = {p.group for p in subset} # eliminate duplicates
595 d = {}
596 d['count'] = count
597 d['name'] = f.name
598 d['groups'] = sorted(group_ids)
599 d['format_units'] = "".join(p.converter.format_unit for p in subset)
600
601 parse_arguments = []
602 for p in subset:
603 p.converter.parse_argument(parse_arguments)
604 d['parse_arguments'] = ", ".join(parse_arguments)
605
606 group_ids.discard(0)
607 lines = [self.group_to_variable_name(g) + " = 1;" for g in group_ids]
608 lines = "\n".join(lines)
609
610 s = """
611 case {count}:
612 if (!PyArg_ParseTuple(args, "{format_units}:{name}", {parse_arguments}))
613 return NULL;
614 {group_booleans}
615 break;
616"""[1:]
617 s = linear_format(s, group_booleans=lines)
618 s = s.format_map(d)
619 add(s)
620
621 add(" default:\n")
622 s = ' PyErr_SetString(PyExc_TypeError, "{} requires {} to {} arguments");\n'
623 add(s.format(f.full_name, count_min, count_max))
624 add(' return NULL;\n')
625 add("}}")
626 template_dict['option_group_parsing'] = output()
627
628 def render_function(self, f):
629 if not f:
630 return ""
631
632 add, output = text_accumulator()
633 data = CRenderData()
634
Larry Hastings31826802013-10-19 00:09:25 -0700635 parameters = list(f.parameters.values())
636 converters = [p.converter for p in parameters]
637
638 template_dict = {}
639
640 full_name = f.full_name
641 template_dict['full_name'] = full_name
642
643 name = full_name.rpartition('.')[2]
644 template_dict['name'] = name
645
646 c_basename = f.c_basename or full_name.replace(".", "_")
647 template_dict['c_basename'] = c_basename
648
649 methoddef_name = "{}_METHODDEF".format(c_basename.upper())
650 template_dict['methoddef_name'] = methoddef_name
651
652 template_dict['docstring'] = self.docstring_for_c_string(f)
653
Larry Hastings31826802013-10-19 00:09:25 -0700654 positional = has_option_groups = False
655
656 if parameters:
657 last_group = 0
658
659 for p in parameters:
660 c = p.converter
661
662 # insert group variable
663 group = p.group
664 if last_group != group:
665 last_group = group
666 if group:
667 group_name = self.group_to_variable_name(group)
668 data.impl_arguments.append(group_name)
669 data.declarations.append("int " + group_name + " = 0;")
670 data.impl_parameters.append("int " + group_name)
671 has_option_groups = True
672 c.render(p, data)
673
674 positional = parameters[-1].kind == inspect.Parameter.POSITIONAL_ONLY
675 if has_option_groups:
676 assert positional
677
Larry Hastingsebdcb502013-11-23 14:54:00 -0800678 # now insert our "self" (or whatever) parameters
679 # (we deliberately don't call render on self converters)
680 stock_self = self_converter('self', f)
681 template_dict['self_name'] = stock_self.name
682 template_dict['self_type'] = stock_self.type
683 data.impl_parameters.insert(0, f.self_converter.type + ("" if f.self_converter.type.endswith('*') else " ") + f.self_converter.name)
684 if f.self_converter.type != stock_self.type:
685 self_cast = '(' + f.self_converter.type + ')'
686 else:
687 self_cast = ''
688 data.impl_arguments.insert(0, self_cast + stock_self.name)
689
Larry Hastings31826802013-10-19 00:09:25 -0700690 f.return_converter.render(f, data)
691 template_dict['impl_return_type'] = f.return_converter.type
692
693 template_dict['declarations'] = "\n".join(data.declarations)
694 template_dict['initializers'] = "\n\n".join(data.initializers)
695 template_dict['keywords'] = '"' + '", "'.join(data.keywords) + '"'
696 template_dict['format_units'] = ''.join(data.format_units)
697 template_dict['parse_arguments'] = ', '.join(data.parse_arguments)
698 template_dict['impl_parameters'] = ", ".join(data.impl_parameters)
699 template_dict['impl_arguments'] = ", ".join(data.impl_arguments)
700 template_dict['return_conversion'] = "".join(data.return_conversion).rstrip()
701 template_dict['cleanup'] = "".join(data.cleanup)
702 template_dict['return_value'] = data.return_value
703
704 template_dict['impl_prototype'] = self.impl_prototype_template.format_map(template_dict)
705
706 default_return_converter = (not f.return_converter or
707 f.return_converter.type == 'PyObject *')
708
709 if not parameters:
Larry Hastings3cceb382014-01-04 11:09:09 -0800710 template = self.meth_noargs_template(f.methoddef_flags)
Larry Hastings31826802013-10-19 00:09:25 -0700711 elif (len(parameters) == 1 and
712 parameters[0].kind == inspect.Parameter.POSITIONAL_ONLY and
713 not converters[0].is_optional() and
714 isinstance(converters[0], object_converter) and
715 converters[0].format_unit == 'O'):
716 if default_return_converter:
Larry Hastingsebdcb502013-11-23 14:54:00 -0800717 template = self.meth_o_template(f.methoddef_flags)
Larry Hastings31826802013-10-19 00:09:25 -0700718 else:
719 # HACK
720 # we're using "impl_parameters" for the
721 # non-impl function, because that works
722 # better for METH_O. but that means we
Larry Hastingsebdcb502013-11-23 14:54:00 -0800723 # must supress actually declaring the
Larry Hastings31826802013-10-19 00:09:25 -0700724 # impl's parameters as variables in the
725 # non-impl. but since it's METH_O, we
Larry Hastingsebdcb502013-11-23 14:54:00 -0800726 # only have one anyway, so
727 # we don't have any problem finding it.
Larry Hastings31826802013-10-19 00:09:25 -0700728 declarations_copy = list(data.declarations)
729 before, pyobject, after = declarations_copy[0].partition('PyObject *')
730 assert not before, "hack failed, see comment"
731 assert pyobject, "hack failed, see comment"
732 assert after and after[0].isalpha(), "hack failed, see comment"
733 del declarations_copy[0]
734 template_dict['declarations'] = "\n".join(declarations_copy)
Larry Hastingsebdcb502013-11-23 14:54:00 -0800735 template = self.meth_o_return_converter_template(f.methoddef_flags)
Larry Hastings31826802013-10-19 00:09:25 -0700736 elif has_option_groups:
737 self.render_option_group_parsing(f, template_dict)
Larry Hastingsebdcb502013-11-23 14:54:00 -0800738 template = self.option_group_template(f.methoddef_flags)
Larry Hastings31826802013-10-19 00:09:25 -0700739 template = linear_format(template,
740 option_group_parsing=template_dict['option_group_parsing'])
741 elif positional:
Larry Hastingsebdcb502013-11-23 14:54:00 -0800742 template = self.positional_only_template(f.methoddef_flags)
Larry Hastings31826802013-10-19 00:09:25 -0700743 else:
Larry Hastingsebdcb502013-11-23 14:54:00 -0800744 template = self.keywords_template(f.methoddef_flags)
Larry Hastings31826802013-10-19 00:09:25 -0700745
746 template = linear_format(template,
747 declarations=template_dict['declarations'],
748 return_conversion=template_dict['return_conversion'],
749 initializers=template_dict['initializers'],
750 cleanup=template_dict['cleanup'],
751 )
752
753 # Only generate the "exit:" label
754 # if we have any gotos
755 need_exit_label = "goto exit;" in template
756 template = linear_format(template,
757 exit_label="exit:" if need_exit_label else ''
758 )
759
760 return template.format_map(template_dict)
761
762
763@contextlib.contextmanager
764def OverrideStdioWith(stdout):
765 saved_stdout = sys.stdout
766 sys.stdout = stdout
767 try:
768 yield
769 finally:
770 assert sys.stdout is stdout
771 sys.stdout = saved_stdout
772
773
774def create_regex(before, after):
775 """Create an re object for matching marker lines."""
776 pattern = r'^{}(\w+){}$'
777 return re.compile(pattern.format(re.escape(before), re.escape(after)))
778
779
780class Block:
781 r"""
782 Represents a single block of text embedded in
783 another file. If dsl_name is None, the block represents
784 verbatim text, raw original text from the file, in
785 which case "input" will be the only non-false member.
786 If dsl_name is not None, the block represents a Clinic
787 block.
788
789 input is always str, with embedded \n characters.
790 input represents the original text from the file;
791 if it's a Clinic block, it is the original text with
792 the body_prefix and redundant leading whitespace removed.
793
794 dsl_name is either str or None. If str, it's the text
795 found on the start line of the block between the square
796 brackets.
797
798 signatures is either list or None. If it's a list,
799 it may only contain clinic.Module, clinic.Class, and
800 clinic.Function objects. At the moment it should
801 contain at most one of each.
802
803 output is either str or None. If str, it's the output
804 from this block, with embedded '\n' characters.
805
806 indent is either str or None. It's the leading whitespace
807 that was found on every line of input. (If body_prefix is
808 not empty, this is the indent *after* removing the
809 body_prefix.)
810
811 preindent is either str or None. It's the whitespace that
812 was found in front of every line of input *before* the
813 "body_prefix" (see the Language object). If body_prefix
814 is empty, preindent must always be empty too.
815
816 To illustrate indent and preindent: Assume that '_'
817 represents whitespace. If the block processed was in a
818 Python file, and looked like this:
819 ____#/*[python]
820 ____#__for a in range(20):
821 ____#____print(a)
822 ____#[python]*/
823 "preindent" would be "____" and "indent" would be "__".
824
825 """
826 def __init__(self, input, dsl_name=None, signatures=None, output=None, indent='', preindent=''):
827 assert isinstance(input, str)
828 self.input = input
829 self.dsl_name = dsl_name
830 self.signatures = signatures or []
831 self.output = output
832 self.indent = indent
833 self.preindent = preindent
834
835
836class BlockParser:
837 """
838 Block-oriented parser for Argument Clinic.
839 Iterator, yields Block objects.
840 """
841
842 def __init__(self, input, language, *, verify=True):
843 """
844 "input" should be a str object
845 with embedded \n characters.
846
847 "language" should be a Language object.
848 """
849 language.validate()
850
851 self.input = collections.deque(reversed(input.splitlines(keepends=True)))
852 self.block_start_line_number = self.line_number = 0
853
854 self.language = language
855 before, _, after = language.start_line.partition('{dsl_name}')
856 assert _ == '{dsl_name}'
857 self.start_re = create_regex(before, after)
858 self.verify = verify
859 self.last_checksum_re = None
860 self.last_dsl_name = None
861 self.dsl_name = None
862
863 def __iter__(self):
864 return self
865
866 def __next__(self):
867 if not self.input:
868 raise StopIteration
869
870 if self.dsl_name:
871 return_value = self.parse_clinic_block(self.dsl_name)
872 self.dsl_name = None
873 return return_value
874 return self.parse_verbatim_block()
875
876 def is_start_line(self, line):
877 match = self.start_re.match(line.lstrip())
878 return match.group(1) if match else None
879
880 def _line(self):
881 self.line_number += 1
882 return self.input.pop()
883
884 def parse_verbatim_block(self):
885 add, output = text_accumulator()
886 self.block_start_line_number = self.line_number
887
888 while self.input:
889 line = self._line()
890 dsl_name = self.is_start_line(line)
891 if dsl_name:
892 self.dsl_name = dsl_name
893 break
894 add(line)
895
896 return Block(output())
897
898 def parse_clinic_block(self, dsl_name):
899 input_add, input_output = text_accumulator()
900 self.block_start_line_number = self.line_number + 1
901 stop_line = self.language.stop_line.format(dsl_name=dsl_name) + '\n'
902 body_prefix = self.language.body_prefix.format(dsl_name=dsl_name)
903
904 # consume body of program
905 while self.input:
906 line = self._line()
907 if line == stop_line or self.is_start_line(line):
908 break
909 if body_prefix:
910 line = line.lstrip()
911 assert line.startswith(body_prefix)
912 line = line[len(body_prefix):]
913 input_add(line)
914
915 # consume output and checksum line, if present.
916 if self.last_dsl_name == dsl_name:
917 checksum_re = self.last_checksum_re
918 else:
919 before, _, after = self.language.checksum_line.format(dsl_name=dsl_name, checksum='{checksum}').partition('{checksum}')
920 assert _ == '{checksum}'
921 checksum_re = create_regex(before, after)
922 self.last_dsl_name = dsl_name
923 self.last_checksum_re = checksum_re
924
925 # scan forward for checksum line
926 output_add, output_output = text_accumulator()
927 checksum = None
928 while self.input:
929 line = self._line()
930 match = checksum_re.match(line.lstrip())
931 checksum = match.group(1) if match else None
932 if checksum:
933 break
934 output_add(line)
935 if self.is_start_line(line):
936 break
937
Larry Hastingsef3b1fb2013-10-22 23:26:23 -0700938 output = output_output()
Larry Hastings31826802013-10-19 00:09:25 -0700939 if checksum:
Larry Hastings31826802013-10-19 00:09:25 -0700940 if self.verify:
941 computed = compute_checksum(output)
942 if checksum != computed:
943 fail("Checksum mismatch!\nExpected: {}\nComputed: {}".format(checksum, computed))
944 else:
945 # put back output
946 self.input.extend(reversed(output.splitlines(keepends=True)))
947 self.line_number -= len(output)
948 output = None
949
950 return Block(input_output(), dsl_name, output=output)
951
952
953class BlockPrinter:
954
955 def __init__(self, language, f=None):
956 self.language = language
957 self.f = f or io.StringIO()
958
959 def print_block(self, block):
960 input = block.input
961 output = block.output
962 dsl_name = block.dsl_name
963 write = self.f.write
964
Larry Hastings31826802013-10-19 00:09:25 -0700965 assert not ((dsl_name == None) ^ (output == None)), "you must specify dsl_name and output together, dsl_name " + repr(dsl_name)
966
967 if not dsl_name:
968 write(input)
969 return
970
971 write(self.language.start_line.format(dsl_name=dsl_name))
972 write("\n")
973
974 body_prefix = self.language.body_prefix.format(dsl_name=dsl_name)
975 if not body_prefix:
976 write(input)
977 else:
978 for line in input.split('\n'):
979 write(body_prefix)
980 write(line)
981 write("\n")
982
983 write(self.language.stop_line.format(dsl_name=dsl_name))
984 write("\n")
985
986 output = block.output
987 if output:
988 write(output)
989 if not output.endswith('\n'):
990 write('\n')
991
992 write(self.language.checksum_line.format(dsl_name=dsl_name, checksum=compute_checksum(output)))
993 write("\n")
994
995
996# maps strings to Language objects.
997# "languages" maps the name of the language ("C", "Python").
998# "extensions" maps the file extension ("c", "py").
999languages = { 'C': CLanguage, 'Python': PythonLanguage }
Larry Hastings6d2ea212014-01-05 02:50:45 -08001000extensions = { name: CLanguage for name in "c cc cpp cxx h hh hpp hxx".split() }
1001extensions['py'] = PythonLanguage
Larry Hastings31826802013-10-19 00:09:25 -07001002
1003
1004# maps strings to callables.
1005# these callables must be of the form:
1006# def foo(name, default, *, ...)
1007# The callable may have any number of keyword-only parameters.
1008# The callable must return a CConverter object.
1009# The callable should not call builtins.print.
1010converters = {}
1011
1012# maps strings to callables.
1013# these callables follow the same rules as those for "converters" above.
1014# note however that they will never be called with keyword-only parameters.
1015legacy_converters = {}
1016
1017
1018# maps strings to callables.
1019# these callables must be of the form:
1020# def foo(*, ...)
1021# The callable may have any number of keyword-only parameters.
1022# The callable must return a CConverter object.
1023# The callable should not call builtins.print.
1024return_converters = {}
1025
1026class Clinic:
1027 def __init__(self, language, printer=None, *, verify=True, filename=None):
1028 # maps strings to Parser objects.
1029 # (instantiated from the "parsers" global.)
1030 self.parsers = {}
1031 self.language = language
1032 self.printer = printer or BlockPrinter(language)
1033 self.verify = verify
1034 self.filename = filename
1035 self.modules = collections.OrderedDict()
Larry Hastingsed4a1c52013-11-18 09:32:13 -08001036 self.classes = collections.OrderedDict()
Larry Hastings31826802013-10-19 00:09:25 -07001037
1038 global clinic
1039 clinic = self
1040
1041 def parse(self, input):
1042 printer = self.printer
1043 self.block_parser = BlockParser(input, self.language, verify=self.verify)
1044 for block in self.block_parser:
1045 dsl_name = block.dsl_name
1046 if dsl_name:
1047 if dsl_name not in self.parsers:
1048 assert dsl_name in parsers, "No parser to handle {!r} block.".format(dsl_name)
1049 self.parsers[dsl_name] = parsers[dsl_name](self)
1050 parser = self.parsers[dsl_name]
1051 parser.parse(block)
1052 printer.print_block(block)
1053 return printer.f.getvalue()
1054
1055 def _module_and_class(self, fields):
1056 """
1057 fields should be an iterable of field names.
1058 returns a tuple of (module, class).
1059 the module object could actually be self (a clinic object).
1060 this function is only ever used to find the parent of where
1061 a new class/module should go.
1062 """
1063 in_classes = False
1064 parent = module = self
1065 cls = None
1066 so_far = []
1067
1068 for field in fields:
1069 so_far.append(field)
1070 if not in_classes:
1071 child = parent.modules.get(field)
1072 if child:
Larry Hastingsed4a1c52013-11-18 09:32:13 -08001073 parent = module = child
Larry Hastings31826802013-10-19 00:09:25 -07001074 continue
1075 in_classes = True
1076 if not hasattr(parent, 'classes'):
1077 return module, cls
1078 child = parent.classes.get(field)
1079 if not child:
1080 fail('Parent class or module ' + '.'.join(so_far) + " does not exist.")
1081 cls = parent = child
1082
1083 return module, cls
1084
1085
1086def parse_file(filename, *, verify=True, output=None, encoding='utf-8'):
1087 extension = os.path.splitext(filename)[1][1:]
1088 if not extension:
1089 fail("Can't extract file type for file " + repr(filename))
1090
1091 try:
1092 language = extensions[extension]()
1093 except KeyError:
1094 fail("Can't identify file type for file " + repr(filename))
1095
1096 clinic = Clinic(language, verify=verify, filename=filename)
1097
1098 with open(filename, 'r', encoding=encoding) as f:
Larry Hastingsdcd340e2013-11-23 14:58:45 -08001099 raw = f.read()
1100
1101 cooked = clinic.parse(raw)
1102 if cooked == raw:
1103 return
Larry Hastings31826802013-10-19 00:09:25 -07001104
1105 directory = os.path.dirname(filename) or '.'
1106
1107 with tempfile.TemporaryDirectory(prefix="clinic", dir=directory) as tmpdir:
Larry Hastingsdcd340e2013-11-23 14:58:45 -08001108 bytes = cooked.encode(encoding)
Larry Hastings31826802013-10-19 00:09:25 -07001109 tmpfilename = os.path.join(tmpdir, os.path.basename(filename))
1110 with open(tmpfilename, "wb") as f:
1111 f.write(bytes)
1112 os.replace(tmpfilename, output or filename)
1113
1114
1115def compute_checksum(input):
1116 input = input or ''
1117 return hashlib.sha1(input.encode('utf-8')).hexdigest()
1118
1119
1120
1121
1122class PythonParser:
1123 def __init__(self, clinic):
1124 pass
1125
1126 def parse(self, block):
1127 s = io.StringIO()
1128 with OverrideStdioWith(s):
1129 exec(block.input)
1130 block.output = s.getvalue()
1131
1132
1133class Module:
1134 def __init__(self, name, module=None):
1135 self.name = name
1136 self.module = self.parent = module
1137
1138 self.modules = collections.OrderedDict()
1139 self.classes = collections.OrderedDict()
1140 self.functions = []
1141
Larry Hastingsed4a1c52013-11-18 09:32:13 -08001142 def __repr__(self):
1143 return "<clinic.Module " + repr(self.name) + " at " + str(id(self)) + ">"
1144
Larry Hastings31826802013-10-19 00:09:25 -07001145class Class:
1146 def __init__(self, name, module=None, cls=None):
1147 self.name = name
1148 self.module = module
1149 self.cls = cls
1150 self.parent = cls or module
1151
1152 self.classes = collections.OrderedDict()
1153 self.functions = []
1154
Larry Hastingsed4a1c52013-11-18 09:32:13 -08001155 def __repr__(self):
1156 return "<clinic.Class " + repr(self.name) + " at " + str(id(self)) + ">"
1157
1158
Larry Hastings31826802013-10-19 00:09:25 -07001159DATA, CALLABLE, METHOD, STATIC_METHOD, CLASS_METHOD = range(5)
1160
1161class Function:
1162 """
1163 Mutable duck type for inspect.Function.
1164
1165 docstring - a str containing
1166 * embedded line breaks
1167 * text outdented to the left margin
1168 * no trailing whitespace.
1169 It will always be true that
1170 (not docstring) or ((not docstring[0].isspace()) and (docstring.rstrip() == docstring))
1171 """
1172
1173 def __init__(self, parameters=None, *, name,
1174 module, cls=None, c_basename=None,
1175 full_name=None,
1176 return_converter, return_annotation=_empty,
1177 docstring=None, kind=CALLABLE, coexist=False):
1178 self.parameters = parameters or collections.OrderedDict()
1179 self.return_annotation = return_annotation
1180 self.name = name
1181 self.full_name = full_name
1182 self.module = module
1183 self.cls = cls
1184 self.parent = cls or module
1185 self.c_basename = c_basename
1186 self.return_converter = return_converter
1187 self.docstring = docstring or ''
1188 self.kind = kind
1189 self.coexist = coexist
Larry Hastingsebdcb502013-11-23 14:54:00 -08001190 self.self_converter = None
1191
1192 @property
1193 def methoddef_flags(self):
1194 flags = []
1195 if self.kind == CLASS_METHOD:
1196 flags.append('METH_CLASS')
1197 elif self.kind == STATIC_METHOD:
1198 flags.append('METH_STATIC')
1199 else:
1200 assert self.kind == CALLABLE, "unknown kind: " + repr(self.kind)
1201 if self.coexist:
1202 flags.append('METH_COEXIST')
1203 return '|'.join(flags)
Larry Hastings31826802013-10-19 00:09:25 -07001204
1205 def __repr__(self):
1206 return '<clinic.Function ' + self.name + '>'
1207
1208
1209class Parameter:
1210 """
1211 Mutable duck type of inspect.Parameter.
1212 """
1213
1214 def __init__(self, name, kind, *, default=_empty,
1215 function, converter, annotation=_empty,
1216 docstring=None, group=0):
1217 self.name = name
1218 self.kind = kind
1219 self.default = default
1220 self.function = function
1221 self.converter = converter
1222 self.annotation = annotation
1223 self.docstring = docstring or ''
1224 self.group = group
1225
1226 def __repr__(self):
1227 return '<clinic.Parameter ' + self.name + '>'
1228
1229 def is_keyword_only(self):
1230 return self.kind == inspect.Parameter.KEYWORD_ONLY
1231
1232py_special_values = {
1233 NULL: "None",
1234}
1235
1236def py_repr(o):
1237 special = py_special_values.get(o)
1238 if special:
1239 return special
1240 return repr(o)
1241
1242
1243c_special_values = {
1244 NULL: "NULL",
1245 None: "Py_None",
1246}
1247
1248def c_repr(o):
1249 special = c_special_values.get(o)
1250 if special:
1251 return special
1252 if isinstance(o, str):
1253 return '"' + quoted_for_c_string(o) + '"'
1254 return repr(o)
1255
1256def add_c_converter(f, name=None):
1257 if not name:
1258 name = f.__name__
1259 if not name.endswith('_converter'):
1260 return f
1261 name = name[:-len('_converter')]
1262 converters[name] = f
1263 return f
1264
1265def add_default_legacy_c_converter(cls):
1266 # automatically add converter for default format unit
1267 # (but without stomping on the existing one if it's already
1268 # set, in case you subclass)
1269 if ((cls.format_unit != 'O&') and
1270 (cls.format_unit not in legacy_converters)):
1271 legacy_converters[cls.format_unit] = cls
1272 return cls
1273
1274def add_legacy_c_converter(format_unit, **kwargs):
1275 """
1276 Adds a legacy converter.
1277 """
1278 def closure(f):
1279 if not kwargs:
1280 added_f = f
1281 else:
1282 added_f = functools.partial(f, **kwargs)
1283 legacy_converters[format_unit] = added_f
1284 return f
1285 return closure
1286
1287class CConverterAutoRegister(type):
1288 def __init__(cls, name, bases, classdict):
1289 add_c_converter(cls)
1290 add_default_legacy_c_converter(cls)
1291
1292class CConverter(metaclass=CConverterAutoRegister):
1293 """
1294 For the init function, self, name, function, and default
1295 must be keyword-or-positional parameters. All other
1296 parameters (including "required" and "doc_default")
1297 must be keyword-only.
1298 """
1299
Larry Hastings78cf85c2014-01-04 12:44:57 -08001300 # The C type to use for this variable.
1301 # 'type' should be a Python string specifying the type, e.g. "int".
1302 # If this is a pointer type, the type string should end with ' *'.
Larry Hastings31826802013-10-19 00:09:25 -07001303 type = None
Larry Hastings31826802013-10-19 00:09:25 -07001304
1305 # The Python default value for this parameter, as a Python value.
Larry Hastings78cf85c2014-01-04 12:44:57 -08001306 # Or the magic value "unspecified" if there is no default.
Larry Hastings31826802013-10-19 00:09:25 -07001307 default = unspecified
1308
Larry Hastings31826802013-10-19 00:09:25 -07001309 # "default" as it should appear in the documentation, as a string.
1310 # Or None if there is no default.
1311 doc_default = None
1312
Larry Hastingsabc716b2013-11-20 09:13:52 -08001313 # "default" converted into a str for rendering into Python code.
1314 py_default = None
1315
Larry Hastings31826802013-10-19 00:09:25 -07001316 # "default" converted into a C value, as a string.
1317 # Or None if there is no default.
1318 c_default = None
1319
Larry Hastingsabc716b2013-11-20 09:13:52 -08001320 # The default value used to initialize the C variable when
1321 # there is no default, but not specifying a default may
1322 # result in an "uninitialized variable" warning. This can
1323 # easily happen when using option groups--although
1324 # properly-written code won't actually use the variable,
1325 # the variable does get passed in to the _impl. (Ah, if
1326 # only dataflow analysis could inline the static function!)
1327 #
1328 # This value is specified as a string.
1329 # Every non-abstract subclass should supply a valid value.
1330 c_ignored_default = 'NULL'
1331
Larry Hastings31826802013-10-19 00:09:25 -07001332 # The C converter *function* to be used, if any.
1333 # (If this is not None, format_unit must be 'O&'.)
1334 converter = None
Larry Hastingsebdcb502013-11-23 14:54:00 -08001335
Larry Hastings78cf85c2014-01-04 12:44:57 -08001336 # Should Argument Clinic add a '&' before the name of
1337 # the variable when passing it into the _impl function?
Larry Hastings31826802013-10-19 00:09:25 -07001338 impl_by_reference = False
Larry Hastings78cf85c2014-01-04 12:44:57 -08001339
1340 # Should Argument Clinic add a '&' before the name of
1341 # the variable when passing it into PyArg_ParseTuple (AndKeywords)?
Larry Hastings31826802013-10-19 00:09:25 -07001342 parse_by_reference = True
Larry Hastings78cf85c2014-01-04 12:44:57 -08001343
1344 #############################################################
1345 #############################################################
1346 ## You shouldn't need to read anything below this point to ##
1347 ## write your own converter functions. ##
1348 #############################################################
1349 #############################################################
1350
1351 # The "format unit" to specify for this variable when
1352 # parsing arguments using PyArg_ParseTuple (AndKeywords).
1353 # Custom converters should always use the default value of 'O&'.
1354 format_unit = 'O&'
1355
1356 # What encoding do we want for this variable? Only used
1357 # by format units starting with 'e'.
1358 encoding = None
1359
1360 # Do we want an adjacent '_length' variable for this variable?
1361 # Only used by format units ending with '#'.
Larry Hastings31826802013-10-19 00:09:25 -07001362 length = False
1363
1364 def __init__(self, name, function, default=unspecified, *, doc_default=None, required=False, annotation=unspecified, **kwargs):
1365 self.function = function
1366 self.name = name
1367
1368 if default is not unspecified:
1369 self.default = default
1370 self.py_default = py_repr(default)
1371 self.doc_default = doc_default if doc_default is not None else self.py_default
1372 self.c_default = c_repr(default)
1373 elif doc_default is not None:
1374 fail(function.fullname + " argument " + name + " specified a 'doc_default' without having a 'default'")
1375 if annotation != unspecified:
1376 fail("The 'annotation' parameter is not currently permitted.")
1377 self.required = required
1378 self.converter_init(**kwargs)
1379
1380 def converter_init(self):
1381 pass
1382
1383 def is_optional(self):
1384 return (self.default is not unspecified) and (not self.required)
1385
1386 def render(self, parameter, data):
1387 """
1388 parameter is a clinic.Parameter instance.
1389 data is a CRenderData instance.
1390 """
Larry Hastingsabc716b2013-11-20 09:13:52 -08001391 self.parameter = parameter
Larry Hastingsdfcd4672013-10-27 02:49:39 -07001392 name = ensure_legal_c_identifier(self.name)
Larry Hastings31826802013-10-19 00:09:25 -07001393
1394 # declarations
1395 d = self.declaration()
1396 data.declarations.append(d)
1397
1398 # initializers
1399 initializers = self.initialize()
1400 if initializers:
1401 data.initializers.append('/* initializers for ' + name + ' */\n' + initializers.rstrip())
1402
1403 # impl_arguments
1404 s = ("&" if self.impl_by_reference else "") + name
1405 data.impl_arguments.append(s)
Larry Hastingsebdcb502013-11-23 14:54:00 -08001406 if self.length:
1407 data.impl_arguments.append(self.length_name())
Larry Hastings31826802013-10-19 00:09:25 -07001408
1409 # keywords
1410 data.keywords.append(name)
1411
1412 # format_units
1413 if self.is_optional() and '|' not in data.format_units:
1414 data.format_units.append('|')
1415 if parameter.is_keyword_only() and '$' not in data.format_units:
1416 data.format_units.append('$')
1417 data.format_units.append(self.format_unit)
1418
1419 # parse_arguments
1420 self.parse_argument(data.parse_arguments)
1421
1422 # impl_parameters
1423 data.impl_parameters.append(self.simple_declaration(by_reference=self.impl_by_reference))
Larry Hastingsebdcb502013-11-23 14:54:00 -08001424 if self.length:
1425 data.impl_parameters.append("Py_ssize_clean_t " + self.length_name())
Larry Hastings31826802013-10-19 00:09:25 -07001426
1427 # cleanup
1428 cleanup = self.cleanup()
1429 if cleanup:
1430 data.cleanup.append('/* Cleanup for ' + name + ' */\n' + cleanup.rstrip() + "\n")
1431
Larry Hastingsebdcb502013-11-23 14:54:00 -08001432 def length_name(self):
1433 """Computes the name of the associated "length" variable."""
1434 if not self.length:
1435 return None
1436 return ensure_legal_c_identifier(self.name) + "_length"
1437
Larry Hastings31826802013-10-19 00:09:25 -07001438 # Why is this one broken out separately?
1439 # For "positional-only" function parsing,
1440 # which generates a bunch of PyArg_ParseTuple calls.
1441 def parse_argument(self, list):
1442 assert not (self.converter and self.encoding)
1443 if self.format_unit == 'O&':
1444 assert self.converter
1445 list.append(self.converter)
1446
1447 if self.encoding:
1448 list.append(self.encoding)
1449
Larry Hastingsebdcb502013-11-23 14:54:00 -08001450 legal_name = ensure_legal_c_identifier(self.name)
1451 s = ("&" if self.parse_by_reference else "") + legal_name
Larry Hastings31826802013-10-19 00:09:25 -07001452 list.append(s)
1453
Larry Hastingsebdcb502013-11-23 14:54:00 -08001454 if self.length:
1455 list.append("&" + self.length_name())
1456
Larry Hastings31826802013-10-19 00:09:25 -07001457 #
1458 # All the functions after here are intended as extension points.
1459 #
1460
1461 def simple_declaration(self, by_reference=False):
1462 """
1463 Computes the basic declaration of the variable.
1464 Used in computing the prototype declaration and the
1465 variable declaration.
1466 """
1467 prototype = [self.type]
1468 if by_reference or not self.type.endswith('*'):
1469 prototype.append(" ")
1470 if by_reference:
1471 prototype.append('*')
Larry Hastingsdfcd4672013-10-27 02:49:39 -07001472 prototype.append(ensure_legal_c_identifier(self.name))
Larry Hastings31826802013-10-19 00:09:25 -07001473 return "".join(prototype)
1474
1475 def declaration(self):
1476 """
1477 The C statement to declare this variable.
1478 """
1479 declaration = [self.simple_declaration()]
Larry Hastingsabc716b2013-11-20 09:13:52 -08001480 default = self.c_default
1481 if not default and self.parameter.group:
1482 default = self.c_ignored_default
1483 if default:
Larry Hastings31826802013-10-19 00:09:25 -07001484 declaration.append(" = ")
Larry Hastingsabc716b2013-11-20 09:13:52 -08001485 declaration.append(default)
Larry Hastings31826802013-10-19 00:09:25 -07001486 declaration.append(";")
Larry Hastingsebdcb502013-11-23 14:54:00 -08001487 if self.length:
1488 declaration.append('\nPy_ssize_clean_t ')
1489 declaration.append(self.length_name())
1490 declaration.append(';')
Larry Hastings3f144c22014-01-06 10:34:00 -08001491 s = "".join(declaration)
1492 # double up curly-braces, this string will be used
1493 # as part of a format_map() template later
1494 s = s.replace("{", "{{")
1495 s = s.replace("}", "}}")
1496 return s
Larry Hastings31826802013-10-19 00:09:25 -07001497
1498 def initialize(self):
1499 """
1500 The C statements required to set up this variable before parsing.
1501 Returns a string containing this code indented at column 0.
1502 If no initialization is necessary, returns an empty string.
1503 """
1504 return ""
1505
1506 def cleanup(self):
1507 """
1508 The C statements required to clean up after this variable.
1509 Returns a string containing this code indented at column 0.
1510 If no cleanup is necessary, returns an empty string.
1511 """
1512 return ""
1513
1514
1515class bool_converter(CConverter):
1516 type = 'int'
1517 format_unit = 'p'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001518 c_ignored_default = '0'
Larry Hastings31826802013-10-19 00:09:25 -07001519
1520 def converter_init(self):
1521 self.default = bool(self.default)
1522 self.c_default = str(int(self.default))
1523
1524class char_converter(CConverter):
1525 type = 'char'
1526 format_unit = 'c'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001527 c_ignored_default = "'\0'"
Larry Hastings31826802013-10-19 00:09:25 -07001528
1529@add_legacy_c_converter('B', bitwise=True)
1530class byte_converter(CConverter):
1531 type = 'byte'
1532 format_unit = 'b'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001533 c_ignored_default = "'\0'"
Larry Hastings31826802013-10-19 00:09:25 -07001534
1535 def converter_init(self, *, bitwise=False):
1536 if bitwise:
Larry Hastingsebdcb502013-11-23 14:54:00 -08001537 self.format_unit = 'B'
Larry Hastings31826802013-10-19 00:09:25 -07001538
1539class short_converter(CConverter):
1540 type = 'short'
1541 format_unit = 'h'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001542 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001543
1544class unsigned_short_converter(CConverter):
1545 type = 'unsigned short'
1546 format_unit = 'H'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001547 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001548
1549 def converter_init(self, *, bitwise=False):
1550 if not bitwise:
1551 fail("Unsigned shorts must be bitwise (for now).")
1552
Larry Hastingsebdcb502013-11-23 14:54:00 -08001553@add_legacy_c_converter('C', types='str')
Larry Hastings31826802013-10-19 00:09:25 -07001554class int_converter(CConverter):
1555 type = 'int'
1556 format_unit = 'i'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001557 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001558
Larry Hastingsebdcb502013-11-23 14:54:00 -08001559 def converter_init(self, *, types='int'):
1560 if types == 'str':
1561 self.format_unit = 'C'
1562 elif types != 'int':
1563 fail("int_converter: illegal 'types' argument")
Larry Hastings31826802013-10-19 00:09:25 -07001564
1565class unsigned_int_converter(CConverter):
1566 type = 'unsigned int'
1567 format_unit = 'I'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001568 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001569
1570 def converter_init(self, *, bitwise=False):
1571 if not bitwise:
1572 fail("Unsigned ints must be bitwise (for now).")
1573
1574class long_converter(CConverter):
1575 type = 'long'
1576 format_unit = 'l'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001577 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001578
1579class unsigned_long_converter(CConverter):
1580 type = 'unsigned long'
1581 format_unit = 'k'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001582 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001583
1584 def converter_init(self, *, bitwise=False):
1585 if not bitwise:
1586 fail("Unsigned longs must be bitwise (for now).")
1587
1588class PY_LONG_LONG_converter(CConverter):
1589 type = 'PY_LONG_LONG'
1590 format_unit = 'L'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001591 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001592
1593class unsigned_PY_LONG_LONG_converter(CConverter):
1594 type = 'unsigned PY_LONG_LONG'
1595 format_unit = 'K'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001596 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001597
1598 def converter_init(self, *, bitwise=False):
1599 if not bitwise:
1600 fail("Unsigned PY_LONG_LONGs must be bitwise (for now).")
1601
1602class Py_ssize_t_converter(CConverter):
1603 type = 'Py_ssize_t'
1604 format_unit = 'n'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001605 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001606
1607
1608class float_converter(CConverter):
1609 type = 'float'
1610 format_unit = 'f'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001611 c_ignored_default = "0.0"
Larry Hastings31826802013-10-19 00:09:25 -07001612
1613class double_converter(CConverter):
1614 type = 'double'
1615 format_unit = 'd'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001616 c_ignored_default = "0.0"
Larry Hastings31826802013-10-19 00:09:25 -07001617
1618
1619class Py_complex_converter(CConverter):
1620 type = 'Py_complex'
1621 format_unit = 'D'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001622 c_ignored_default = "{0.0, 0.0}"
Larry Hastings31826802013-10-19 00:09:25 -07001623
1624
1625class object_converter(CConverter):
1626 type = 'PyObject *'
1627 format_unit = 'O'
1628
1629 def converter_init(self, *, type=None):
1630 if type:
1631 assert isinstance(type, str)
1632 assert type.isidentifier()
1633 try:
1634 type = eval(type)
1635 # need more of these!
1636 type = {
1637 str: '&PyUnicode_Type',
1638 }[type]
1639 except NameError:
1640 type = type
1641 self.format_unit = 'O!'
1642 self.encoding = type
1643
1644
Larry Hastingsebdcb502013-11-23 14:54:00 -08001645@add_legacy_c_converter('s#', length=True)
1646@add_legacy_c_converter('y', type="bytes")
1647@add_legacy_c_converter('y#', type="bytes", length=True)
Larry Hastings31826802013-10-19 00:09:25 -07001648@add_legacy_c_converter('z', nullable=True)
Larry Hastingsebdcb502013-11-23 14:54:00 -08001649@add_legacy_c_converter('z#', nullable=True, length=True)
Larry Hastings31826802013-10-19 00:09:25 -07001650class str_converter(CConverter):
1651 type = 'const char *'
1652 format_unit = 's'
1653
Larry Hastingsebdcb502013-11-23 14:54:00 -08001654 def converter_init(self, *, encoding=None, types="str",
1655 length=False, nullable=False, zeroes=False):
1656
1657 types = set(types.strip().split())
1658 bytes_type = set(("bytes",))
1659 str_type = set(("str",))
1660 all_3_type = set(("bytearray",)) | bytes_type | str_type
1661 is_bytes = types == bytes_type
1662 is_str = types == str_type
1663 is_all_3 = types == all_3_type
1664
1665 self.length = bool(length)
1666 format_unit = None
1667
1668 if encoding:
1669 self.encoding = encoding
1670
1671 if is_str and not (length or zeroes or nullable):
1672 format_unit = 'es'
1673 elif is_all_3 and not (length or zeroes or nullable):
1674 format_unit = 'et'
1675 elif is_str and length and zeroes and not nullable:
1676 format_unit = 'es#'
1677 elif is_all_3 and length and not (nullable or zeroes):
1678 format_unit = 'et#'
1679
1680 if format_unit.endswith('#'):
Larry Hastings2f9a9aa2013-11-24 04:23:35 -08001681 print("Warning: code using format unit ", repr(format_unit), "probably doesn't work properly.")
Larry Hastingsebdcb502013-11-23 14:54:00 -08001682 # TODO set pointer to NULL
1683 # TODO add cleanup for buffer
1684 pass
1685
1686 else:
1687 if zeroes:
1688 fail("str_converter: illegal combination of arguments (zeroes is only legal with an encoding)")
1689
1690 if is_bytes and not (nullable or length):
1691 format_unit = 'y'
1692 elif is_bytes and length and not nullable:
1693 format_unit = 'y#'
1694 elif is_str and not (nullable or length):
1695 format_unit = 's'
1696 elif is_str and length and not nullable:
1697 format_unit = 's#'
1698 elif is_str and nullable and not length:
1699 format_unit = 'z'
1700 elif is_str and nullable and length:
1701 format_unit = 'z#'
1702
1703 if not format_unit:
1704 fail("str_converter: illegal combination of arguments")
1705 self.format_unit = format_unit
Larry Hastings31826802013-10-19 00:09:25 -07001706
1707
1708class PyBytesObject_converter(CConverter):
1709 type = 'PyBytesObject *'
1710 format_unit = 'S'
1711
1712class PyByteArrayObject_converter(CConverter):
1713 type = 'PyByteArrayObject *'
1714 format_unit = 'Y'
1715
1716class unicode_converter(CConverter):
1717 type = 'PyObject *'
1718 format_unit = 'U'
1719
Larry Hastingsebdcb502013-11-23 14:54:00 -08001720@add_legacy_c_converter('u#', length=True)
Larry Hastings31826802013-10-19 00:09:25 -07001721@add_legacy_c_converter('Z', nullable=True)
Larry Hastingsebdcb502013-11-23 14:54:00 -08001722@add_legacy_c_converter('Z#', nullable=True, length=True)
Larry Hastings31826802013-10-19 00:09:25 -07001723class Py_UNICODE_converter(CConverter):
1724 type = 'Py_UNICODE *'
1725 format_unit = 'u'
1726
Larry Hastingsebdcb502013-11-23 14:54:00 -08001727 def converter_init(self, *, nullable=False, length=False):
1728 format_unit = 'Z' if nullable else 'u'
1729 if length:
1730 format_unit += '#'
1731 self.length = True
1732 self.format_unit = format_unit
Larry Hastings31826802013-10-19 00:09:25 -07001733
Larry Hastingsebdcb502013-11-23 14:54:00 -08001734#
1735# We define three string conventions for buffer types in the 'types' argument:
1736# 'buffer' : any object supporting the buffer interface
1737# 'rwbuffer': any object supporting the buffer interface, but must be writeable
1738# 'robuffer': any object supporting the buffer interface, but must not be writeable
1739#
1740@add_legacy_c_converter('s*', types='str bytes bytearray buffer')
1741@add_legacy_c_converter('z*', types='str bytes bytearray buffer', nullable=True)
1742@add_legacy_c_converter('w*', types='bytearray rwbuffer')
Larry Hastings31826802013-10-19 00:09:25 -07001743class Py_buffer_converter(CConverter):
1744 type = 'Py_buffer'
1745 format_unit = 'y*'
1746 impl_by_reference = True
Larry Hastingsabc716b2013-11-20 09:13:52 -08001747 c_ignored_default = "{NULL, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL}"
Larry Hastings31826802013-10-19 00:09:25 -07001748
Larry Hastingsebdcb502013-11-23 14:54:00 -08001749 def converter_init(self, *, types='bytes bytearray buffer', nullable=False):
Larry Hastings3f144c22014-01-06 10:34:00 -08001750 if self.default != unspecified:
1751 fail("There is no legal default value for Py_buffer ")
1752 self.c_default = self.c_ignored_default
Larry Hastingsebdcb502013-11-23 14:54:00 -08001753 types = set(types.strip().split())
1754 bytes_type = set(('bytes',))
1755 bytearray_type = set(('bytearray',))
1756 buffer_type = set(('buffer',))
1757 rwbuffer_type = set(('rwbuffer',))
1758 robuffer_type = set(('robuffer',))
1759 str_type = set(('str',))
1760 bytes_bytearray_buffer_type = bytes_type | bytearray_type | buffer_type
1761
1762 format_unit = None
1763 if types == (str_type | bytes_bytearray_buffer_type):
1764 format_unit = 's*' if not nullable else 'z*'
Larry Hastings31826802013-10-19 00:09:25 -07001765 else:
Larry Hastingsebdcb502013-11-23 14:54:00 -08001766 if nullable:
1767 fail('Py_buffer_converter: illegal combination of arguments (nullable=True)')
1768 elif types == (bytes_bytearray_buffer_type):
1769 format_unit = 'y*'
1770 elif types == (bytearray_type | rwuffer_type):
1771 format_unit = 'w*'
1772 if not format_unit:
1773 fail("Py_buffer_converter: illegal combination of arguments")
1774
1775 self.format_unit = format_unit
Larry Hastings31826802013-10-19 00:09:25 -07001776
1777 def cleanup(self):
Larry Hastingsebdcb502013-11-23 14:54:00 -08001778 name = ensure_legal_c_identifier(self.name)
1779 return "".join(["if (", name, ".buf)\n PyBuffer_Release(&", name, ");\n"])
1780
1781
1782class self_converter(CConverter):
1783 """
1784 A special-case converter:
1785 this is the default converter used for "self".
1786 """
1787 type = "PyObject *"
Larry Hastings78cf85c2014-01-04 12:44:57 -08001788 def converter_init(self, *, type=None):
Larry Hastingsebdcb502013-11-23 14:54:00 -08001789 f = self.function
1790 if f.kind == CALLABLE:
1791 if f.cls:
1792 self.name = "self"
1793 else:
1794 self.name = "module"
1795 self.type = "PyModuleDef *"
1796 elif f.kind == STATIC_METHOD:
1797 self.name = "null"
1798 self.type = "void *"
1799 elif f.kind == CLASS_METHOD:
1800 self.name = "cls"
1801 self.type = "PyTypeObject *"
1802
Larry Hastings78cf85c2014-01-04 12:44:57 -08001803 if type:
1804 self.type = type
1805
Larry Hastingsebdcb502013-11-23 14:54:00 -08001806 def render(self, parameter, data):
1807 fail("render() should never be called on self_converter instances")
1808
Larry Hastings31826802013-10-19 00:09:25 -07001809
1810
1811def add_c_return_converter(f, name=None):
1812 if not name:
1813 name = f.__name__
1814 if not name.endswith('_return_converter'):
1815 return f
1816 name = name[:-len('_return_converter')]
1817 return_converters[name] = f
1818 return f
1819
1820
1821class CReturnConverterAutoRegister(type):
1822 def __init__(cls, name, bases, classdict):
1823 add_c_return_converter(cls)
1824
1825class CReturnConverter(metaclass=CReturnConverterAutoRegister):
1826
Larry Hastings78cf85c2014-01-04 12:44:57 -08001827 # The C type to use for this variable.
1828 # 'type' should be a Python string specifying the type, e.g. "int".
1829 # If this is a pointer type, the type string should end with ' *'.
Larry Hastings31826802013-10-19 00:09:25 -07001830 type = 'PyObject *'
Larry Hastings78cf85c2014-01-04 12:44:57 -08001831
1832 # The Python default value for this parameter, as a Python value.
1833 # Or the magic value "unspecified" if there is no default.
Larry Hastings31826802013-10-19 00:09:25 -07001834 default = None
1835
1836 def __init__(self, *, doc_default=None, **kwargs):
1837 self.doc_default = doc_default
1838 try:
1839 self.return_converter_init(**kwargs)
1840 except TypeError as e:
1841 s = ', '.join(name + '=' + repr(value) for name, value in kwargs.items())
1842 sys.exit(self.__class__.__name__ + '(' + s + ')\n' + str(e))
1843
1844 def return_converter_init(self):
1845 pass
1846
1847 def declare(self, data, name="_return_value"):
1848 line = []
1849 add = line.append
1850 add(self.type)
1851 if not self.type.endswith('*'):
1852 add(' ')
1853 add(name + ';')
1854 data.declarations.append(''.join(line))
1855 data.return_value = name
1856
1857 def err_occurred_if(self, expr, data):
1858 data.return_conversion.append('if (({}) && PyErr_Occurred())\n goto exit;\n'.format(expr))
1859
1860 def err_occurred_if_null_pointer(self, variable, data):
1861 data.return_conversion.append('if ({} == NULL)\n goto exit;\n'.format(variable))
1862
1863 def render(self, function, data):
1864 """
1865 function is a clinic.Function instance.
1866 data is a CRenderData instance.
1867 """
1868 pass
1869
1870add_c_return_converter(CReturnConverter, 'object')
1871
Larry Hastings78cf85c2014-01-04 12:44:57 -08001872class NoneType_return_converter(CReturnConverter):
1873 def render(self, function, data):
1874 self.declare(data)
1875 data.return_conversion.append('''
1876if (_return_value != Py_None)
1877 goto exit;
1878return_value = Py_None;
1879Py_INCREF(Py_None);
1880'''.strip())
1881
Larry Hastings31826802013-10-19 00:09:25 -07001882class int_return_converter(CReturnConverter):
1883 type = 'int'
1884
1885 def render(self, function, data):
1886 self.declare(data)
1887 self.err_occurred_if("_return_value == -1", data)
1888 data.return_conversion.append(
1889 'return_value = PyLong_FromLong((long)_return_value);\n')
1890
1891
1892class long_return_converter(CReturnConverter):
1893 type = 'long'
1894
1895 def render(self, function, data):
1896 self.declare(data)
1897 self.err_occurred_if("_return_value == -1", data)
1898 data.return_conversion.append(
1899 'return_value = PyLong_FromLong(_return_value);\n')
1900
1901
1902class Py_ssize_t_return_converter(CReturnConverter):
1903 type = 'Py_ssize_t'
1904
1905 def render(self, function, data):
1906 self.declare(data)
1907 self.err_occurred_if("_return_value == -1", data)
1908 data.return_conversion.append(
1909 'return_value = PyLong_FromSsize_t(_return_value);\n')
1910
1911
1912class DecodeFSDefault_return_converter(CReturnConverter):
1913 type = 'char *'
1914
1915 def render(self, function, data):
1916 self.declare(data)
1917 self.err_occurred_if_null_pointer("_return_value", data)
1918 data.return_conversion.append(
1919 'return_value = PyUnicode_DecodeFSDefault(_return_value);\n')
1920
1921
1922class IndentStack:
1923 def __init__(self):
1924 self.indents = []
1925 self.margin = None
1926
1927 def _ensure(self):
1928 if not self.indents:
1929 fail('IndentStack expected indents, but none are defined.')
1930
1931 def measure(self, line):
1932 """
1933 Returns the length of the line's margin.
1934 """
1935 if '\t' in line:
1936 fail('Tab characters are illegal in the Clinic DSL.')
1937 stripped = line.lstrip()
1938 if not len(stripped):
1939 # we can't tell anything from an empty line
1940 # so just pretend it's indented like our current indent
1941 self._ensure()
1942 return self.indents[-1]
1943 return len(line) - len(stripped)
1944
1945 def infer(self, line):
1946 """
1947 Infer what is now the current margin based on this line.
1948 Returns:
1949 1 if we have indented (or this is the first margin)
1950 0 if the margin has not changed
1951 -N if we have dedented N times
1952 """
1953 indent = self.measure(line)
1954 margin = ' ' * indent
1955 if not self.indents:
1956 self.indents.append(indent)
1957 self.margin = margin
1958 return 1
1959 current = self.indents[-1]
1960 if indent == current:
1961 return 0
1962 if indent > current:
1963 self.indents.append(indent)
1964 self.margin = margin
1965 return 1
1966 # indent < current
1967 if indent not in self.indents:
1968 fail("Illegal outdent.")
1969 outdent_count = 0
1970 while indent != current:
1971 self.indents.pop()
1972 current = self.indents[-1]
1973 outdent_count -= 1
1974 self.margin = margin
1975 return outdent_count
1976
1977 @property
1978 def depth(self):
1979 """
1980 Returns how many margins are currently defined.
1981 """
1982 return len(self.indents)
1983
1984 def indent(self, line):
1985 """
1986 Indents a line by the currently defined margin.
1987 """
1988 return self.margin + line
1989
1990 def dedent(self, line):
1991 """
1992 Dedents a line by the currently defined margin.
1993 (The inverse of 'indent'.)
1994 """
1995 margin = self.margin
1996 indent = self.indents[-1]
1997 if not line.startswith(margin):
1998 fail('Cannot dedent, line does not start with the previous margin:')
1999 return line[indent:]
2000
2001
2002class DSLParser:
2003 def __init__(self, clinic):
2004 self.clinic = clinic
2005
2006 self.directives = {}
2007 for name in dir(self):
2008 # functions that start with directive_ are added to directives
2009 _, s, key = name.partition("directive_")
2010 if s:
2011 self.directives[key] = getattr(self, name)
2012
2013 # functions that start with at_ are too, with an @ in front
2014 _, s, key = name.partition("at_")
2015 if s:
2016 self.directives['@' + key] = getattr(self, name)
2017
2018 self.reset()
2019
2020 def reset(self):
2021 self.function = None
2022 self.state = self.state_dsl_start
2023 self.parameter_indent = None
2024 self.keyword_only = False
2025 self.group = 0
2026 self.parameter_state = self.ps_start
2027 self.indent = IndentStack()
2028 self.kind = CALLABLE
2029 self.coexist = False
2030
Larry Hastingsebdcb502013-11-23 14:54:00 -08002031 def directive_version(self, required):
2032 global version
2033 if version_comparitor(version, required) < 0:
2034 fail("Insufficient Clinic version!\n Version: " + version + "\n Required: " + required)
2035
Larry Hastings31826802013-10-19 00:09:25 -07002036 def directive_module(self, name):
2037 fields = name.split('.')
2038 new = fields.pop()
2039 module, cls = self.clinic._module_and_class(fields)
2040 if cls:
2041 fail("Can't nest a module inside a class!")
2042 m = Module(name, module)
2043 module.modules[name] = m
2044 self.block.signatures.append(m)
2045
2046 def directive_class(self, name):
2047 fields = name.split('.')
2048 in_classes = False
2049 parent = self
2050 name = fields.pop()
2051 so_far = []
2052 module, cls = self.clinic._module_and_class(fields)
2053
Larry Hastings31826802013-10-19 00:09:25 -07002054 c = Class(name, module, cls)
Larry Hastings31826802013-10-19 00:09:25 -07002055 if cls:
2056 cls.classes[name] = c
Larry Hastingsed4a1c52013-11-18 09:32:13 -08002057 else:
2058 module.classes[name] = c
Larry Hastings31826802013-10-19 00:09:25 -07002059 self.block.signatures.append(c)
2060
2061 def at_classmethod(self):
2062 assert self.kind is CALLABLE
2063 self.kind = CLASS_METHOD
2064
2065 def at_staticmethod(self):
2066 assert self.kind is CALLABLE
2067 self.kind = STATIC_METHOD
2068
2069 def at_coexist(self):
2070 assert self.coexist == False
2071 self.coexist = True
2072
Larry Hastingsebdcb502013-11-23 14:54:00 -08002073
Larry Hastings31826802013-10-19 00:09:25 -07002074 def parse(self, block):
2075 self.reset()
2076 self.block = block
2077 block_start = self.clinic.block_parser.line_number
2078 lines = block.input.split('\n')
2079 for line_number, line in enumerate(lines, self.clinic.block_parser.block_start_line_number):
2080 if '\t' in line:
2081 fail('Tab characters are illegal in the Clinic DSL.\n\t' + repr(line), line_number=block_start)
2082 self.state(line)
2083
2084 self.next(self.state_terminal)
2085 self.state(None)
2086
2087 block.output = self.clinic.language.render(block.signatures)
2088
2089 @staticmethod
2090 def ignore_line(line):
2091 # ignore comment-only lines
2092 if line.lstrip().startswith('#'):
2093 return True
2094
2095 # Ignore empty lines too
2096 # (but not in docstring sections!)
2097 if not line.strip():
2098 return True
2099
2100 return False
2101
2102 @staticmethod
2103 def calculate_indent(line):
2104 return len(line) - len(line.strip())
2105
2106 def next(self, state, line=None):
2107 # real_print(self.state.__name__, "->", state.__name__, ", line=", line)
2108 self.state = state
2109 if line is not None:
2110 self.state(line)
2111
2112 def state_dsl_start(self, line):
2113 # self.block = self.ClinicOutputBlock(self)
2114 if self.ignore_line(line):
2115 return
2116 self.next(self.state_modulename_name, line)
2117
2118 def state_modulename_name(self, line):
2119 # looking for declaration, which establishes the leftmost column
2120 # line should be
2121 # modulename.fnname [as c_basename] [-> return annotation]
2122 # square brackets denote optional syntax.
2123 #
2124 # (but we might find a directive first!)
2125 #
2126 # this line is permitted to start with whitespace.
2127 # we'll call this number of spaces F (for "function").
2128
2129 if not line.strip():
2130 return
2131
2132 self.indent.infer(line)
2133
2134 # is it a directive?
2135 fields = shlex.split(line)
2136 directive_name = fields[0]
2137 directive = self.directives.get(directive_name, None)
2138 if directive:
2139 directive(*fields[1:])
2140 return
2141
2142 line, _, returns = line.partition('->')
2143
2144 full_name, _, c_basename = line.partition(' as ')
2145 full_name = full_name.strip()
2146 c_basename = c_basename.strip() or None
2147
Larry Hastingsdfcd4672013-10-27 02:49:39 -07002148 if not is_legal_py_identifier(full_name):
2149 fail("Illegal function name: {}".format(full_name))
2150 if c_basename and not is_legal_c_identifier(c_basename):
2151 fail("Illegal C basename: {}".format(c_basename))
2152
Larry Hastings31826802013-10-19 00:09:25 -07002153 if not returns:
2154 return_converter = CReturnConverter()
2155 else:
2156 ast_input = "def x() -> {}: pass".format(returns)
2157 module = None
2158 try:
2159 module = ast.parse(ast_input)
2160 except SyntaxError:
2161 pass
2162 if not module:
2163 fail("Badly-formed annotation for " + full_name + ": " + returns)
2164 try:
2165 name, legacy, kwargs = self.parse_converter(module.body[0].returns)
2166 assert not legacy
2167 if name not in return_converters:
2168 fail("Error: No available return converter called " + repr(name))
2169 return_converter = return_converters[name](**kwargs)
2170 except ValueError:
2171 fail("Badly-formed annotation for " + full_name + ": " + returns)
2172
2173 fields = [x.strip() for x in full_name.split('.')]
2174 function_name = fields.pop()
2175 module, cls = self.clinic._module_and_class(fields)
2176
2177 if not module:
2178 fail("Undefined module used in declaration of " + repr(full_name.strip()) + ".")
2179 self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,
2180 return_converter=return_converter, kind=self.kind, coexist=self.coexist)
2181 self.block.signatures.append(self.function)
2182 self.next(self.state_parameters_start)
2183
2184 # Now entering the parameters section. The rules, formally stated:
2185 #
2186 # * All lines must be indented with spaces only.
2187 # * The first line must be a parameter declaration.
2188 # * The first line must be indented.
2189 # * This first line establishes the indent for parameters.
2190 # * We'll call this number of spaces P (for "parameter").
2191 # * Thenceforth:
2192 # * Lines indented with P spaces specify a parameter.
2193 # * Lines indented with > P spaces are docstrings for the previous
2194 # parameter.
2195 # * We'll call this number of spaces D (for "docstring").
2196 # * All subsequent lines indented with >= D spaces are stored as
2197 # part of the per-parameter docstring.
2198 # * All lines will have the first D spaces of the indent stripped
2199 # before they are stored.
2200 # * It's illegal to have a line starting with a number of spaces X
2201 # such that P < X < D.
2202 # * A line with < P spaces is the first line of the function
2203 # docstring, which ends processing for parameters and per-parameter
2204 # docstrings.
2205 # * The first line of the function docstring must be at the same
2206 # indent as the function declaration.
2207 # * It's illegal to have any line in the parameters section starting
2208 # with X spaces such that F < X < P. (As before, F is the indent
2209 # of the function declaration.)
2210 #
2211 ##############
2212 #
2213 # Also, currently Argument Clinic places the following restrictions on groups:
2214 # * Each group must contain at least one parameter.
2215 # * Each group may contain at most one group, which must be the furthest
2216 # thing in the group from the required parameters. (The nested group
2217 # must be the first in the group when it's before the required
2218 # parameters, and the last thing in the group when after the required
2219 # parameters.)
2220 # * There may be at most one (top-level) group to the left or right of
2221 # the required parameters.
2222 # * You must specify a slash, and it must be after all parameters.
2223 # (In other words: either all parameters are positional-only,
2224 # or none are.)
2225 #
2226 # Said another way:
2227 # * Each group must contain at least one parameter.
2228 # * All left square brackets before the required parameters must be
2229 # consecutive. (You can't have a left square bracket followed
2230 # by a parameter, then another left square bracket. You can't
2231 # have a left square bracket, a parameter, a right square bracket,
2232 # and then a left square bracket.)
2233 # * All right square brackets after the required parameters must be
2234 # consecutive.
2235 #
2236 # These rules are enforced with a single state variable:
2237 # "parameter_state". (Previously the code was a miasma of ifs and
2238 # separate boolean state variables.) The states are:
2239 #
2240 # [ [ a, b, ] c, ] d, e, f, [ g, h, [ i ] ] / <- line
2241 # 01 2 3 4 5 6 <- state transitions
2242 #
2243 # 0: ps_start. before we've seen anything. legal transitions are to 1 or 3.
2244 # 1: ps_left_square_before. left square brackets before required parameters.
2245 # 2: ps_group_before. in a group, before required parameters.
2246 # 3: ps_required. required parameters. (renumber left groups!)
2247 # 4: ps_group_after. in a group, after required parameters.
2248 # 5: ps_right_square_after. right square brackets after required parameters.
2249 # 6: ps_seen_slash. seen slash.
2250 ps_start, ps_left_square_before, ps_group_before, ps_required, \
2251 ps_group_after, ps_right_square_after, ps_seen_slash = range(7)
2252
2253 def state_parameters_start(self, line):
2254 if self.ignore_line(line):
2255 return
2256
2257 # if this line is not indented, we have no parameters
2258 if not self.indent.infer(line):
2259 return self.next(self.state_function_docstring, line)
2260
2261 return self.next(self.state_parameter, line)
2262
2263
2264 def to_required(self):
2265 """
2266 Transition to the "required" parameter state.
2267 """
2268 if self.parameter_state != self.ps_required:
2269 self.parameter_state = self.ps_required
2270 for p in self.function.parameters.values():
2271 p.group = -p.group
2272
2273 def state_parameter(self, line):
2274 if self.ignore_line(line):
2275 return
2276
2277 assert self.indent.depth == 2
2278 indent = self.indent.infer(line)
2279 if indent == -1:
2280 # we outdented, must be to definition column
2281 return self.next(self.state_function_docstring, line)
2282
2283 if indent == 1:
2284 # we indented, must be to new parameter docstring column
2285 return self.next(self.state_parameter_docstring_start, line)
2286
2287 line = line.lstrip()
2288
2289 if line in ('*', '/', '[', ']'):
2290 self.parse_special_symbol(line)
2291 return
2292
2293 if self.parameter_state in (self.ps_start, self.ps_required):
2294 self.to_required()
2295 elif self.parameter_state == self.ps_left_square_before:
2296 self.parameter_state = self.ps_group_before
2297 elif self.parameter_state == self.ps_group_before:
2298 if not self.group:
2299 self.to_required()
2300 elif self.parameter_state == self.ps_group_after:
2301 pass
2302 else:
2303 fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ")")
2304
2305 ast_input = "def x({}): pass".format(line)
2306 module = None
2307 try:
2308 module = ast.parse(ast_input)
2309 except SyntaxError:
2310 pass
2311 if not module:
Larry Hastingsef3b1fb2013-10-22 23:26:23 -07002312 fail("Function " + self.function.name + " has an invalid parameter declaration:\n\t" + line)
Larry Hastings31826802013-10-19 00:09:25 -07002313
2314 function_args = module.body[0].args
2315 parameter = function_args.args[0]
2316
2317 if function_args.defaults:
2318 expr = function_args.defaults[0]
2319 # mild hack: explicitly support NULL as a default value
2320 if isinstance(expr, ast.Name) and expr.id == 'NULL':
2321 value = NULL
2322 else:
2323 value = ast.literal_eval(expr)
2324 else:
2325 value = unspecified
2326
2327 parameter_name = parameter.arg
2328 name, legacy, kwargs = self.parse_converter(parameter.annotation)
2329 dict = legacy_converters if legacy else converters
2330 legacy_str = "legacy " if legacy else ""
2331 if name not in dict:
2332 fail('{} is not a valid {}converter'.format(name, legacy_str))
2333 converter = dict[name](parameter_name, self.function, value, **kwargs)
2334
Larry Hastingsebdcb502013-11-23 14:54:00 -08002335 # special case: if it's the self converter,
2336 # don't actually add it to the parameter list
2337 if isinstance(converter, self_converter):
2338 if self.function.parameters or (self.parameter_state != self.ps_required):
2339 fail("The 'self' parameter, if specified, must be the very first thing in the parameter block.")
2340 if self.function.self_converter:
2341 fail("You can't specify the 'self' parameter more than once.")
2342 self.function.self_converter = converter
2343 self.parameter_state = self.ps_start
2344 return
2345
Larry Hastings31826802013-10-19 00:09:25 -07002346 kind = inspect.Parameter.KEYWORD_ONLY if self.keyword_only else inspect.Parameter.POSITIONAL_OR_KEYWORD
2347 p = Parameter(parameter_name, kind, function=self.function, converter=converter, default=value, group=self.group)
2348 self.function.parameters[parameter_name] = p
2349
2350 def parse_converter(self, annotation):
2351 if isinstance(annotation, ast.Str):
2352 return annotation.s, True, {}
2353
2354 if isinstance(annotation, ast.Name):
2355 return annotation.id, False, {}
2356
2357 assert isinstance(annotation, ast.Call)
2358
2359 name = annotation.func.id
2360 kwargs = {node.arg: ast.literal_eval(node.value) for node in annotation.keywords}
2361 return name, False, kwargs
2362
2363 def parse_special_symbol(self, symbol):
2364 if self.parameter_state == self.ps_seen_slash:
2365 fail("Function " + self.function.name + " specifies " + symbol + " after /, which is unsupported.")
2366
2367 if symbol == '*':
2368 if self.keyword_only:
2369 fail("Function " + self.function.name + " uses '*' more than once.")
2370 self.keyword_only = True
2371 elif symbol == '[':
2372 if self.parameter_state in (self.ps_start, self.ps_left_square_before):
2373 self.parameter_state = self.ps_left_square_before
2374 elif self.parameter_state in (self.ps_required, self.ps_group_after):
2375 self.parameter_state = self.ps_group_after
2376 else:
2377 fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ")")
2378 self.group += 1
2379 elif symbol == ']':
2380 if not self.group:
2381 fail("Function " + self.function.name + " has a ] without a matching [.")
2382 if not any(p.group == self.group for p in self.function.parameters.values()):
2383 fail("Function " + self.function.name + " has an empty group.\nAll groups must contain at least one parameter.")
2384 self.group -= 1
2385 if self.parameter_state in (self.ps_left_square_before, self.ps_group_before):
2386 self.parameter_state = self.ps_group_before
2387 elif self.parameter_state in (self.ps_group_after, self.ps_right_square_after):
2388 self.parameter_state = self.ps_right_square_after
2389 else:
2390 fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ")")
2391 elif symbol == '/':
2392 # ps_required is allowed here, that allows positional-only without option groups
2393 # to work (and have default values!)
2394 if (self.parameter_state not in (self.ps_required, self.ps_right_square_after, self.ps_group_before)) or self.group:
2395 fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ")")
2396 if self.keyword_only:
2397 fail("Function " + self.function.name + " mixes keyword-only and positional-only parameters, which is unsupported.")
2398 self.parameter_state = self.ps_seen_slash
2399 # fixup preceeding parameters
2400 for p in self.function.parameters.values():
2401 if p.kind != inspect.Parameter.POSITIONAL_OR_KEYWORD:
2402 fail("Function " + self.function.name + " mixes keyword-only and positional-only parameters, which is unsupported.")
2403 p.kind = inspect.Parameter.POSITIONAL_ONLY
2404
2405 def state_parameter_docstring_start(self, line):
2406 self.parameter_docstring_indent = len(self.indent.margin)
2407 assert self.indent.depth == 3
2408 return self.next(self.state_parameter_docstring, line)
2409
2410 # every line of the docstring must start with at least F spaces,
2411 # where F > P.
2412 # these F spaces will be stripped.
2413 def state_parameter_docstring(self, line):
2414 stripped = line.strip()
2415 if stripped.startswith('#'):
2416 return
2417
2418 indent = self.indent.measure(line)
2419 if indent < self.parameter_docstring_indent:
2420 self.indent.infer(line)
2421 assert self.indent.depth < 3
2422 if self.indent.depth == 2:
2423 # back to a parameter
2424 return self.next(self.state_parameter, line)
2425 assert self.indent.depth == 1
2426 return self.next(self.state_function_docstring, line)
2427
2428 assert self.function.parameters
2429 last_parameter = next(reversed(list(self.function.parameters.values())))
2430
2431 new_docstring = last_parameter.docstring
2432
2433 if new_docstring:
2434 new_docstring += '\n'
2435 if stripped:
2436 new_docstring += self.indent.dedent(line)
2437
2438 last_parameter.docstring = new_docstring
2439
2440 # the final stanza of the DSL is the docstring.
2441 def state_function_docstring(self, line):
Larry Hastings31826802013-10-19 00:09:25 -07002442 if self.group:
2443 fail("Function " + self.function.name + " has a ] without a matching [.")
2444
2445 stripped = line.strip()
2446 if stripped.startswith('#'):
2447 return
2448
2449 new_docstring = self.function.docstring
2450 if new_docstring:
2451 new_docstring += "\n"
2452 if stripped:
2453 line = self.indent.dedent(line).rstrip()
2454 else:
2455 line = ''
2456 new_docstring += line
2457 self.function.docstring = new_docstring
2458
2459 def format_docstring(self):
2460 f = self.function
2461
2462 add, output = text_accumulator()
2463 parameters = list(f.parameters.values())
2464
2465 ##
2466 ## docstring first line
2467 ##
2468
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002469 add(f.name)
Larry Hastings31826802013-10-19 00:09:25 -07002470 add('(')
2471
2472 # populate "right_bracket_count" field for every parameter
2473 if parameters:
2474 # for now, the only way Clinic supports positional-only parameters
2475 # is if all of them are positional-only.
2476 positional_only_parameters = [p.kind == inspect.Parameter.POSITIONAL_ONLY for p in parameters]
2477 if parameters[0].kind == inspect.Parameter.POSITIONAL_ONLY:
2478 assert all(positional_only_parameters)
2479 for p in parameters:
2480 p.right_bracket_count = abs(p.group)
2481 else:
2482 # don't put any right brackets around non-positional-only parameters, ever.
2483 for p in parameters:
2484 p.right_bracket_count = 0
2485
2486 right_bracket_count = 0
2487
2488 def fix_right_bracket_count(desired):
2489 nonlocal right_bracket_count
2490 s = ''
2491 while right_bracket_count < desired:
2492 s += '['
2493 right_bracket_count += 1
2494 while right_bracket_count > desired:
2495 s += ']'
2496 right_bracket_count -= 1
2497 return s
2498
2499 added_star = False
2500 add_comma = False
2501
2502 for p in parameters:
2503 assert p.name
2504
2505 if p.is_keyword_only() and not added_star:
2506 added_star = True
2507 if add_comma:
2508 add(', ')
2509 add('*')
2510
2511 a = [p.name]
2512 if p.converter.is_optional():
2513 a.append('=')
2514 value = p.converter.default
2515 a.append(p.converter.doc_default)
2516 s = fix_right_bracket_count(p.right_bracket_count)
2517 s += "".join(a)
2518 if add_comma:
2519 add(', ')
2520 add(s)
2521 add_comma = True
2522
2523 add(fix_right_bracket_count(0))
2524 add(')')
2525
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002526 # if f.return_converter.doc_default:
2527 # add(' -> ')
2528 # add(f.return_converter.doc_default)
Larry Hastings31826802013-10-19 00:09:25 -07002529
2530 docstring_first_line = output()
2531
2532 # now fix up the places where the brackets look wrong
2533 docstring_first_line = docstring_first_line.replace(', ]', ',] ')
2534
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002535 # okay. now we're officially building the "parameters" section.
Larry Hastings31826802013-10-19 00:09:25 -07002536 # create substitution text for {parameters}
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002537 spacer_line = False
Larry Hastings31826802013-10-19 00:09:25 -07002538 for p in parameters:
2539 if not p.docstring.strip():
2540 continue
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002541 if spacer_line:
2542 add('\n')
2543 else:
2544 spacer_line = True
Larry Hastings31826802013-10-19 00:09:25 -07002545 add(" ")
2546 add(p.name)
2547 add('\n')
2548 add(textwrap.indent(rstrip_lines(p.docstring.rstrip()), " "))
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002549 parameters = output()
2550 if parameters:
2551 parameters += '\n'
Larry Hastings31826802013-10-19 00:09:25 -07002552
2553 ##
2554 ## docstring body
2555 ##
2556
2557 docstring = f.docstring.rstrip()
2558 lines = [line.rstrip() for line in docstring.split('\n')]
2559
2560 # Enforce the summary line!
2561 # The first line of a docstring should be a summary of the function.
2562 # It should fit on one line (80 columns? 79 maybe?) and be a paragraph
2563 # by itself.
2564 #
2565 # Argument Clinic enforces the following rule:
2566 # * either the docstring is empty,
2567 # * or it must have a summary line.
2568 #
2569 # Guido said Clinic should enforce this:
2570 # http://mail.python.org/pipermail/python-dev/2013-June/127110.html
2571
2572 if len(lines) >= 2:
2573 if lines[1]:
2574 fail("Docstring for " + f.full_name + " does not have a summary line!\n" +
2575 "Every non-blank function docstring must start with\n" +
2576 "a single line summary followed by an empty line.")
2577 elif len(lines) == 1:
2578 # the docstring is only one line right now--the summary line.
2579 # add an empty line after the summary line so we have space
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002580 # between it and the {parameters} we're about to add.
Larry Hastings31826802013-10-19 00:09:25 -07002581 lines.append('')
2582
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002583 parameters_marker_count = len(docstring.split('{parameters}')) - 1
2584 if parameters_marker_count > 1:
2585 fail('You may not specify {parameters} more than once in a docstring!')
2586
2587 if not parameters_marker_count:
2588 # insert after summary line
2589 lines.insert(2, '{parameters}')
2590
2591 # insert at front of docstring
2592 lines.insert(0, docstring_first_line)
Larry Hastings31826802013-10-19 00:09:25 -07002593
2594 docstring = "\n".join(lines)
2595
2596 add(docstring)
2597 docstring = output()
2598
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002599 docstring = linear_format(docstring, parameters=parameters)
Larry Hastings31826802013-10-19 00:09:25 -07002600 docstring = docstring.rstrip()
2601
2602 return docstring
2603
2604 def state_terminal(self, line):
2605 """
2606 Called when processing the block is done.
2607 """
2608 assert not line
2609
2610 if not self.function:
2611 return
2612
Larry Hastings6d2ea212014-01-05 02:50:45 -08002613 if not self.function.self_converter:
2614 self.function.self_converter = self_converter("self", self.function)
2615
Larry Hastings31826802013-10-19 00:09:25 -07002616 if self.keyword_only:
2617 values = self.function.parameters.values()
2618 if not values:
2619 no_parameter_after_star = True
2620 else:
2621 last_parameter = next(reversed(list(values)))
2622 no_parameter_after_star = last_parameter.kind != inspect.Parameter.KEYWORD_ONLY
2623 if no_parameter_after_star:
2624 fail("Function " + self.function.name + " specifies '*' without any parameters afterwards.")
2625
2626 # remove trailing whitespace from all parameter docstrings
2627 for name, value in self.function.parameters.items():
2628 if not value:
2629 continue
2630 value.docstring = value.docstring.rstrip()
2631
2632 self.function.docstring = self.format_docstring()
2633
2634
2635# maps strings to callables.
2636# the callable should return an object
2637# that implements the clinic parser
2638# interface (__init__ and parse).
2639#
2640# example parsers:
2641# "clinic", handles the Clinic DSL
2642# "python", handles running Python code
2643#
2644parsers = {'clinic' : DSLParser, 'python': PythonParser}
2645
2646
2647clinic = None
2648
2649
2650def main(argv):
2651 import sys
2652
2653 if sys.version_info.major < 3 or sys.version_info.minor < 3:
2654 sys.exit("Error: clinic.py requires Python 3.3 or greater.")
2655
2656 import argparse
2657 cmdline = argparse.ArgumentParser()
2658 cmdline.add_argument("-f", "--force", action='store_true')
2659 cmdline.add_argument("-o", "--output", type=str)
2660 cmdline.add_argument("--converters", action='store_true')
Larry Hastingsdcd340e2013-11-23 14:58:45 -08002661 cmdline.add_argument("--make", action='store_true')
Larry Hastings31826802013-10-19 00:09:25 -07002662 cmdline.add_argument("filename", type=str, nargs="*")
2663 ns = cmdline.parse_args(argv)
2664
2665 if ns.converters:
2666 if ns.filename:
2667 print("Usage error: can't specify --converters and a filename at the same time.")
2668 print()
2669 cmdline.print_usage()
2670 sys.exit(-1)
2671 converters = []
2672 return_converters = []
2673 ignored = set("""
2674 add_c_converter
2675 add_c_return_converter
2676 add_default_legacy_c_converter
2677 add_legacy_c_converter
2678 """.strip().split())
2679 module = globals()
2680 for name in module:
2681 for suffix, ids in (
2682 ("_return_converter", return_converters),
2683 ("_converter", converters),
2684 ):
2685 if name in ignored:
2686 continue
2687 if name.endswith(suffix):
2688 ids.append((name, name[:-len(suffix)]))
2689 break
2690 print()
2691
2692 print("Legacy converters:")
2693 legacy = sorted(legacy_converters)
2694 print(' ' + ' '.join(c for c in legacy if c[0].isupper()))
2695 print(' ' + ' '.join(c for c in legacy if c[0].islower()))
2696 print()
2697
2698 for title, attribute, ids in (
2699 ("Converters", 'converter_init', converters),
2700 ("Return converters", 'return_converter_init', return_converters),
2701 ):
2702 print(title + ":")
2703 longest = -1
2704 for name, short_name in ids:
2705 longest = max(longest, len(short_name))
2706 for name, short_name in sorted(ids, key=lambda x: x[1].lower()):
2707 cls = module[name]
2708 callable = getattr(cls, attribute, None)
2709 if not callable:
2710 continue
2711 signature = inspect.signature(callable)
2712 parameters = []
2713 for parameter_name, parameter in signature.parameters.items():
2714 if parameter.kind == inspect.Parameter.KEYWORD_ONLY:
2715 if parameter.default != inspect.Parameter.empty:
2716 s = '{}={!r}'.format(parameter_name, parameter.default)
2717 else:
2718 s = parameter_name
2719 parameters.append(s)
2720 print(' {}({})'.format(short_name, ', '.join(parameters)))
2721 # add_comma = False
2722 # for parameter_name, parameter in signature.parameters.items():
2723 # if parameter.kind == inspect.Parameter.KEYWORD_ONLY:
2724 # if add_comma:
2725 # parameters.append(', ')
2726 # else:
2727 # add_comma = True
2728 # s = parameter_name
2729 # if parameter.default != inspect.Parameter.empty:
2730 # s += '=' + repr(parameter.default)
2731 # parameters.append(s)
2732 # parameters.append(')')
2733
2734 # print(" ", short_name + "".join(parameters))
2735 print()
Larry Hastings78cf85c2014-01-04 12:44:57 -08002736 print("All converters also accept (doc_default=None, required=False, annotation=None).")
Larry Hastings31826802013-10-19 00:09:25 -07002737 print("All return converters also accept (doc_default=None).")
2738 sys.exit(0)
2739
Larry Hastingsdcd340e2013-11-23 14:58:45 -08002740 if ns.make:
2741 if ns.output or ns.filename:
2742 print("Usage error: can't use -o or filenames with --make.")
2743 print()
2744 cmdline.print_usage()
2745 sys.exit(-1)
2746 for root, dirs, files in os.walk('.'):
2747 for rcs_dir in ('.svn', '.git', '.hg'):
2748 if rcs_dir in dirs:
2749 dirs.remove(rcs_dir)
2750 for filename in files:
2751 if not filename.endswith('.c'):
2752 continue
2753 path = os.path.join(root, filename)
2754 parse_file(path, verify=not ns.force)
2755 return
2756
Larry Hastings31826802013-10-19 00:09:25 -07002757 if not ns.filename:
2758 cmdline.print_usage()
2759 sys.exit(-1)
2760
2761 if ns.output and len(ns.filename) > 1:
2762 print("Usage error: can't use -o with multiple filenames.")
2763 print()
2764 cmdline.print_usage()
2765 sys.exit(-1)
2766
2767 for filename in ns.filename:
2768 parse_file(filename, output=ns.output, verify=not ns.force)
2769
2770
2771if __name__ == "__main__":
2772 sys.exit(main(sys.argv[1:]))