blob: 5351b6dcc4c8d586db1d9a828b6b2acd898b6b94 [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
Larry Hastingseb31e9d2014-01-06 11:10:08 -0800946 output_lines = output.splitlines(keepends=True)
947 self.line_number -= len(output_lines)
948 self.input.extend(reversed(output_lines))
Larry Hastings31826802013-10-19 00:09:25 -0700949 output = None
950
951 return Block(input_output(), dsl_name, output=output)
952
953
954class BlockPrinter:
955
956 def __init__(self, language, f=None):
957 self.language = language
958 self.f = f or io.StringIO()
959
960 def print_block(self, block):
961 input = block.input
962 output = block.output
963 dsl_name = block.dsl_name
964 write = self.f.write
965
Larry Hastings31826802013-10-19 00:09:25 -0700966 assert not ((dsl_name == None) ^ (output == None)), "you must specify dsl_name and output together, dsl_name " + repr(dsl_name)
967
968 if not dsl_name:
969 write(input)
970 return
971
972 write(self.language.start_line.format(dsl_name=dsl_name))
973 write("\n")
974
975 body_prefix = self.language.body_prefix.format(dsl_name=dsl_name)
976 if not body_prefix:
977 write(input)
978 else:
979 for line in input.split('\n'):
980 write(body_prefix)
981 write(line)
982 write("\n")
983
984 write(self.language.stop_line.format(dsl_name=dsl_name))
985 write("\n")
986
987 output = block.output
988 if output:
989 write(output)
990 if not output.endswith('\n'):
991 write('\n')
992
993 write(self.language.checksum_line.format(dsl_name=dsl_name, checksum=compute_checksum(output)))
994 write("\n")
995
996
997# maps strings to Language objects.
998# "languages" maps the name of the language ("C", "Python").
999# "extensions" maps the file extension ("c", "py").
1000languages = { 'C': CLanguage, 'Python': PythonLanguage }
Larry Hastings6d2ea212014-01-05 02:50:45 -08001001extensions = { name: CLanguage for name in "c cc cpp cxx h hh hpp hxx".split() }
1002extensions['py'] = PythonLanguage
Larry Hastings31826802013-10-19 00:09:25 -07001003
1004
1005# maps strings to callables.
1006# these callables must be of the form:
1007# def foo(name, default, *, ...)
1008# The callable may have any number of keyword-only parameters.
1009# The callable must return a CConverter object.
1010# The callable should not call builtins.print.
1011converters = {}
1012
1013# maps strings to callables.
1014# these callables follow the same rules as those for "converters" above.
1015# note however that they will never be called with keyword-only parameters.
1016legacy_converters = {}
1017
1018
1019# maps strings to callables.
1020# these callables must be of the form:
1021# def foo(*, ...)
1022# The callable may have any number of keyword-only parameters.
1023# The callable must return a CConverter object.
1024# The callable should not call builtins.print.
1025return_converters = {}
1026
1027class Clinic:
1028 def __init__(self, language, printer=None, *, verify=True, filename=None):
1029 # maps strings to Parser objects.
1030 # (instantiated from the "parsers" global.)
1031 self.parsers = {}
1032 self.language = language
1033 self.printer = printer or BlockPrinter(language)
1034 self.verify = verify
1035 self.filename = filename
1036 self.modules = collections.OrderedDict()
Larry Hastingsed4a1c52013-11-18 09:32:13 -08001037 self.classes = collections.OrderedDict()
Larry Hastings31826802013-10-19 00:09:25 -07001038
1039 global clinic
1040 clinic = self
1041
1042 def parse(self, input):
1043 printer = self.printer
1044 self.block_parser = BlockParser(input, self.language, verify=self.verify)
1045 for block in self.block_parser:
1046 dsl_name = block.dsl_name
1047 if dsl_name:
1048 if dsl_name not in self.parsers:
1049 assert dsl_name in parsers, "No parser to handle {!r} block.".format(dsl_name)
1050 self.parsers[dsl_name] = parsers[dsl_name](self)
1051 parser = self.parsers[dsl_name]
1052 parser.parse(block)
1053 printer.print_block(block)
1054 return printer.f.getvalue()
1055
1056 def _module_and_class(self, fields):
1057 """
1058 fields should be an iterable of field names.
1059 returns a tuple of (module, class).
1060 the module object could actually be self (a clinic object).
1061 this function is only ever used to find the parent of where
1062 a new class/module should go.
1063 """
1064 in_classes = False
1065 parent = module = self
1066 cls = None
1067 so_far = []
1068
1069 for field in fields:
1070 so_far.append(field)
1071 if not in_classes:
1072 child = parent.modules.get(field)
1073 if child:
Larry Hastingsed4a1c52013-11-18 09:32:13 -08001074 parent = module = child
Larry Hastings31826802013-10-19 00:09:25 -07001075 continue
1076 in_classes = True
1077 if not hasattr(parent, 'classes'):
1078 return module, cls
1079 child = parent.classes.get(field)
1080 if not child:
1081 fail('Parent class or module ' + '.'.join(so_far) + " does not exist.")
1082 cls = parent = child
1083
1084 return module, cls
1085
1086
1087def parse_file(filename, *, verify=True, output=None, encoding='utf-8'):
1088 extension = os.path.splitext(filename)[1][1:]
1089 if not extension:
1090 fail("Can't extract file type for file " + repr(filename))
1091
1092 try:
1093 language = extensions[extension]()
1094 except KeyError:
1095 fail("Can't identify file type for file " + repr(filename))
1096
1097 clinic = Clinic(language, verify=verify, filename=filename)
1098
1099 with open(filename, 'r', encoding=encoding) as f:
Larry Hastingsdcd340e2013-11-23 14:58:45 -08001100 raw = f.read()
1101
1102 cooked = clinic.parse(raw)
1103 if cooked == raw:
1104 return
Larry Hastings31826802013-10-19 00:09:25 -07001105
1106 directory = os.path.dirname(filename) or '.'
1107
1108 with tempfile.TemporaryDirectory(prefix="clinic", dir=directory) as tmpdir:
Larry Hastingsdcd340e2013-11-23 14:58:45 -08001109 bytes = cooked.encode(encoding)
Larry Hastings31826802013-10-19 00:09:25 -07001110 tmpfilename = os.path.join(tmpdir, os.path.basename(filename))
1111 with open(tmpfilename, "wb") as f:
1112 f.write(bytes)
1113 os.replace(tmpfilename, output or filename)
1114
1115
1116def compute_checksum(input):
1117 input = input or ''
1118 return hashlib.sha1(input.encode('utf-8')).hexdigest()
1119
1120
1121
1122
1123class PythonParser:
1124 def __init__(self, clinic):
1125 pass
1126
1127 def parse(self, block):
1128 s = io.StringIO()
1129 with OverrideStdioWith(s):
1130 exec(block.input)
1131 block.output = s.getvalue()
1132
1133
1134class Module:
1135 def __init__(self, name, module=None):
1136 self.name = name
1137 self.module = self.parent = module
1138
1139 self.modules = collections.OrderedDict()
1140 self.classes = collections.OrderedDict()
1141 self.functions = []
1142
Larry Hastingsed4a1c52013-11-18 09:32:13 -08001143 def __repr__(self):
1144 return "<clinic.Module " + repr(self.name) + " at " + str(id(self)) + ">"
1145
Larry Hastings31826802013-10-19 00:09:25 -07001146class Class:
1147 def __init__(self, name, module=None, cls=None):
1148 self.name = name
1149 self.module = module
1150 self.cls = cls
1151 self.parent = cls or module
1152
1153 self.classes = collections.OrderedDict()
1154 self.functions = []
1155
Larry Hastingsed4a1c52013-11-18 09:32:13 -08001156 def __repr__(self):
1157 return "<clinic.Class " + repr(self.name) + " at " + str(id(self)) + ">"
1158
1159
Larry Hastings31826802013-10-19 00:09:25 -07001160DATA, CALLABLE, METHOD, STATIC_METHOD, CLASS_METHOD = range(5)
1161
1162class Function:
1163 """
1164 Mutable duck type for inspect.Function.
1165
1166 docstring - a str containing
1167 * embedded line breaks
1168 * text outdented to the left margin
1169 * no trailing whitespace.
1170 It will always be true that
1171 (not docstring) or ((not docstring[0].isspace()) and (docstring.rstrip() == docstring))
1172 """
1173
1174 def __init__(self, parameters=None, *, name,
1175 module, cls=None, c_basename=None,
1176 full_name=None,
1177 return_converter, return_annotation=_empty,
1178 docstring=None, kind=CALLABLE, coexist=False):
1179 self.parameters = parameters or collections.OrderedDict()
1180 self.return_annotation = return_annotation
1181 self.name = name
1182 self.full_name = full_name
1183 self.module = module
1184 self.cls = cls
1185 self.parent = cls or module
1186 self.c_basename = c_basename
1187 self.return_converter = return_converter
1188 self.docstring = docstring or ''
1189 self.kind = kind
1190 self.coexist = coexist
Larry Hastingsebdcb502013-11-23 14:54:00 -08001191 self.self_converter = None
1192
1193 @property
1194 def methoddef_flags(self):
1195 flags = []
1196 if self.kind == CLASS_METHOD:
1197 flags.append('METH_CLASS')
1198 elif self.kind == STATIC_METHOD:
1199 flags.append('METH_STATIC')
1200 else:
1201 assert self.kind == CALLABLE, "unknown kind: " + repr(self.kind)
1202 if self.coexist:
1203 flags.append('METH_COEXIST')
1204 return '|'.join(flags)
Larry Hastings31826802013-10-19 00:09:25 -07001205
1206 def __repr__(self):
1207 return '<clinic.Function ' + self.name + '>'
1208
1209
1210class Parameter:
1211 """
1212 Mutable duck type of inspect.Parameter.
1213 """
1214
1215 def __init__(self, name, kind, *, default=_empty,
1216 function, converter, annotation=_empty,
1217 docstring=None, group=0):
1218 self.name = name
1219 self.kind = kind
1220 self.default = default
1221 self.function = function
1222 self.converter = converter
1223 self.annotation = annotation
1224 self.docstring = docstring or ''
1225 self.group = group
1226
1227 def __repr__(self):
1228 return '<clinic.Parameter ' + self.name + '>'
1229
1230 def is_keyword_only(self):
1231 return self.kind == inspect.Parameter.KEYWORD_ONLY
1232
1233py_special_values = {
1234 NULL: "None",
1235}
1236
1237def py_repr(o):
1238 special = py_special_values.get(o)
1239 if special:
1240 return special
1241 return repr(o)
1242
1243
1244c_special_values = {
1245 NULL: "NULL",
1246 None: "Py_None",
1247}
1248
1249def c_repr(o):
1250 special = c_special_values.get(o)
1251 if special:
1252 return special
1253 if isinstance(o, str):
1254 return '"' + quoted_for_c_string(o) + '"'
1255 return repr(o)
1256
1257def add_c_converter(f, name=None):
1258 if not name:
1259 name = f.__name__
1260 if not name.endswith('_converter'):
1261 return f
1262 name = name[:-len('_converter')]
1263 converters[name] = f
1264 return f
1265
1266def add_default_legacy_c_converter(cls):
1267 # automatically add converter for default format unit
1268 # (but without stomping on the existing one if it's already
1269 # set, in case you subclass)
1270 if ((cls.format_unit != 'O&') and
1271 (cls.format_unit not in legacy_converters)):
1272 legacy_converters[cls.format_unit] = cls
1273 return cls
1274
1275def add_legacy_c_converter(format_unit, **kwargs):
1276 """
1277 Adds a legacy converter.
1278 """
1279 def closure(f):
1280 if not kwargs:
1281 added_f = f
1282 else:
1283 added_f = functools.partial(f, **kwargs)
1284 legacy_converters[format_unit] = added_f
1285 return f
1286 return closure
1287
1288class CConverterAutoRegister(type):
1289 def __init__(cls, name, bases, classdict):
1290 add_c_converter(cls)
1291 add_default_legacy_c_converter(cls)
1292
1293class CConverter(metaclass=CConverterAutoRegister):
1294 """
1295 For the init function, self, name, function, and default
1296 must be keyword-or-positional parameters. All other
1297 parameters (including "required" and "doc_default")
1298 must be keyword-only.
1299 """
1300
Larry Hastings78cf85c2014-01-04 12:44:57 -08001301 # The C type to use for this variable.
1302 # 'type' should be a Python string specifying the type, e.g. "int".
1303 # If this is a pointer type, the type string should end with ' *'.
Larry Hastings31826802013-10-19 00:09:25 -07001304 type = None
Larry Hastings31826802013-10-19 00:09:25 -07001305
1306 # The Python default value for this parameter, as a Python value.
Larry Hastings78cf85c2014-01-04 12:44:57 -08001307 # Or the magic value "unspecified" if there is no default.
Larry Hastings31826802013-10-19 00:09:25 -07001308 default = unspecified
1309
Larry Hastings31826802013-10-19 00:09:25 -07001310 # "default" as it should appear in the documentation, as a string.
1311 # Or None if there is no default.
1312 doc_default = None
1313
Larry Hastingsabc716b2013-11-20 09:13:52 -08001314 # "default" converted into a str for rendering into Python code.
1315 py_default = None
1316
Larry Hastings31826802013-10-19 00:09:25 -07001317 # "default" converted into a C value, as a string.
1318 # Or None if there is no default.
1319 c_default = None
1320
Larry Hastingsabc716b2013-11-20 09:13:52 -08001321 # The default value used to initialize the C variable when
1322 # there is no default, but not specifying a default may
1323 # result in an "uninitialized variable" warning. This can
1324 # easily happen when using option groups--although
1325 # properly-written code won't actually use the variable,
1326 # the variable does get passed in to the _impl. (Ah, if
1327 # only dataflow analysis could inline the static function!)
1328 #
1329 # This value is specified as a string.
1330 # Every non-abstract subclass should supply a valid value.
1331 c_ignored_default = 'NULL'
1332
Larry Hastings31826802013-10-19 00:09:25 -07001333 # The C converter *function* to be used, if any.
1334 # (If this is not None, format_unit must be 'O&'.)
1335 converter = None
Larry Hastingsebdcb502013-11-23 14:54:00 -08001336
Larry Hastings78cf85c2014-01-04 12:44:57 -08001337 # Should Argument Clinic add a '&' before the name of
1338 # the variable when passing it into the _impl function?
Larry Hastings31826802013-10-19 00:09:25 -07001339 impl_by_reference = False
Larry Hastings78cf85c2014-01-04 12:44:57 -08001340
1341 # Should Argument Clinic add a '&' before the name of
1342 # the variable when passing it into PyArg_ParseTuple (AndKeywords)?
Larry Hastings31826802013-10-19 00:09:25 -07001343 parse_by_reference = True
Larry Hastings78cf85c2014-01-04 12:44:57 -08001344
1345 #############################################################
1346 #############################################################
1347 ## You shouldn't need to read anything below this point to ##
1348 ## write your own converter functions. ##
1349 #############################################################
1350 #############################################################
1351
1352 # The "format unit" to specify for this variable when
1353 # parsing arguments using PyArg_ParseTuple (AndKeywords).
1354 # Custom converters should always use the default value of 'O&'.
1355 format_unit = 'O&'
1356
1357 # What encoding do we want for this variable? Only used
1358 # by format units starting with 'e'.
1359 encoding = None
1360
1361 # Do we want an adjacent '_length' variable for this variable?
1362 # Only used by format units ending with '#'.
Larry Hastings31826802013-10-19 00:09:25 -07001363 length = False
1364
1365 def __init__(self, name, function, default=unspecified, *, doc_default=None, required=False, annotation=unspecified, **kwargs):
1366 self.function = function
1367 self.name = name
1368
1369 if default is not unspecified:
1370 self.default = default
1371 self.py_default = py_repr(default)
1372 self.doc_default = doc_default if doc_default is not None else self.py_default
1373 self.c_default = c_repr(default)
1374 elif doc_default is not None:
1375 fail(function.fullname + " argument " + name + " specified a 'doc_default' without having a 'default'")
1376 if annotation != unspecified:
1377 fail("The 'annotation' parameter is not currently permitted.")
1378 self.required = required
1379 self.converter_init(**kwargs)
1380
1381 def converter_init(self):
1382 pass
1383
1384 def is_optional(self):
1385 return (self.default is not unspecified) and (not self.required)
1386
1387 def render(self, parameter, data):
1388 """
1389 parameter is a clinic.Parameter instance.
1390 data is a CRenderData instance.
1391 """
Larry Hastingsabc716b2013-11-20 09:13:52 -08001392 self.parameter = parameter
Larry Hastingsdfcd4672013-10-27 02:49:39 -07001393 name = ensure_legal_c_identifier(self.name)
Larry Hastings31826802013-10-19 00:09:25 -07001394
1395 # declarations
1396 d = self.declaration()
1397 data.declarations.append(d)
1398
1399 # initializers
1400 initializers = self.initialize()
1401 if initializers:
1402 data.initializers.append('/* initializers for ' + name + ' */\n' + initializers.rstrip())
1403
1404 # impl_arguments
1405 s = ("&" if self.impl_by_reference else "") + name
1406 data.impl_arguments.append(s)
Larry Hastingsebdcb502013-11-23 14:54:00 -08001407 if self.length:
1408 data.impl_arguments.append(self.length_name())
Larry Hastings31826802013-10-19 00:09:25 -07001409
1410 # keywords
1411 data.keywords.append(name)
1412
1413 # format_units
1414 if self.is_optional() and '|' not in data.format_units:
1415 data.format_units.append('|')
1416 if parameter.is_keyword_only() and '$' not in data.format_units:
1417 data.format_units.append('$')
1418 data.format_units.append(self.format_unit)
1419
1420 # parse_arguments
1421 self.parse_argument(data.parse_arguments)
1422
1423 # impl_parameters
1424 data.impl_parameters.append(self.simple_declaration(by_reference=self.impl_by_reference))
Larry Hastingsebdcb502013-11-23 14:54:00 -08001425 if self.length:
1426 data.impl_parameters.append("Py_ssize_clean_t " + self.length_name())
Larry Hastings31826802013-10-19 00:09:25 -07001427
1428 # cleanup
1429 cleanup = self.cleanup()
1430 if cleanup:
1431 data.cleanup.append('/* Cleanup for ' + name + ' */\n' + cleanup.rstrip() + "\n")
1432
Larry Hastingsebdcb502013-11-23 14:54:00 -08001433 def length_name(self):
1434 """Computes the name of the associated "length" variable."""
1435 if not self.length:
1436 return None
1437 return ensure_legal_c_identifier(self.name) + "_length"
1438
Larry Hastings31826802013-10-19 00:09:25 -07001439 # Why is this one broken out separately?
1440 # For "positional-only" function parsing,
1441 # which generates a bunch of PyArg_ParseTuple calls.
1442 def parse_argument(self, list):
1443 assert not (self.converter and self.encoding)
1444 if self.format_unit == 'O&':
1445 assert self.converter
1446 list.append(self.converter)
1447
1448 if self.encoding:
1449 list.append(self.encoding)
1450
Larry Hastingsebdcb502013-11-23 14:54:00 -08001451 legal_name = ensure_legal_c_identifier(self.name)
1452 s = ("&" if self.parse_by_reference else "") + legal_name
Larry Hastings31826802013-10-19 00:09:25 -07001453 list.append(s)
1454
Larry Hastingsebdcb502013-11-23 14:54:00 -08001455 if self.length:
1456 list.append("&" + self.length_name())
1457
Larry Hastings31826802013-10-19 00:09:25 -07001458 #
1459 # All the functions after here are intended as extension points.
1460 #
1461
1462 def simple_declaration(self, by_reference=False):
1463 """
1464 Computes the basic declaration of the variable.
1465 Used in computing the prototype declaration and the
1466 variable declaration.
1467 """
1468 prototype = [self.type]
1469 if by_reference or not self.type.endswith('*'):
1470 prototype.append(" ")
1471 if by_reference:
1472 prototype.append('*')
Larry Hastingsdfcd4672013-10-27 02:49:39 -07001473 prototype.append(ensure_legal_c_identifier(self.name))
Larry Hastings31826802013-10-19 00:09:25 -07001474 return "".join(prototype)
1475
1476 def declaration(self):
1477 """
1478 The C statement to declare this variable.
1479 """
1480 declaration = [self.simple_declaration()]
Larry Hastingsabc716b2013-11-20 09:13:52 -08001481 default = self.c_default
1482 if not default and self.parameter.group:
1483 default = self.c_ignored_default
1484 if default:
Larry Hastings31826802013-10-19 00:09:25 -07001485 declaration.append(" = ")
Larry Hastingsabc716b2013-11-20 09:13:52 -08001486 declaration.append(default)
Larry Hastings31826802013-10-19 00:09:25 -07001487 declaration.append(";")
Larry Hastingsebdcb502013-11-23 14:54:00 -08001488 if self.length:
1489 declaration.append('\nPy_ssize_clean_t ')
1490 declaration.append(self.length_name())
1491 declaration.append(';')
Larry Hastings3f144c22014-01-06 10:34:00 -08001492 s = "".join(declaration)
1493 # double up curly-braces, this string will be used
1494 # as part of a format_map() template later
1495 s = s.replace("{", "{{")
1496 s = s.replace("}", "}}")
1497 return s
Larry Hastings31826802013-10-19 00:09:25 -07001498
1499 def initialize(self):
1500 """
1501 The C statements required to set up this variable before parsing.
1502 Returns a string containing this code indented at column 0.
1503 If no initialization is necessary, returns an empty string.
1504 """
1505 return ""
1506
1507 def cleanup(self):
1508 """
1509 The C statements required to clean up after this variable.
1510 Returns a string containing this code indented at column 0.
1511 If no cleanup is necessary, returns an empty string.
1512 """
1513 return ""
1514
1515
1516class bool_converter(CConverter):
1517 type = 'int'
1518 format_unit = 'p'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001519 c_ignored_default = '0'
Larry Hastings31826802013-10-19 00:09:25 -07001520
1521 def converter_init(self):
1522 self.default = bool(self.default)
1523 self.c_default = str(int(self.default))
1524
1525class char_converter(CConverter):
1526 type = 'char'
1527 format_unit = 'c'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001528 c_ignored_default = "'\0'"
Larry Hastings31826802013-10-19 00:09:25 -07001529
1530@add_legacy_c_converter('B', bitwise=True)
1531class byte_converter(CConverter):
1532 type = 'byte'
1533 format_unit = 'b'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001534 c_ignored_default = "'\0'"
Larry Hastings31826802013-10-19 00:09:25 -07001535
1536 def converter_init(self, *, bitwise=False):
1537 if bitwise:
Larry Hastingsebdcb502013-11-23 14:54:00 -08001538 self.format_unit = 'B'
Larry Hastings31826802013-10-19 00:09:25 -07001539
1540class short_converter(CConverter):
1541 type = 'short'
1542 format_unit = 'h'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001543 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001544
1545class unsigned_short_converter(CConverter):
1546 type = 'unsigned short'
1547 format_unit = 'H'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001548 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001549
1550 def converter_init(self, *, bitwise=False):
1551 if not bitwise:
1552 fail("Unsigned shorts must be bitwise (for now).")
1553
Larry Hastingsebdcb502013-11-23 14:54:00 -08001554@add_legacy_c_converter('C', types='str')
Larry Hastings31826802013-10-19 00:09:25 -07001555class int_converter(CConverter):
1556 type = 'int'
1557 format_unit = 'i'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001558 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001559
Larry Hastingsebdcb502013-11-23 14:54:00 -08001560 def converter_init(self, *, types='int'):
1561 if types == 'str':
1562 self.format_unit = 'C'
1563 elif types != 'int':
1564 fail("int_converter: illegal 'types' argument")
Larry Hastings31826802013-10-19 00:09:25 -07001565
1566class unsigned_int_converter(CConverter):
1567 type = 'unsigned int'
1568 format_unit = 'I'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001569 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001570
1571 def converter_init(self, *, bitwise=False):
1572 if not bitwise:
1573 fail("Unsigned ints must be bitwise (for now).")
1574
1575class long_converter(CConverter):
1576 type = 'long'
1577 format_unit = 'l'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001578 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001579
1580class unsigned_long_converter(CConverter):
1581 type = 'unsigned long'
1582 format_unit = 'k'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001583 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001584
1585 def converter_init(self, *, bitwise=False):
1586 if not bitwise:
1587 fail("Unsigned longs must be bitwise (for now).")
1588
1589class PY_LONG_LONG_converter(CConverter):
1590 type = 'PY_LONG_LONG'
1591 format_unit = 'L'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001592 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001593
1594class unsigned_PY_LONG_LONG_converter(CConverter):
1595 type = 'unsigned PY_LONG_LONG'
1596 format_unit = 'K'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001597 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001598
1599 def converter_init(self, *, bitwise=False):
1600 if not bitwise:
1601 fail("Unsigned PY_LONG_LONGs must be bitwise (for now).")
1602
1603class Py_ssize_t_converter(CConverter):
1604 type = 'Py_ssize_t'
1605 format_unit = 'n'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001606 c_ignored_default = "0"
Larry Hastings31826802013-10-19 00:09:25 -07001607
1608
1609class float_converter(CConverter):
1610 type = 'float'
1611 format_unit = 'f'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001612 c_ignored_default = "0.0"
Larry Hastings31826802013-10-19 00:09:25 -07001613
1614class double_converter(CConverter):
1615 type = 'double'
1616 format_unit = 'd'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001617 c_ignored_default = "0.0"
Larry Hastings31826802013-10-19 00:09:25 -07001618
1619
1620class Py_complex_converter(CConverter):
1621 type = 'Py_complex'
1622 format_unit = 'D'
Larry Hastingsabc716b2013-11-20 09:13:52 -08001623 c_ignored_default = "{0.0, 0.0}"
Larry Hastings31826802013-10-19 00:09:25 -07001624
1625
1626class object_converter(CConverter):
1627 type = 'PyObject *'
1628 format_unit = 'O'
1629
1630 def converter_init(self, *, type=None):
1631 if type:
1632 assert isinstance(type, str)
1633 assert type.isidentifier()
1634 try:
1635 type = eval(type)
1636 # need more of these!
1637 type = {
1638 str: '&PyUnicode_Type',
1639 }[type]
1640 except NameError:
1641 type = type
1642 self.format_unit = 'O!'
1643 self.encoding = type
1644
1645
Larry Hastingsebdcb502013-11-23 14:54:00 -08001646@add_legacy_c_converter('s#', length=True)
1647@add_legacy_c_converter('y', type="bytes")
1648@add_legacy_c_converter('y#', type="bytes", length=True)
Larry Hastings31826802013-10-19 00:09:25 -07001649@add_legacy_c_converter('z', nullable=True)
Larry Hastingsebdcb502013-11-23 14:54:00 -08001650@add_legacy_c_converter('z#', nullable=True, length=True)
Larry Hastings31826802013-10-19 00:09:25 -07001651class str_converter(CConverter):
1652 type = 'const char *'
1653 format_unit = 's'
1654
Larry Hastingsebdcb502013-11-23 14:54:00 -08001655 def converter_init(self, *, encoding=None, types="str",
1656 length=False, nullable=False, zeroes=False):
1657
1658 types = set(types.strip().split())
1659 bytes_type = set(("bytes",))
1660 str_type = set(("str",))
1661 all_3_type = set(("bytearray",)) | bytes_type | str_type
1662 is_bytes = types == bytes_type
1663 is_str = types == str_type
1664 is_all_3 = types == all_3_type
1665
1666 self.length = bool(length)
1667 format_unit = None
1668
1669 if encoding:
1670 self.encoding = encoding
1671
1672 if is_str and not (length or zeroes or nullable):
1673 format_unit = 'es'
1674 elif is_all_3 and not (length or zeroes or nullable):
1675 format_unit = 'et'
1676 elif is_str and length and zeroes and not nullable:
1677 format_unit = 'es#'
1678 elif is_all_3 and length and not (nullable or zeroes):
1679 format_unit = 'et#'
1680
1681 if format_unit.endswith('#'):
Larry Hastings2f9a9aa2013-11-24 04:23:35 -08001682 print("Warning: code using format unit ", repr(format_unit), "probably doesn't work properly.")
Larry Hastingsebdcb502013-11-23 14:54:00 -08001683 # TODO set pointer to NULL
1684 # TODO add cleanup for buffer
1685 pass
1686
1687 else:
1688 if zeroes:
1689 fail("str_converter: illegal combination of arguments (zeroes is only legal with an encoding)")
1690
1691 if is_bytes and not (nullable or length):
1692 format_unit = 'y'
1693 elif is_bytes and length and not nullable:
1694 format_unit = 'y#'
1695 elif is_str and not (nullable or length):
1696 format_unit = 's'
1697 elif is_str and length and not nullable:
1698 format_unit = 's#'
1699 elif is_str and nullable and not length:
1700 format_unit = 'z'
1701 elif is_str and nullable and length:
1702 format_unit = 'z#'
1703
1704 if not format_unit:
1705 fail("str_converter: illegal combination of arguments")
1706 self.format_unit = format_unit
Larry Hastings31826802013-10-19 00:09:25 -07001707
1708
1709class PyBytesObject_converter(CConverter):
1710 type = 'PyBytesObject *'
1711 format_unit = 'S'
1712
1713class PyByteArrayObject_converter(CConverter):
1714 type = 'PyByteArrayObject *'
1715 format_unit = 'Y'
1716
1717class unicode_converter(CConverter):
1718 type = 'PyObject *'
1719 format_unit = 'U'
1720
Larry Hastingsebdcb502013-11-23 14:54:00 -08001721@add_legacy_c_converter('u#', length=True)
Larry Hastings31826802013-10-19 00:09:25 -07001722@add_legacy_c_converter('Z', nullable=True)
Larry Hastingsebdcb502013-11-23 14:54:00 -08001723@add_legacy_c_converter('Z#', nullable=True, length=True)
Larry Hastings31826802013-10-19 00:09:25 -07001724class Py_UNICODE_converter(CConverter):
1725 type = 'Py_UNICODE *'
1726 format_unit = 'u'
1727
Larry Hastingsebdcb502013-11-23 14:54:00 -08001728 def converter_init(self, *, nullable=False, length=False):
1729 format_unit = 'Z' if nullable else 'u'
1730 if length:
1731 format_unit += '#'
1732 self.length = True
1733 self.format_unit = format_unit
Larry Hastings31826802013-10-19 00:09:25 -07001734
Larry Hastingsebdcb502013-11-23 14:54:00 -08001735#
1736# We define three string conventions for buffer types in the 'types' argument:
1737# 'buffer' : any object supporting the buffer interface
1738# 'rwbuffer': any object supporting the buffer interface, but must be writeable
1739# 'robuffer': any object supporting the buffer interface, but must not be writeable
1740#
1741@add_legacy_c_converter('s*', types='str bytes bytearray buffer')
1742@add_legacy_c_converter('z*', types='str bytes bytearray buffer', nullable=True)
1743@add_legacy_c_converter('w*', types='bytearray rwbuffer')
Larry Hastings31826802013-10-19 00:09:25 -07001744class Py_buffer_converter(CConverter):
1745 type = 'Py_buffer'
1746 format_unit = 'y*'
1747 impl_by_reference = True
Larry Hastingsabc716b2013-11-20 09:13:52 -08001748 c_ignored_default = "{NULL, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL}"
Larry Hastings31826802013-10-19 00:09:25 -07001749
Larry Hastingsebdcb502013-11-23 14:54:00 -08001750 def converter_init(self, *, types='bytes bytearray buffer', nullable=False):
Larry Hastings3f144c22014-01-06 10:34:00 -08001751 if self.default != unspecified:
1752 fail("There is no legal default value for Py_buffer ")
1753 self.c_default = self.c_ignored_default
Larry Hastingsebdcb502013-11-23 14:54:00 -08001754 types = set(types.strip().split())
1755 bytes_type = set(('bytes',))
1756 bytearray_type = set(('bytearray',))
1757 buffer_type = set(('buffer',))
1758 rwbuffer_type = set(('rwbuffer',))
1759 robuffer_type = set(('robuffer',))
1760 str_type = set(('str',))
1761 bytes_bytearray_buffer_type = bytes_type | bytearray_type | buffer_type
1762
1763 format_unit = None
1764 if types == (str_type | bytes_bytearray_buffer_type):
1765 format_unit = 's*' if not nullable else 'z*'
Larry Hastings31826802013-10-19 00:09:25 -07001766 else:
Larry Hastingsebdcb502013-11-23 14:54:00 -08001767 if nullable:
1768 fail('Py_buffer_converter: illegal combination of arguments (nullable=True)')
1769 elif types == (bytes_bytearray_buffer_type):
1770 format_unit = 'y*'
1771 elif types == (bytearray_type | rwuffer_type):
1772 format_unit = 'w*'
1773 if not format_unit:
1774 fail("Py_buffer_converter: illegal combination of arguments")
1775
1776 self.format_unit = format_unit
Larry Hastings31826802013-10-19 00:09:25 -07001777
1778 def cleanup(self):
Larry Hastingsebdcb502013-11-23 14:54:00 -08001779 name = ensure_legal_c_identifier(self.name)
1780 return "".join(["if (", name, ".buf)\n PyBuffer_Release(&", name, ");\n"])
1781
1782
1783class self_converter(CConverter):
1784 """
1785 A special-case converter:
1786 this is the default converter used for "self".
1787 """
1788 type = "PyObject *"
Larry Hastings78cf85c2014-01-04 12:44:57 -08001789 def converter_init(self, *, type=None):
Larry Hastingsebdcb502013-11-23 14:54:00 -08001790 f = self.function
1791 if f.kind == CALLABLE:
1792 if f.cls:
1793 self.name = "self"
1794 else:
1795 self.name = "module"
1796 self.type = "PyModuleDef *"
1797 elif f.kind == STATIC_METHOD:
1798 self.name = "null"
1799 self.type = "void *"
1800 elif f.kind == CLASS_METHOD:
1801 self.name = "cls"
1802 self.type = "PyTypeObject *"
1803
Larry Hastings78cf85c2014-01-04 12:44:57 -08001804 if type:
1805 self.type = type
1806
Larry Hastingsebdcb502013-11-23 14:54:00 -08001807 def render(self, parameter, data):
1808 fail("render() should never be called on self_converter instances")
1809
Larry Hastings31826802013-10-19 00:09:25 -07001810
1811
1812def add_c_return_converter(f, name=None):
1813 if not name:
1814 name = f.__name__
1815 if not name.endswith('_return_converter'):
1816 return f
1817 name = name[:-len('_return_converter')]
1818 return_converters[name] = f
1819 return f
1820
1821
1822class CReturnConverterAutoRegister(type):
1823 def __init__(cls, name, bases, classdict):
1824 add_c_return_converter(cls)
1825
1826class CReturnConverter(metaclass=CReturnConverterAutoRegister):
1827
Larry Hastings78cf85c2014-01-04 12:44:57 -08001828 # The C type to use for this variable.
1829 # 'type' should be a Python string specifying the type, e.g. "int".
1830 # If this is a pointer type, the type string should end with ' *'.
Larry Hastings31826802013-10-19 00:09:25 -07001831 type = 'PyObject *'
Larry Hastings78cf85c2014-01-04 12:44:57 -08001832
1833 # The Python default value for this parameter, as a Python value.
1834 # Or the magic value "unspecified" if there is no default.
Larry Hastings31826802013-10-19 00:09:25 -07001835 default = None
1836
1837 def __init__(self, *, doc_default=None, **kwargs):
1838 self.doc_default = doc_default
1839 try:
1840 self.return_converter_init(**kwargs)
1841 except TypeError as e:
1842 s = ', '.join(name + '=' + repr(value) for name, value in kwargs.items())
1843 sys.exit(self.__class__.__name__ + '(' + s + ')\n' + str(e))
1844
1845 def return_converter_init(self):
1846 pass
1847
1848 def declare(self, data, name="_return_value"):
1849 line = []
1850 add = line.append
1851 add(self.type)
1852 if not self.type.endswith('*'):
1853 add(' ')
1854 add(name + ';')
1855 data.declarations.append(''.join(line))
1856 data.return_value = name
1857
1858 def err_occurred_if(self, expr, data):
1859 data.return_conversion.append('if (({}) && PyErr_Occurred())\n goto exit;\n'.format(expr))
1860
1861 def err_occurred_if_null_pointer(self, variable, data):
1862 data.return_conversion.append('if ({} == NULL)\n goto exit;\n'.format(variable))
1863
1864 def render(self, function, data):
1865 """
1866 function is a clinic.Function instance.
1867 data is a CRenderData instance.
1868 """
1869 pass
1870
1871add_c_return_converter(CReturnConverter, 'object')
1872
Larry Hastings78cf85c2014-01-04 12:44:57 -08001873class NoneType_return_converter(CReturnConverter):
1874 def render(self, function, data):
1875 self.declare(data)
1876 data.return_conversion.append('''
1877if (_return_value != Py_None)
1878 goto exit;
1879return_value = Py_None;
1880Py_INCREF(Py_None);
1881'''.strip())
1882
Larry Hastings31826802013-10-19 00:09:25 -07001883class int_return_converter(CReturnConverter):
1884 type = 'int'
1885
1886 def render(self, function, data):
1887 self.declare(data)
1888 self.err_occurred_if("_return_value == -1", data)
1889 data.return_conversion.append(
1890 'return_value = PyLong_FromLong((long)_return_value);\n')
1891
1892
1893class long_return_converter(CReturnConverter):
1894 type = 'long'
1895
1896 def render(self, function, data):
1897 self.declare(data)
1898 self.err_occurred_if("_return_value == -1", data)
1899 data.return_conversion.append(
1900 'return_value = PyLong_FromLong(_return_value);\n')
1901
1902
1903class Py_ssize_t_return_converter(CReturnConverter):
1904 type = 'Py_ssize_t'
1905
1906 def render(self, function, data):
1907 self.declare(data)
1908 self.err_occurred_if("_return_value == -1", data)
1909 data.return_conversion.append(
1910 'return_value = PyLong_FromSsize_t(_return_value);\n')
1911
1912
1913class DecodeFSDefault_return_converter(CReturnConverter):
1914 type = 'char *'
1915
1916 def render(self, function, data):
1917 self.declare(data)
1918 self.err_occurred_if_null_pointer("_return_value", data)
1919 data.return_conversion.append(
1920 'return_value = PyUnicode_DecodeFSDefault(_return_value);\n')
1921
1922
1923class IndentStack:
1924 def __init__(self):
1925 self.indents = []
1926 self.margin = None
1927
1928 def _ensure(self):
1929 if not self.indents:
1930 fail('IndentStack expected indents, but none are defined.')
1931
1932 def measure(self, line):
1933 """
1934 Returns the length of the line's margin.
1935 """
1936 if '\t' in line:
1937 fail('Tab characters are illegal in the Clinic DSL.')
1938 stripped = line.lstrip()
1939 if not len(stripped):
1940 # we can't tell anything from an empty line
1941 # so just pretend it's indented like our current indent
1942 self._ensure()
1943 return self.indents[-1]
1944 return len(line) - len(stripped)
1945
1946 def infer(self, line):
1947 """
1948 Infer what is now the current margin based on this line.
1949 Returns:
1950 1 if we have indented (or this is the first margin)
1951 0 if the margin has not changed
1952 -N if we have dedented N times
1953 """
1954 indent = self.measure(line)
1955 margin = ' ' * indent
1956 if not self.indents:
1957 self.indents.append(indent)
1958 self.margin = margin
1959 return 1
1960 current = self.indents[-1]
1961 if indent == current:
1962 return 0
1963 if indent > current:
1964 self.indents.append(indent)
1965 self.margin = margin
1966 return 1
1967 # indent < current
1968 if indent not in self.indents:
1969 fail("Illegal outdent.")
1970 outdent_count = 0
1971 while indent != current:
1972 self.indents.pop()
1973 current = self.indents[-1]
1974 outdent_count -= 1
1975 self.margin = margin
1976 return outdent_count
1977
1978 @property
1979 def depth(self):
1980 """
1981 Returns how many margins are currently defined.
1982 """
1983 return len(self.indents)
1984
1985 def indent(self, line):
1986 """
1987 Indents a line by the currently defined margin.
1988 """
1989 return self.margin + line
1990
1991 def dedent(self, line):
1992 """
1993 Dedents a line by the currently defined margin.
1994 (The inverse of 'indent'.)
1995 """
1996 margin = self.margin
1997 indent = self.indents[-1]
1998 if not line.startswith(margin):
1999 fail('Cannot dedent, line does not start with the previous margin:')
2000 return line[indent:]
2001
2002
2003class DSLParser:
2004 def __init__(self, clinic):
2005 self.clinic = clinic
2006
2007 self.directives = {}
2008 for name in dir(self):
2009 # functions that start with directive_ are added to directives
2010 _, s, key = name.partition("directive_")
2011 if s:
2012 self.directives[key] = getattr(self, name)
2013
2014 # functions that start with at_ are too, with an @ in front
2015 _, s, key = name.partition("at_")
2016 if s:
2017 self.directives['@' + key] = getattr(self, name)
2018
2019 self.reset()
2020
2021 def reset(self):
2022 self.function = None
2023 self.state = self.state_dsl_start
2024 self.parameter_indent = None
2025 self.keyword_only = False
2026 self.group = 0
2027 self.parameter_state = self.ps_start
2028 self.indent = IndentStack()
2029 self.kind = CALLABLE
2030 self.coexist = False
2031
Larry Hastingsebdcb502013-11-23 14:54:00 -08002032 def directive_version(self, required):
2033 global version
2034 if version_comparitor(version, required) < 0:
2035 fail("Insufficient Clinic version!\n Version: " + version + "\n Required: " + required)
2036
Larry Hastings31826802013-10-19 00:09:25 -07002037 def directive_module(self, name):
2038 fields = name.split('.')
2039 new = fields.pop()
2040 module, cls = self.clinic._module_and_class(fields)
2041 if cls:
2042 fail("Can't nest a module inside a class!")
2043 m = Module(name, module)
2044 module.modules[name] = m
2045 self.block.signatures.append(m)
2046
2047 def directive_class(self, name):
2048 fields = name.split('.')
2049 in_classes = False
2050 parent = self
2051 name = fields.pop()
2052 so_far = []
2053 module, cls = self.clinic._module_and_class(fields)
2054
Larry Hastings31826802013-10-19 00:09:25 -07002055 c = Class(name, module, cls)
Larry Hastings31826802013-10-19 00:09:25 -07002056 if cls:
2057 cls.classes[name] = c
Larry Hastingsed4a1c52013-11-18 09:32:13 -08002058 else:
2059 module.classes[name] = c
Larry Hastings31826802013-10-19 00:09:25 -07002060 self.block.signatures.append(c)
2061
2062 def at_classmethod(self):
2063 assert self.kind is CALLABLE
2064 self.kind = CLASS_METHOD
2065
2066 def at_staticmethod(self):
2067 assert self.kind is CALLABLE
2068 self.kind = STATIC_METHOD
2069
2070 def at_coexist(self):
2071 assert self.coexist == False
2072 self.coexist = True
2073
Larry Hastingsebdcb502013-11-23 14:54:00 -08002074
Larry Hastings31826802013-10-19 00:09:25 -07002075 def parse(self, block):
2076 self.reset()
2077 self.block = block
2078 block_start = self.clinic.block_parser.line_number
2079 lines = block.input.split('\n')
2080 for line_number, line in enumerate(lines, self.clinic.block_parser.block_start_line_number):
2081 if '\t' in line:
2082 fail('Tab characters are illegal in the Clinic DSL.\n\t' + repr(line), line_number=block_start)
2083 self.state(line)
2084
2085 self.next(self.state_terminal)
2086 self.state(None)
2087
2088 block.output = self.clinic.language.render(block.signatures)
2089
2090 @staticmethod
2091 def ignore_line(line):
2092 # ignore comment-only lines
2093 if line.lstrip().startswith('#'):
2094 return True
2095
2096 # Ignore empty lines too
2097 # (but not in docstring sections!)
2098 if not line.strip():
2099 return True
2100
2101 return False
2102
2103 @staticmethod
2104 def calculate_indent(line):
2105 return len(line) - len(line.strip())
2106
2107 def next(self, state, line=None):
2108 # real_print(self.state.__name__, "->", state.__name__, ", line=", line)
2109 self.state = state
2110 if line is not None:
2111 self.state(line)
2112
2113 def state_dsl_start(self, line):
2114 # self.block = self.ClinicOutputBlock(self)
2115 if self.ignore_line(line):
2116 return
2117 self.next(self.state_modulename_name, line)
2118
2119 def state_modulename_name(self, line):
2120 # looking for declaration, which establishes the leftmost column
2121 # line should be
2122 # modulename.fnname [as c_basename] [-> return annotation]
2123 # square brackets denote optional syntax.
2124 #
2125 # (but we might find a directive first!)
2126 #
2127 # this line is permitted to start with whitespace.
2128 # we'll call this number of spaces F (for "function").
2129
2130 if not line.strip():
2131 return
2132
2133 self.indent.infer(line)
2134
2135 # is it a directive?
2136 fields = shlex.split(line)
2137 directive_name = fields[0]
2138 directive = self.directives.get(directive_name, None)
2139 if directive:
2140 directive(*fields[1:])
2141 return
2142
2143 line, _, returns = line.partition('->')
2144
2145 full_name, _, c_basename = line.partition(' as ')
2146 full_name = full_name.strip()
2147 c_basename = c_basename.strip() or None
2148
Larry Hastingsdfcd4672013-10-27 02:49:39 -07002149 if not is_legal_py_identifier(full_name):
2150 fail("Illegal function name: {}".format(full_name))
2151 if c_basename and not is_legal_c_identifier(c_basename):
2152 fail("Illegal C basename: {}".format(c_basename))
2153
Larry Hastings31826802013-10-19 00:09:25 -07002154 if not returns:
2155 return_converter = CReturnConverter()
2156 else:
2157 ast_input = "def x() -> {}: pass".format(returns)
2158 module = None
2159 try:
2160 module = ast.parse(ast_input)
2161 except SyntaxError:
2162 pass
2163 if not module:
2164 fail("Badly-formed annotation for " + full_name + ": " + returns)
2165 try:
2166 name, legacy, kwargs = self.parse_converter(module.body[0].returns)
2167 assert not legacy
2168 if name not in return_converters:
2169 fail("Error: No available return converter called " + repr(name))
2170 return_converter = return_converters[name](**kwargs)
2171 except ValueError:
2172 fail("Badly-formed annotation for " + full_name + ": " + returns)
2173
2174 fields = [x.strip() for x in full_name.split('.')]
2175 function_name = fields.pop()
2176 module, cls = self.clinic._module_and_class(fields)
2177
2178 if not module:
2179 fail("Undefined module used in declaration of " + repr(full_name.strip()) + ".")
2180 self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,
2181 return_converter=return_converter, kind=self.kind, coexist=self.coexist)
2182 self.block.signatures.append(self.function)
2183 self.next(self.state_parameters_start)
2184
2185 # Now entering the parameters section. The rules, formally stated:
2186 #
2187 # * All lines must be indented with spaces only.
2188 # * The first line must be a parameter declaration.
2189 # * The first line must be indented.
2190 # * This first line establishes the indent for parameters.
2191 # * We'll call this number of spaces P (for "parameter").
2192 # * Thenceforth:
2193 # * Lines indented with P spaces specify a parameter.
2194 # * Lines indented with > P spaces are docstrings for the previous
2195 # parameter.
2196 # * We'll call this number of spaces D (for "docstring").
2197 # * All subsequent lines indented with >= D spaces are stored as
2198 # part of the per-parameter docstring.
2199 # * All lines will have the first D spaces of the indent stripped
2200 # before they are stored.
2201 # * It's illegal to have a line starting with a number of spaces X
2202 # such that P < X < D.
2203 # * A line with < P spaces is the first line of the function
2204 # docstring, which ends processing for parameters and per-parameter
2205 # docstrings.
2206 # * The first line of the function docstring must be at the same
2207 # indent as the function declaration.
2208 # * It's illegal to have any line in the parameters section starting
2209 # with X spaces such that F < X < P. (As before, F is the indent
2210 # of the function declaration.)
2211 #
2212 ##############
2213 #
2214 # Also, currently Argument Clinic places the following restrictions on groups:
2215 # * Each group must contain at least one parameter.
2216 # * Each group may contain at most one group, which must be the furthest
2217 # thing in the group from the required parameters. (The nested group
2218 # must be the first in the group when it's before the required
2219 # parameters, and the last thing in the group when after the required
2220 # parameters.)
2221 # * There may be at most one (top-level) group to the left or right of
2222 # the required parameters.
2223 # * You must specify a slash, and it must be after all parameters.
2224 # (In other words: either all parameters are positional-only,
2225 # or none are.)
2226 #
2227 # Said another way:
2228 # * Each group must contain at least one parameter.
2229 # * All left square brackets before the required parameters must be
2230 # consecutive. (You can't have a left square bracket followed
2231 # by a parameter, then another left square bracket. You can't
2232 # have a left square bracket, a parameter, a right square bracket,
2233 # and then a left square bracket.)
2234 # * All right square brackets after the required parameters must be
2235 # consecutive.
2236 #
2237 # These rules are enforced with a single state variable:
2238 # "parameter_state". (Previously the code was a miasma of ifs and
2239 # separate boolean state variables.) The states are:
2240 #
2241 # [ [ a, b, ] c, ] d, e, f, [ g, h, [ i ] ] / <- line
2242 # 01 2 3 4 5 6 <- state transitions
2243 #
2244 # 0: ps_start. before we've seen anything. legal transitions are to 1 or 3.
2245 # 1: ps_left_square_before. left square brackets before required parameters.
2246 # 2: ps_group_before. in a group, before required parameters.
2247 # 3: ps_required. required parameters. (renumber left groups!)
2248 # 4: ps_group_after. in a group, after required parameters.
2249 # 5: ps_right_square_after. right square brackets after required parameters.
2250 # 6: ps_seen_slash. seen slash.
2251 ps_start, ps_left_square_before, ps_group_before, ps_required, \
2252 ps_group_after, ps_right_square_after, ps_seen_slash = range(7)
2253
2254 def state_parameters_start(self, line):
2255 if self.ignore_line(line):
2256 return
2257
2258 # if this line is not indented, we have no parameters
2259 if not self.indent.infer(line):
2260 return self.next(self.state_function_docstring, line)
2261
2262 return self.next(self.state_parameter, line)
2263
2264
2265 def to_required(self):
2266 """
2267 Transition to the "required" parameter state.
2268 """
2269 if self.parameter_state != self.ps_required:
2270 self.parameter_state = self.ps_required
2271 for p in self.function.parameters.values():
2272 p.group = -p.group
2273
2274 def state_parameter(self, line):
2275 if self.ignore_line(line):
2276 return
2277
2278 assert self.indent.depth == 2
2279 indent = self.indent.infer(line)
2280 if indent == -1:
2281 # we outdented, must be to definition column
2282 return self.next(self.state_function_docstring, line)
2283
2284 if indent == 1:
2285 # we indented, must be to new parameter docstring column
2286 return self.next(self.state_parameter_docstring_start, line)
2287
2288 line = line.lstrip()
2289
2290 if line in ('*', '/', '[', ']'):
2291 self.parse_special_symbol(line)
2292 return
2293
2294 if self.parameter_state in (self.ps_start, self.ps_required):
2295 self.to_required()
2296 elif self.parameter_state == self.ps_left_square_before:
2297 self.parameter_state = self.ps_group_before
2298 elif self.parameter_state == self.ps_group_before:
2299 if not self.group:
2300 self.to_required()
2301 elif self.parameter_state == self.ps_group_after:
2302 pass
2303 else:
2304 fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ")")
2305
2306 ast_input = "def x({}): pass".format(line)
2307 module = None
2308 try:
2309 module = ast.parse(ast_input)
2310 except SyntaxError:
2311 pass
2312 if not module:
Larry Hastingsef3b1fb2013-10-22 23:26:23 -07002313 fail("Function " + self.function.name + " has an invalid parameter declaration:\n\t" + line)
Larry Hastings31826802013-10-19 00:09:25 -07002314
2315 function_args = module.body[0].args
2316 parameter = function_args.args[0]
2317
2318 if function_args.defaults:
2319 expr = function_args.defaults[0]
2320 # mild hack: explicitly support NULL as a default value
2321 if isinstance(expr, ast.Name) and expr.id == 'NULL':
2322 value = NULL
2323 else:
2324 value = ast.literal_eval(expr)
2325 else:
2326 value = unspecified
2327
2328 parameter_name = parameter.arg
2329 name, legacy, kwargs = self.parse_converter(parameter.annotation)
2330 dict = legacy_converters if legacy else converters
2331 legacy_str = "legacy " if legacy else ""
2332 if name not in dict:
2333 fail('{} is not a valid {}converter'.format(name, legacy_str))
2334 converter = dict[name](parameter_name, self.function, value, **kwargs)
2335
Larry Hastingsebdcb502013-11-23 14:54:00 -08002336 # special case: if it's the self converter,
2337 # don't actually add it to the parameter list
2338 if isinstance(converter, self_converter):
2339 if self.function.parameters or (self.parameter_state != self.ps_required):
2340 fail("The 'self' parameter, if specified, must be the very first thing in the parameter block.")
2341 if self.function.self_converter:
2342 fail("You can't specify the 'self' parameter more than once.")
2343 self.function.self_converter = converter
2344 self.parameter_state = self.ps_start
2345 return
2346
Larry Hastings31826802013-10-19 00:09:25 -07002347 kind = inspect.Parameter.KEYWORD_ONLY if self.keyword_only else inspect.Parameter.POSITIONAL_OR_KEYWORD
2348 p = Parameter(parameter_name, kind, function=self.function, converter=converter, default=value, group=self.group)
2349 self.function.parameters[parameter_name] = p
2350
2351 def parse_converter(self, annotation):
2352 if isinstance(annotation, ast.Str):
2353 return annotation.s, True, {}
2354
2355 if isinstance(annotation, ast.Name):
2356 return annotation.id, False, {}
2357
2358 assert isinstance(annotation, ast.Call)
2359
2360 name = annotation.func.id
2361 kwargs = {node.arg: ast.literal_eval(node.value) for node in annotation.keywords}
2362 return name, False, kwargs
2363
2364 def parse_special_symbol(self, symbol):
2365 if self.parameter_state == self.ps_seen_slash:
2366 fail("Function " + self.function.name + " specifies " + symbol + " after /, which is unsupported.")
2367
2368 if symbol == '*':
2369 if self.keyword_only:
2370 fail("Function " + self.function.name + " uses '*' more than once.")
2371 self.keyword_only = True
2372 elif symbol == '[':
2373 if self.parameter_state in (self.ps_start, self.ps_left_square_before):
2374 self.parameter_state = self.ps_left_square_before
2375 elif self.parameter_state in (self.ps_required, self.ps_group_after):
2376 self.parameter_state = self.ps_group_after
2377 else:
2378 fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ")")
2379 self.group += 1
2380 elif symbol == ']':
2381 if not self.group:
2382 fail("Function " + self.function.name + " has a ] without a matching [.")
2383 if not any(p.group == self.group for p in self.function.parameters.values()):
2384 fail("Function " + self.function.name + " has an empty group.\nAll groups must contain at least one parameter.")
2385 self.group -= 1
2386 if self.parameter_state in (self.ps_left_square_before, self.ps_group_before):
2387 self.parameter_state = self.ps_group_before
2388 elif self.parameter_state in (self.ps_group_after, self.ps_right_square_after):
2389 self.parameter_state = self.ps_right_square_after
2390 else:
2391 fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ")")
2392 elif symbol == '/':
2393 # ps_required is allowed here, that allows positional-only without option groups
2394 # to work (and have default values!)
2395 if (self.parameter_state not in (self.ps_required, self.ps_right_square_after, self.ps_group_before)) or self.group:
2396 fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ")")
2397 if self.keyword_only:
2398 fail("Function " + self.function.name + " mixes keyword-only and positional-only parameters, which is unsupported.")
2399 self.parameter_state = self.ps_seen_slash
2400 # fixup preceeding parameters
2401 for p in self.function.parameters.values():
2402 if p.kind != inspect.Parameter.POSITIONAL_OR_KEYWORD:
2403 fail("Function " + self.function.name + " mixes keyword-only and positional-only parameters, which is unsupported.")
2404 p.kind = inspect.Parameter.POSITIONAL_ONLY
2405
2406 def state_parameter_docstring_start(self, line):
2407 self.parameter_docstring_indent = len(self.indent.margin)
2408 assert self.indent.depth == 3
2409 return self.next(self.state_parameter_docstring, line)
2410
2411 # every line of the docstring must start with at least F spaces,
2412 # where F > P.
2413 # these F spaces will be stripped.
2414 def state_parameter_docstring(self, line):
2415 stripped = line.strip()
2416 if stripped.startswith('#'):
2417 return
2418
2419 indent = self.indent.measure(line)
2420 if indent < self.parameter_docstring_indent:
2421 self.indent.infer(line)
2422 assert self.indent.depth < 3
2423 if self.indent.depth == 2:
2424 # back to a parameter
2425 return self.next(self.state_parameter, line)
2426 assert self.indent.depth == 1
2427 return self.next(self.state_function_docstring, line)
2428
2429 assert self.function.parameters
2430 last_parameter = next(reversed(list(self.function.parameters.values())))
2431
2432 new_docstring = last_parameter.docstring
2433
2434 if new_docstring:
2435 new_docstring += '\n'
2436 if stripped:
2437 new_docstring += self.indent.dedent(line)
2438
2439 last_parameter.docstring = new_docstring
2440
2441 # the final stanza of the DSL is the docstring.
2442 def state_function_docstring(self, line):
Larry Hastings31826802013-10-19 00:09:25 -07002443 if self.group:
2444 fail("Function " + self.function.name + " has a ] without a matching [.")
2445
2446 stripped = line.strip()
2447 if stripped.startswith('#'):
2448 return
2449
2450 new_docstring = self.function.docstring
2451 if new_docstring:
2452 new_docstring += "\n"
2453 if stripped:
2454 line = self.indent.dedent(line).rstrip()
2455 else:
2456 line = ''
2457 new_docstring += line
2458 self.function.docstring = new_docstring
2459
2460 def format_docstring(self):
2461 f = self.function
2462
2463 add, output = text_accumulator()
2464 parameters = list(f.parameters.values())
2465
2466 ##
2467 ## docstring first line
2468 ##
2469
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002470 add(f.name)
Larry Hastings31826802013-10-19 00:09:25 -07002471 add('(')
2472
2473 # populate "right_bracket_count" field for every parameter
2474 if parameters:
2475 # for now, the only way Clinic supports positional-only parameters
2476 # is if all of them are positional-only.
2477 positional_only_parameters = [p.kind == inspect.Parameter.POSITIONAL_ONLY for p in parameters]
2478 if parameters[0].kind == inspect.Parameter.POSITIONAL_ONLY:
2479 assert all(positional_only_parameters)
2480 for p in parameters:
2481 p.right_bracket_count = abs(p.group)
2482 else:
2483 # don't put any right brackets around non-positional-only parameters, ever.
2484 for p in parameters:
2485 p.right_bracket_count = 0
2486
2487 right_bracket_count = 0
2488
2489 def fix_right_bracket_count(desired):
2490 nonlocal right_bracket_count
2491 s = ''
2492 while right_bracket_count < desired:
2493 s += '['
2494 right_bracket_count += 1
2495 while right_bracket_count > desired:
2496 s += ']'
2497 right_bracket_count -= 1
2498 return s
2499
2500 added_star = False
2501 add_comma = False
2502
2503 for p in parameters:
2504 assert p.name
2505
2506 if p.is_keyword_only() and not added_star:
2507 added_star = True
2508 if add_comma:
2509 add(', ')
2510 add('*')
2511
2512 a = [p.name]
2513 if p.converter.is_optional():
2514 a.append('=')
2515 value = p.converter.default
2516 a.append(p.converter.doc_default)
2517 s = fix_right_bracket_count(p.right_bracket_count)
2518 s += "".join(a)
2519 if add_comma:
2520 add(', ')
2521 add(s)
2522 add_comma = True
2523
2524 add(fix_right_bracket_count(0))
2525 add(')')
2526
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002527 # if f.return_converter.doc_default:
2528 # add(' -> ')
2529 # add(f.return_converter.doc_default)
Larry Hastings31826802013-10-19 00:09:25 -07002530
2531 docstring_first_line = output()
2532
2533 # now fix up the places where the brackets look wrong
2534 docstring_first_line = docstring_first_line.replace(', ]', ',] ')
2535
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002536 # okay. now we're officially building the "parameters" section.
Larry Hastings31826802013-10-19 00:09:25 -07002537 # create substitution text for {parameters}
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002538 spacer_line = False
Larry Hastings31826802013-10-19 00:09:25 -07002539 for p in parameters:
2540 if not p.docstring.strip():
2541 continue
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002542 if spacer_line:
2543 add('\n')
2544 else:
2545 spacer_line = True
Larry Hastings31826802013-10-19 00:09:25 -07002546 add(" ")
2547 add(p.name)
2548 add('\n')
2549 add(textwrap.indent(rstrip_lines(p.docstring.rstrip()), " "))
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002550 parameters = output()
2551 if parameters:
2552 parameters += '\n'
Larry Hastings31826802013-10-19 00:09:25 -07002553
2554 ##
2555 ## docstring body
2556 ##
2557
2558 docstring = f.docstring.rstrip()
2559 lines = [line.rstrip() for line in docstring.split('\n')]
2560
2561 # Enforce the summary line!
2562 # The first line of a docstring should be a summary of the function.
2563 # It should fit on one line (80 columns? 79 maybe?) and be a paragraph
2564 # by itself.
2565 #
2566 # Argument Clinic enforces the following rule:
2567 # * either the docstring is empty,
2568 # * or it must have a summary line.
2569 #
2570 # Guido said Clinic should enforce this:
2571 # http://mail.python.org/pipermail/python-dev/2013-June/127110.html
2572
2573 if len(lines) >= 2:
2574 if lines[1]:
2575 fail("Docstring for " + f.full_name + " does not have a summary line!\n" +
2576 "Every non-blank function docstring must start with\n" +
2577 "a single line summary followed by an empty line.")
2578 elif len(lines) == 1:
2579 # the docstring is only one line right now--the summary line.
2580 # add an empty line after the summary line so we have space
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002581 # between it and the {parameters} we're about to add.
Larry Hastings31826802013-10-19 00:09:25 -07002582 lines.append('')
2583
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002584 parameters_marker_count = len(docstring.split('{parameters}')) - 1
2585 if parameters_marker_count > 1:
2586 fail('You may not specify {parameters} more than once in a docstring!')
2587
2588 if not parameters_marker_count:
2589 # insert after summary line
2590 lines.insert(2, '{parameters}')
2591
2592 # insert at front of docstring
2593 lines.insert(0, docstring_first_line)
Larry Hastings31826802013-10-19 00:09:25 -07002594
2595 docstring = "\n".join(lines)
2596
2597 add(docstring)
2598 docstring = output()
2599
Larry Hastings44e2eaa2013-11-23 15:37:55 -08002600 docstring = linear_format(docstring, parameters=parameters)
Larry Hastings31826802013-10-19 00:09:25 -07002601 docstring = docstring.rstrip()
2602
2603 return docstring
2604
2605 def state_terminal(self, line):
2606 """
2607 Called when processing the block is done.
2608 """
2609 assert not line
2610
2611 if not self.function:
2612 return
2613
Larry Hastings6d2ea212014-01-05 02:50:45 -08002614 if not self.function.self_converter:
2615 self.function.self_converter = self_converter("self", self.function)
2616
Larry Hastings31826802013-10-19 00:09:25 -07002617 if self.keyword_only:
2618 values = self.function.parameters.values()
2619 if not values:
2620 no_parameter_after_star = True
2621 else:
2622 last_parameter = next(reversed(list(values)))
2623 no_parameter_after_star = last_parameter.kind != inspect.Parameter.KEYWORD_ONLY
2624 if no_parameter_after_star:
2625 fail("Function " + self.function.name + " specifies '*' without any parameters afterwards.")
2626
2627 # remove trailing whitespace from all parameter docstrings
2628 for name, value in self.function.parameters.items():
2629 if not value:
2630 continue
2631 value.docstring = value.docstring.rstrip()
2632
2633 self.function.docstring = self.format_docstring()
2634
2635
2636# maps strings to callables.
2637# the callable should return an object
2638# that implements the clinic parser
2639# interface (__init__ and parse).
2640#
2641# example parsers:
2642# "clinic", handles the Clinic DSL
2643# "python", handles running Python code
2644#
2645parsers = {'clinic' : DSLParser, 'python': PythonParser}
2646
2647
2648clinic = None
2649
2650
2651def main(argv):
2652 import sys
2653
2654 if sys.version_info.major < 3 or sys.version_info.minor < 3:
2655 sys.exit("Error: clinic.py requires Python 3.3 or greater.")
2656
2657 import argparse
2658 cmdline = argparse.ArgumentParser()
2659 cmdline.add_argument("-f", "--force", action='store_true')
2660 cmdline.add_argument("-o", "--output", type=str)
2661 cmdline.add_argument("--converters", action='store_true')
Larry Hastingsdcd340e2013-11-23 14:58:45 -08002662 cmdline.add_argument("--make", action='store_true')
Larry Hastings31826802013-10-19 00:09:25 -07002663 cmdline.add_argument("filename", type=str, nargs="*")
2664 ns = cmdline.parse_args(argv)
2665
2666 if ns.converters:
2667 if ns.filename:
2668 print("Usage error: can't specify --converters and a filename at the same time.")
2669 print()
2670 cmdline.print_usage()
2671 sys.exit(-1)
2672 converters = []
2673 return_converters = []
2674 ignored = set("""
2675 add_c_converter
2676 add_c_return_converter
2677 add_default_legacy_c_converter
2678 add_legacy_c_converter
2679 """.strip().split())
2680 module = globals()
2681 for name in module:
2682 for suffix, ids in (
2683 ("_return_converter", return_converters),
2684 ("_converter", converters),
2685 ):
2686 if name in ignored:
2687 continue
2688 if name.endswith(suffix):
2689 ids.append((name, name[:-len(suffix)]))
2690 break
2691 print()
2692
2693 print("Legacy converters:")
2694 legacy = sorted(legacy_converters)
2695 print(' ' + ' '.join(c for c in legacy if c[0].isupper()))
2696 print(' ' + ' '.join(c for c in legacy if c[0].islower()))
2697 print()
2698
2699 for title, attribute, ids in (
2700 ("Converters", 'converter_init', converters),
2701 ("Return converters", 'return_converter_init', return_converters),
2702 ):
2703 print(title + ":")
2704 longest = -1
2705 for name, short_name in ids:
2706 longest = max(longest, len(short_name))
2707 for name, short_name in sorted(ids, key=lambda x: x[1].lower()):
2708 cls = module[name]
2709 callable = getattr(cls, attribute, None)
2710 if not callable:
2711 continue
2712 signature = inspect.signature(callable)
2713 parameters = []
2714 for parameter_name, parameter in signature.parameters.items():
2715 if parameter.kind == inspect.Parameter.KEYWORD_ONLY:
2716 if parameter.default != inspect.Parameter.empty:
2717 s = '{}={!r}'.format(parameter_name, parameter.default)
2718 else:
2719 s = parameter_name
2720 parameters.append(s)
2721 print(' {}({})'.format(short_name, ', '.join(parameters)))
2722 # add_comma = False
2723 # for parameter_name, parameter in signature.parameters.items():
2724 # if parameter.kind == inspect.Parameter.KEYWORD_ONLY:
2725 # if add_comma:
2726 # parameters.append(', ')
2727 # else:
2728 # add_comma = True
2729 # s = parameter_name
2730 # if parameter.default != inspect.Parameter.empty:
2731 # s += '=' + repr(parameter.default)
2732 # parameters.append(s)
2733 # parameters.append(')')
2734
2735 # print(" ", short_name + "".join(parameters))
2736 print()
Larry Hastings78cf85c2014-01-04 12:44:57 -08002737 print("All converters also accept (doc_default=None, required=False, annotation=None).")
Larry Hastings31826802013-10-19 00:09:25 -07002738 print("All return converters also accept (doc_default=None).")
2739 sys.exit(0)
2740
Larry Hastingsdcd340e2013-11-23 14:58:45 -08002741 if ns.make:
2742 if ns.output or ns.filename:
2743 print("Usage error: can't use -o or filenames with --make.")
2744 print()
2745 cmdline.print_usage()
2746 sys.exit(-1)
2747 for root, dirs, files in os.walk('.'):
2748 for rcs_dir in ('.svn', '.git', '.hg'):
2749 if rcs_dir in dirs:
2750 dirs.remove(rcs_dir)
2751 for filename in files:
2752 if not filename.endswith('.c'):
2753 continue
2754 path = os.path.join(root, filename)
2755 parse_file(path, verify=not ns.force)
2756 return
2757
Larry Hastings31826802013-10-19 00:09:25 -07002758 if not ns.filename:
2759 cmdline.print_usage()
2760 sys.exit(-1)
2761
2762 if ns.output and len(ns.filename) > 1:
2763 print("Usage error: can't use -o with multiple filenames.")
2764 print()
2765 cmdline.print_usage()
2766 sys.exit(-1)
2767
2768 for filename in ns.filename:
2769 parse_file(filename, output=ns.output, verify=not ns.force)
2770
2771
2772if __name__ == "__main__":
2773 sys.exit(main(sys.argv[1:]))