ยปCore Development>Code coverage>Tools/clinic/clinic.py

Python code coverage for Tools/clinic/clinic.py

#countcontent
1n/a#!/usr/bin/env python3
2n/a#
3n/a# Argument Clinic
4n/a# Copyright 2012-2013 by Larry Hastings.
5n/a# Licensed to the PSF under a contributor agreement.
6n/a#
7n/a
8n/aimport abc
9n/aimport ast
10n/aimport atexit
11n/aimport collections
12n/aimport contextlib
13n/aimport copy
14n/aimport cpp
15n/aimport functools
16n/aimport hashlib
17n/aimport inspect
18n/aimport io
19n/aimport itertools
20n/aimport os
21n/aimport pprint
22n/aimport re
23n/aimport shlex
24n/aimport string
25n/aimport sys
26n/aimport tempfile
27n/aimport textwrap
28n/aimport traceback
29n/aimport types
30n/aimport uuid
31n/a
32n/afrom types import *
33n/aNoneType = type(None)
34n/a
35n/a# TODO:
36n/a#
37n/a# soon:
38n/a#
39n/a# * allow mixing any two of {positional-only, positional-or-keyword,
40n/a# keyword-only}
41n/a# * dict constructor uses positional-only and keyword-only
42n/a# * max and min use positional only with an optional group
43n/a# and keyword-only
44n/a#
45n/a
46n/aversion = '1'
47n/a
48n/a_empty = inspect._empty
49n/a_void = inspect._void
50n/a
51n/aNoneType = type(None)
52n/a
53n/aclass Unspecified:
54n/a def __repr__(self):
55n/a return '<Unspecified>'
56n/a
57n/aunspecified = Unspecified()
58n/a
59n/a
60n/aclass Null:
61n/a def __repr__(self):
62n/a return '<Null>'
63n/a
64n/aNULL = Null()
65n/a
66n/a
67n/aclass Unknown:
68n/a def __repr__(self):
69n/a return '<Unknown>'
70n/a
71n/aunknown = Unknown()
72n/a
73n/asig_end_marker = '--'
74n/a
75n/a
76n/a_text_accumulator_nt = collections.namedtuple("_text_accumulator", "text append output")
77n/a
78n/adef _text_accumulator():
79n/a text = []
80n/a def output():
81n/a s = ''.join(text)
82n/a text.clear()
83n/a return s
84n/a return _text_accumulator_nt(text, text.append, output)
85n/a
86n/a
87n/atext_accumulator_nt = collections.namedtuple("text_accumulator", "text append")
88n/a
89n/adef text_accumulator():
90n/a """
91n/a Creates a simple text accumulator / joiner.
92n/a
93n/a Returns a pair of callables:
94n/a append, output
95n/a "append" appends a string to the accumulator.
96n/a "output" returns the contents of the accumulator
97n/a joined together (''.join(accumulator)) and
98n/a empties the accumulator.
99n/a """
100n/a text, append, output = _text_accumulator()
101n/a return text_accumulator_nt(append, output)
102n/a
103n/a
104n/adef warn_or_fail(fail=False, *args, filename=None, line_number=None):
105n/a joined = " ".join([str(a) for a in args])
106n/a add, output = text_accumulator()
107n/a if fail:
108n/a add("Error")
109n/a else:
110n/a add("Warning")
111n/a if clinic:
112n/a if filename is None:
113n/a filename = clinic.filename
114n/a if getattr(clinic, 'block_parser', None) and (line_number is None):
115n/a line_number = clinic.block_parser.line_number
116n/a if filename is not None:
117n/a add(' in file "' + filename + '"')
118n/a if line_number is not None:
119n/a add(" on line " + str(line_number))
120n/a add(':\n')
121n/a add(joined)
122n/a print(output())
123n/a if fail:
124n/a sys.exit(-1)
125n/a
126n/a
127n/adef warn(*args, filename=None, line_number=None):
128n/a return warn_or_fail(False, *args, filename=filename, line_number=line_number)
129n/a
130n/adef fail(*args, filename=None, line_number=None):
131n/a return warn_or_fail(True, *args, filename=filename, line_number=line_number)
132n/a
133n/a
134n/adef quoted_for_c_string(s):
135n/a for old, new in (
136n/a ('\\', '\\\\'), # must be first!
137n/a ('"', '\\"'),
138n/a ("'", "\\'"),
139n/a ):
140n/a s = s.replace(old, new)
141n/a return s
142n/a
143n/adef c_repr(s):
144n/a return '"' + s + '"'
145n/a
146n/a
147n/ais_legal_c_identifier = re.compile('^[A-Za-z_][A-Za-z0-9_]*$').match
148n/a
149n/adef is_legal_py_identifier(s):
150n/a return all(is_legal_c_identifier(field) for field in s.split('.'))
151n/a
152n/a# identifiers that are okay in Python but aren't a good idea in C.
153n/a# so if they're used Argument Clinic will add "_value" to the end
154n/a# of the name in C.
155n/ac_keywords = set("""
156n/aasm auto break case char const continue default do double
157n/aelse enum extern float for goto if inline int long
158n/aregister return short signed sizeof static struct switch
159n/atypedef typeof union unsigned void volatile while
160n/a""".strip().split())
161n/a
162n/adef ensure_legal_c_identifier(s):
163n/a # for now, just complain if what we're given isn't legal
164n/a if not is_legal_c_identifier(s):
165n/a fail("Illegal C identifier: {}".format(s))
166n/a # but if we picked a C keyword, pick something else
167n/a if s in c_keywords:
168n/a return s + "_value"
169n/a return s
170n/a
171n/adef rstrip_lines(s):
172n/a text, add, output = _text_accumulator()
173n/a for line in s.split('\n'):
174n/a add(line.rstrip())
175n/a add('\n')
176n/a text.pop()
177n/a return output()
178n/a
179n/adef format_escape(s):
180n/a # double up curly-braces, this string will be used
181n/a # as part of a format_map() template later
182n/a s = s.replace('{', '{{')
183n/a s = s.replace('}', '}}')
184n/a return s
185n/a
186n/adef linear_format(s, **kwargs):
187n/a """
188n/a Perform str.format-like substitution, except:
189n/a * The strings substituted must be on lines by
190n/a themselves. (This line is the "source line".)
191n/a * If the substitution text is empty, the source line
192n/a is removed in the output.
193n/a * If the field is not recognized, the original line
194n/a is passed unmodified through to the output.
195n/a * If the substitution text is not empty:
196n/a * Each line of the substituted text is indented
197n/a by the indent of the source line.
198n/a * A newline will be added to the end.
199n/a """
200n/a
201n/a add, output = text_accumulator()
202n/a for line in s.split('\n'):
203n/a indent, curly, trailing = line.partition('{')
204n/a if not curly:
205n/a add(line)
206n/a add('\n')
207n/a continue
208n/a
209n/a name, curly, trailing = trailing.partition('}')
210n/a if not curly or name not in kwargs:
211n/a add(line)
212n/a add('\n')
213n/a continue
214n/a
215n/a if trailing:
216n/a fail("Text found after {" + name + "} block marker! It must be on a line by itself.")
217n/a if indent.strip():
218n/a fail("Non-whitespace characters found before {" + name + "} block marker! It must be on a line by itself.")
219n/a
220n/a value = kwargs[name]
221n/a if not value:
222n/a continue
223n/a
224n/a value = textwrap.indent(rstrip_lines(value), indent)
225n/a add(value)
226n/a add('\n')
227n/a
228n/a return output()[:-1]
229n/a
230n/adef indent_all_lines(s, prefix):
231n/a """
232n/a Returns 's', with 'prefix' prepended to all lines.
233n/a
234n/a If the last line is empty, prefix is not prepended
235n/a to it. (If s is blank, returns s unchanged.)
236n/a
237n/a (textwrap.indent only adds to non-blank lines.)
238n/a """
239n/a split = s.split('\n')
240n/a last = split.pop()
241n/a final = []
242n/a for line in split:
243n/a final.append(prefix)
244n/a final.append(line)
245n/a final.append('\n')
246n/a if last:
247n/a final.append(prefix)
248n/a final.append(last)
249n/a return ''.join(final)
250n/a
251n/adef suffix_all_lines(s, suffix):
252n/a """
253n/a Returns 's', with 'suffix' appended to all lines.
254n/a
255n/a If the last line is empty, suffix is not appended
256n/a to it. (If s is blank, returns s unchanged.)
257n/a """
258n/a split = s.split('\n')
259n/a last = split.pop()
260n/a final = []
261n/a for line in split:
262n/a final.append(line)
263n/a final.append(suffix)
264n/a final.append('\n')
265n/a if last:
266n/a final.append(last)
267n/a final.append(suffix)
268n/a return ''.join(final)
269n/a
270n/a
271n/adef version_splitter(s):
272n/a """Splits a version string into a tuple of integers.
273n/a
274n/a The following ASCII characters are allowed, and employ
275n/a the following conversions:
276n/a a -> -3
277n/a b -> -2
278n/a c -> -1
279n/a (This permits Python-style version strings such as "1.4b3".)
280n/a """
281n/a version = []
282n/a accumulator = []
283n/a def flush():
284n/a if not accumulator:
285n/a raise ValueError('Unsupported version string: ' + repr(s))
286n/a version.append(int(''.join(accumulator)))
287n/a accumulator.clear()
288n/a
289n/a for c in s:
290n/a if c.isdigit():
291n/a accumulator.append(c)
292n/a elif c == '.':
293n/a flush()
294n/a elif c in 'abc':
295n/a flush()
296n/a version.append('abc'.index(c) - 3)
297n/a else:
298n/a raise ValueError('Illegal character ' + repr(c) + ' in version string ' + repr(s))
299n/a flush()
300n/a return tuple(version)
301n/a
302n/adef version_comparitor(version1, version2):
303n/a iterator = itertools.zip_longest(version_splitter(version1), version_splitter(version2), fillvalue=0)
304n/a for i, (a, b) in enumerate(iterator):
305n/a if a < b:
306n/a return -1
307n/a if a > b:
308n/a return 1
309n/a return 0
310n/a
311n/a
312n/aclass CRenderData:
313n/a def __init__(self):
314n/a
315n/a # The C statements to declare variables.
316n/a # Should be full lines with \n eol characters.
317n/a self.declarations = []
318n/a
319n/a # The C statements required to initialize the variables before the parse call.
320n/a # Should be full lines with \n eol characters.
321n/a self.initializers = []
322n/a
323n/a # The C statements needed to dynamically modify the values
324n/a # parsed by the parse call, before calling the impl.
325n/a self.modifications = []
326n/a
327n/a # The entries for the "keywords" array for PyArg_ParseTuple.
328n/a # Should be individual strings representing the names.
329n/a self.keywords = []
330n/a
331n/a # The "format units" for PyArg_ParseTuple.
332n/a # Should be individual strings that will get
333n/a self.format_units = []
334n/a
335n/a # The varargs arguments for PyArg_ParseTuple.
336n/a self.parse_arguments = []
337n/a
338n/a # The parameter declarations for the impl function.
339n/a self.impl_parameters = []
340n/a
341n/a # The arguments to the impl function at the time it's called.
342n/a self.impl_arguments = []
343n/a
344n/a # For return converters: the name of the variable that
345n/a # should receive the value returned by the impl.
346n/a self.return_value = "return_value"
347n/a
348n/a # For return converters: the code to convert the return
349n/a # value from the parse function. This is also where
350n/a # you should check the _return_value for errors, and
351n/a # "goto exit" if there are any.
352n/a self.return_conversion = []
353n/a
354n/a # The C statements required to clean up after the impl call.
355n/a self.cleanup = []
356n/a
357n/a
358n/aclass FormatCounterFormatter(string.Formatter):
359n/a """
360n/a This counts how many instances of each formatter
361n/a "replacement string" appear in the format string.
362n/a
363n/a e.g. after evaluating "string {a}, {b}, {c}, {a}"
364n/a the counts dict would now look like
365n/a {'a': 2, 'b': 1, 'c': 1}
366n/a """
367n/a def __init__(self):
368n/a self.counts = collections.Counter()
369n/a
370n/a def get_value(self, key, args, kwargs):
371n/a self.counts[key] += 1
372n/a return ''
373n/a
374n/aclass Language(metaclass=abc.ABCMeta):
375n/a
376n/a start_line = ""
377n/a body_prefix = ""
378n/a stop_line = ""
379n/a checksum_line = ""
380n/a
381n/a def __init__(self, filename):
382n/a pass
383n/a
384n/a @abc.abstractmethod
385n/a def render(self, clinic, signatures):
386n/a pass
387n/a
388n/a def parse_line(self, line):
389n/a pass
390n/a
391n/a def validate(self):
392n/a def assert_only_one(attr, *additional_fields):
393n/a """
394n/a Ensures that the string found at getattr(self, attr)
395n/a contains exactly one formatter replacement string for
396n/a each valid field. The list of valid fields is
397n/a ['dsl_name'] extended by additional_fields.
398n/a
399n/a e.g.
400n/a self.fmt = "{dsl_name} {a} {b}"
401n/a
402n/a # this passes
403n/a self.assert_only_one('fmt', 'a', 'b')
404n/a
405n/a # this fails, the format string has a {b} in it
406n/a self.assert_only_one('fmt', 'a')
407n/a
408n/a # this fails, the format string doesn't have a {c} in it
409n/a self.assert_only_one('fmt', 'a', 'b', 'c')
410n/a
411n/a # this fails, the format string has two {a}s in it,
412n/a # it must contain exactly one
413n/a self.fmt2 = '{dsl_name} {a} {a}'
414n/a self.assert_only_one('fmt2', 'a')
415n/a
416n/a """
417n/a fields = ['dsl_name']
418n/a fields.extend(additional_fields)
419n/a line = getattr(self, attr)
420n/a fcf = FormatCounterFormatter()
421n/a fcf.format(line)
422n/a def local_fail(should_be_there_but_isnt):
423n/a if should_be_there_but_isnt:
424n/a fail("{} {} must contain {{{}}} exactly once!".format(
425n/a self.__class__.__name__, attr, name))
426n/a else:
427n/a fail("{} {} must not contain {{{}}}!".format(
428n/a self.__class__.__name__, attr, name))
429n/a
430n/a for name, count in fcf.counts.items():
431n/a if name in fields:
432n/a if count > 1:
433n/a local_fail(True)
434n/a else:
435n/a local_fail(False)
436n/a for name in fields:
437n/a if fcf.counts.get(name) != 1:
438n/a local_fail(True)
439n/a
440n/a assert_only_one('start_line')
441n/a assert_only_one('stop_line')
442n/a
443n/a field = "arguments" if "{arguments}" in self.checksum_line else "checksum"
444n/a assert_only_one('checksum_line', field)
445n/a
446n/a
447n/a
448n/aclass PythonLanguage(Language):
449n/a
450n/a language = 'Python'
451n/a start_line = "#/*[{dsl_name} input]"
452n/a body_prefix = "#"
453n/a stop_line = "#[{dsl_name} start generated code]*/"
454n/a checksum_line = "#/*[{dsl_name} end generated code: {arguments}]*/"
455n/a
456n/a
457n/adef permute_left_option_groups(l):
458n/a """
459n/a Given [1, 2, 3], should yield:
460n/a ()
461n/a (3,)
462n/a (2, 3)
463n/a (1, 2, 3)
464n/a """
465n/a yield tuple()
466n/a accumulator = []
467n/a for group in reversed(l):
468n/a accumulator = list(group) + accumulator
469n/a yield tuple(accumulator)
470n/a
471n/a
472n/adef permute_right_option_groups(l):
473n/a """
474n/a Given [1, 2, 3], should yield:
475n/a ()
476n/a (1,)
477n/a (1, 2)
478n/a (1, 2, 3)
479n/a """
480n/a yield tuple()
481n/a accumulator = []
482n/a for group in l:
483n/a accumulator.extend(group)
484n/a yield tuple(accumulator)
485n/a
486n/a
487n/adef permute_optional_groups(left, required, right):
488n/a """
489n/a Generator function that computes the set of acceptable
490n/a argument lists for the provided iterables of
491n/a argument groups. (Actually it generates a tuple of tuples.)
492n/a
493n/a Algorithm: prefer left options over right options.
494n/a
495n/a If required is empty, left must also be empty.
496n/a """
497n/a required = tuple(required)
498n/a result = []
499n/a
500n/a if not required:
501n/a assert not left
502n/a
503n/a accumulator = []
504n/a counts = set()
505n/a for r in permute_right_option_groups(right):
506n/a for l in permute_left_option_groups(left):
507n/a t = l + required + r
508n/a if len(t) in counts:
509n/a continue
510n/a counts.add(len(t))
511n/a accumulator.append(t)
512n/a
513n/a accumulator.sort(key=len)
514n/a return tuple(accumulator)
515n/a
516n/a
517n/adef strip_leading_and_trailing_blank_lines(s):
518n/a lines = s.rstrip().split('\n')
519n/a while lines:
520n/a line = lines[0]
521n/a if line.strip():
522n/a break
523n/a del lines[0]
524n/a return '\n'.join(lines)
525n/a
526n/a@functools.lru_cache()
527n/adef normalize_snippet(s, *, indent=0):
528n/a """
529n/a Reformats s:
530n/a * removes leading and trailing blank lines
531n/a * ensures that it does not end with a newline
532n/a * dedents so the first nonwhite character on any line is at column "indent"
533n/a """
534n/a s = strip_leading_and_trailing_blank_lines(s)
535n/a s = textwrap.dedent(s)
536n/a if indent:
537n/a s = textwrap.indent(s, ' ' * indent)
538n/a return s
539n/a
540n/a
541n/adef wrap_declarations(text, length=78):
542n/a """
543n/a A simple-minded text wrapper for C function declarations.
544n/a
545n/a It views a declaration line as looking like this:
546n/a xxxxxxxx(xxxxxxxxx,xxxxxxxxx)
547n/a If called with length=30, it would wrap that line into
548n/a xxxxxxxx(xxxxxxxxx,
549n/a xxxxxxxxx)
550n/a (If the declaration has zero or one parameters, this
551n/a function won't wrap it.)
552n/a
553n/a If this doesn't work properly, it's probably better to
554n/a start from scratch with a more sophisticated algorithm,
555n/a rather than try and improve/debug this dumb little function.
556n/a """
557n/a lines = []
558n/a for line in text.split('\n'):
559n/a prefix, _, after_l_paren = line.partition('(')
560n/a if not after_l_paren:
561n/a lines.append(line)
562n/a continue
563n/a parameters, _, after_r_paren = after_l_paren.partition(')')
564n/a if not _:
565n/a lines.append(line)
566n/a continue
567n/a if ',' not in parameters:
568n/a lines.append(line)
569n/a continue
570n/a parameters = [x.strip() + ", " for x in parameters.split(',')]
571n/a prefix += "("
572n/a if len(prefix) < length:
573n/a spaces = " " * len(prefix)
574n/a else:
575n/a spaces = " " * 4
576n/a
577n/a while parameters:
578n/a line = prefix
579n/a first = True
580n/a while parameters:
581n/a if (not first and
582n/a (len(line) + len(parameters[0]) > length)):
583n/a break
584n/a line += parameters.pop(0)
585n/a first = False
586n/a if not parameters:
587n/a line = line.rstrip(", ") + ")" + after_r_paren
588n/a lines.append(line.rstrip())
589n/a prefix = spaces
590n/a return "\n".join(lines)
591n/a
592n/a
593n/aclass CLanguage(Language):
594n/a
595n/a body_prefix = "#"
596n/a language = 'C'
597n/a start_line = "/*[{dsl_name} input]"
598n/a body_prefix = ""
599n/a stop_line = "[{dsl_name} start generated code]*/"
600n/a checksum_line = "/*[{dsl_name} end generated code: {arguments}]*/"
601n/a
602n/a def __init__(self, filename):
603n/a super().__init__(filename)
604n/a self.cpp = cpp.Monitor(filename)
605n/a self.cpp.fail = fail
606n/a
607n/a def parse_line(self, line):
608n/a self.cpp.writeline(line)
609n/a
610n/a def render(self, clinic, signatures):
611n/a function = None
612n/a for o in signatures:
613n/a if isinstance(o, Function):
614n/a if function:
615n/a fail("You may specify at most one function per block.\nFound a block containing at least two:\n\t" + repr(function) + " and " + repr(o))
616n/a function = o
617n/a return self.render_function(clinic, function)
618n/a
619n/a def docstring_for_c_string(self, f):
620n/a text, add, output = _text_accumulator()
621n/a # turn docstring into a properly quoted C string
622n/a for line in f.docstring.split('\n'):
623n/a add('"')
624n/a add(quoted_for_c_string(line))
625n/a add('\\n"\n')
626n/a
627n/a if text[-2] == sig_end_marker:
628n/a # If we only have a signature, add the blank line that the
629n/a # __text_signature__ getter expects to be there.
630n/a add('"\\n"')
631n/a else:
632n/a text.pop()
633n/a add('"')
634n/a return ''.join(text)
635n/a
636n/a def output_templates(self, f):
637n/a parameters = list(f.parameters.values())
638n/a assert parameters
639n/a assert isinstance(parameters[0].converter, self_converter)
640n/a del parameters[0]
641n/a converters = [p.converter for p in parameters]
642n/a
643n/a has_option_groups = parameters and (parameters[0].group or parameters[-1].group)
644n/a default_return_converter = (not f.return_converter or
645n/a f.return_converter.type == 'PyObject *')
646n/a
647n/a positional = parameters and parameters[-1].is_positional_only()
648n/a all_boring_objects = False # yes, this will be false if there are 0 parameters, it's fine
649n/a first_optional = len(parameters)
650n/a for i, p in enumerate(parameters):
651n/a c = p.converter
652n/a if type(c) != object_converter:
653n/a break
654n/a if c.format_unit != 'O':
655n/a break
656n/a if p.default is not unspecified:
657n/a first_optional = min(first_optional, i)
658n/a else:
659n/a all_boring_objects = True
660n/a
661n/a new_or_init = f.kind in (METHOD_NEW, METHOD_INIT)
662n/a
663n/a meth_o = (len(parameters) == 1 and
664n/a parameters[0].is_positional_only() and
665n/a not converters[0].is_optional() and
666n/a not new_or_init)
667n/a
668n/a # we have to set these things before we're done:
669n/a #
670n/a # docstring_prototype
671n/a # docstring_definition
672n/a # impl_prototype
673n/a # methoddef_define
674n/a # parser_prototype
675n/a # parser_definition
676n/a # impl_definition
677n/a # cpp_if
678n/a # cpp_endif
679n/a # methoddef_ifndef
680n/a
681n/a return_value_declaration = "PyObject *return_value = NULL;"
682n/a
683n/a methoddef_define = normalize_snippet("""
684n/a #define {methoddef_name} \\
685n/a {{"{name}", (PyCFunction){c_basename}, {methoddef_flags}, {c_basename}__doc__}},
686n/a """)
687n/a if new_or_init and not f.docstring:
688n/a docstring_prototype = docstring_definition = ''
689n/a else:
690n/a docstring_prototype = normalize_snippet("""
691n/a PyDoc_VAR({c_basename}__doc__);
692n/a """)
693n/a docstring_definition = normalize_snippet("""
694n/a PyDoc_STRVAR({c_basename}__doc__,
695n/a {docstring});
696n/a """)
697n/a impl_definition = normalize_snippet("""
698n/a static {impl_return_type}
699n/a {c_basename}_impl({impl_parameters})
700n/a """)
701n/a impl_prototype = parser_prototype = parser_definition = None
702n/a
703n/a parser_prototype_keyword = normalize_snippet("""
704n/a static PyObject *
705n/a {c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
706n/a """)
707n/a
708n/a parser_prototype_varargs = normalize_snippet("""
709n/a static PyObject *
710n/a {c_basename}({self_type}{self_name}, PyObject *args)
711n/a """)
712n/a
713n/a parser_prototype_fastcall = normalize_snippet("""
714n/a static PyObject *
715n/a {c_basename}({self_type}{self_name}, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
716n/a """)
717n/a
718n/a # parser_body_fields remembers the fields passed in to the
719n/a # previous call to parser_body. this is used for an awful hack.
720n/a parser_body_fields = ()
721n/a def parser_body(prototype, *fields):
722n/a nonlocal parser_body_fields
723n/a add, output = text_accumulator()
724n/a add(prototype)
725n/a parser_body_fields = fields
726n/a
727n/a fields = list(fields)
728n/a fields.insert(0, normalize_snippet("""
729n/a {{
730n/a {return_value_declaration}
731n/a {declarations}
732n/a {initializers}
733n/a """) + "\n")
734n/a # just imagine--your code is here in the middle
735n/a fields.append(normalize_snippet("""
736n/a {modifications}
737n/a {return_value} = {c_basename}_impl({impl_arguments});
738n/a {return_conversion}
739n/a
740n/a {exit_label}
741n/a {cleanup}
742n/a return return_value;
743n/a }}
744n/a """))
745n/a for field in fields:
746n/a add('\n')
747n/a add(field)
748n/a return output()
749n/a
750n/a def insert_keywords(s):
751n/a return linear_format(s, declarations=
752n/a 'static const char * const _keywords[] = {{{keywords}, NULL}};\n'
753n/a 'static _PyArg_Parser _parser = {{"{format_units}:{name}", _keywords, 0}};\n'
754n/a '{declarations}')
755n/a
756n/a if not parameters:
757n/a # no parameters, METH_NOARGS
758n/a
759n/a flags = "METH_NOARGS"
760n/a
761n/a parser_prototype = normalize_snippet("""
762n/a static PyObject *
763n/a {c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored))
764n/a """)
765n/a parser_definition = parser_prototype
766n/a
767n/a if default_return_converter:
768n/a parser_definition = parser_prototype + '\n' + normalize_snippet("""
769n/a {{
770n/a return {c_basename}_impl({impl_arguments});
771n/a }}
772n/a """)
773n/a else:
774n/a parser_definition = parser_body(parser_prototype)
775n/a
776n/a elif meth_o:
777n/a flags = "METH_O"
778n/a
779n/a if (isinstance(converters[0], object_converter) and
780n/a converters[0].format_unit == 'O'):
781n/a meth_o_prototype = normalize_snippet("""
782n/a static PyObject *
783n/a {c_basename}({impl_parameters})
784n/a """)
785n/a
786n/a if default_return_converter:
787n/a # maps perfectly to METH_O, doesn't need a return converter.
788n/a # so we skip making a parse function
789n/a # and call directly into the impl function.
790n/a impl_prototype = parser_prototype = parser_definition = ''
791n/a impl_definition = meth_o_prototype
792n/a else:
793n/a # SLIGHT HACK
794n/a # use impl_parameters for the parser here!
795n/a parser_prototype = meth_o_prototype
796n/a parser_definition = parser_body(parser_prototype)
797n/a
798n/a else:
799n/a argname = 'arg'
800n/a if parameters[0].name == argname:
801n/a argname += '_'
802n/a parser_prototype = normalize_snippet("""
803n/a static PyObject *
804n/a {c_basename}({self_type}{self_name}, PyObject *%s)
805n/a """ % argname)
806n/a
807n/a parser_definition = parser_body(parser_prototype, normalize_snippet("""
808n/a if (!PyArg_Parse(%s, "{format_units}:{name}", {parse_arguments})) {{
809n/a goto exit;
810n/a }}
811n/a """ % argname, indent=4))
812n/a
813n/a elif has_option_groups:
814n/a # positional parameters with option groups
815n/a # (we have to generate lots of PyArg_ParseTuple calls
816n/a # in a big switch statement)
817n/a
818n/a flags = "METH_VARARGS"
819n/a parser_prototype = parser_prototype_varargs
820n/a
821n/a parser_definition = parser_body(parser_prototype, ' {option_group_parsing}')
822n/a
823n/a elif positional and all_boring_objects:
824n/a # positional-only, but no option groups,
825n/a # and nothing but normal objects:
826n/a # PyArg_UnpackTuple!
827n/a
828n/a if not new_or_init:
829n/a flags = "METH_FASTCALL"
830n/a parser_prototype = parser_prototype_fastcall
831n/a
832n/a parser_definition = parser_body(parser_prototype, normalize_snippet("""
833n/a if (!_PyArg_UnpackStack(args, nargs, "{name}",
834n/a {unpack_min}, {unpack_max},
835n/a {parse_arguments})) {{
836n/a goto exit;
837n/a }}
838n/a
839n/a if ({self_type_check}!_PyArg_NoStackKeywords("{name}", kwnames)) {{
840n/a goto exit;
841n/a }}
842n/a """, indent=4))
843n/a else:
844n/a flags = "METH_VARARGS"
845n/a parser_prototype = parser_prototype_varargs
846n/a
847n/a parser_definition = parser_body(parser_prototype, normalize_snippet("""
848n/a if (!PyArg_UnpackTuple(args, "{name}",
849n/a {unpack_min}, {unpack_max},
850n/a {parse_arguments})) {{
851n/a goto exit;
852n/a }}
853n/a """, indent=4))
854n/a
855n/a elif positional:
856n/a if not new_or_init:
857n/a # positional-only, but no option groups
858n/a # we only need one call to _PyArg_ParseStack
859n/a
860n/a flags = "METH_FASTCALL"
861n/a parser_prototype = parser_prototype_fastcall
862n/a
863n/a parser_definition = parser_body(parser_prototype, normalize_snippet("""
864n/a if (!_PyArg_ParseStack(args, nargs, "{format_units}:{name}",
865n/a {parse_arguments})) {{
866n/a goto exit;
867n/a }}
868n/a
869n/a if ({self_type_check}!_PyArg_NoStackKeywords("{name}", kwnames)) {{
870n/a goto exit;
871n/a }}
872n/a """, indent=4))
873n/a else:
874n/a # positional-only, but no option groups
875n/a # we only need one call to PyArg_ParseTuple
876n/a
877n/a flags = "METH_VARARGS"
878n/a parser_prototype = parser_prototype_varargs
879n/a
880n/a parser_definition = parser_body(parser_prototype, normalize_snippet("""
881n/a if (!PyArg_ParseTuple(args, "{format_units}:{name}",
882n/a {parse_arguments})) {{
883n/a goto exit;
884n/a }}
885n/a """, indent=4))
886n/a
887n/a elif not new_or_init:
888n/a flags = "METH_FASTCALL"
889n/a
890n/a parser_prototype = parser_prototype_fastcall
891n/a
892n/a body = normalize_snippet("""
893n/a if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
894n/a {parse_arguments})) {{
895n/a goto exit;
896n/a }}
897n/a """, indent=4)
898n/a parser_definition = parser_body(parser_prototype, body)
899n/a parser_definition = insert_keywords(parser_definition)
900n/a else:
901n/a # positional-or-keyword arguments
902n/a flags = "METH_VARARGS|METH_KEYWORDS"
903n/a
904n/a parser_prototype = parser_prototype_keyword
905n/a
906n/a body = normalize_snippet("""
907n/a if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser,
908n/a {parse_arguments})) {{
909n/a goto exit;
910n/a }}
911n/a """, indent=4)
912n/a parser_definition = parser_body(parser_prototype, body)
913n/a parser_definition = insert_keywords(parser_definition)
914n/a
915n/a
916n/a if new_or_init:
917n/a methoddef_define = ''
918n/a
919n/a if f.kind == METHOD_NEW:
920n/a parser_prototype = parser_prototype_keyword
921n/a else:
922n/a return_value_declaration = "int return_value = -1;"
923n/a parser_prototype = normalize_snippet("""
924n/a static int
925n/a {c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
926n/a """)
927n/a
928n/a fields = list(parser_body_fields)
929n/a parses_positional = 'METH_NOARGS' not in flags
930n/a parses_keywords = 'METH_KEYWORDS' in flags
931n/a if parses_keywords:
932n/a assert parses_positional
933n/a
934n/a if not parses_keywords:
935n/a fields.insert(0, normalize_snippet("""
936n/a if ({self_type_check}!_PyArg_NoKeywords("{name}", kwargs)) {{
937n/a goto exit;
938n/a }}
939n/a """, indent=4))
940n/a if not parses_positional:
941n/a fields.insert(0, normalize_snippet("""
942n/a if ({self_type_check}!_PyArg_NoPositional("{name}", args)) {{
943n/a goto exit;
944n/a }}
945n/a """, indent=4))
946n/a
947n/a parser_definition = parser_body(parser_prototype, *fields)
948n/a if parses_keywords:
949n/a parser_definition = insert_keywords(parser_definition)
950n/a
951n/a
952n/a if f.methoddef_flags:
953n/a flags += '|' + f.methoddef_flags
954n/a
955n/a methoddef_define = methoddef_define.replace('{methoddef_flags}', flags)
956n/a
957n/a methoddef_ifndef = ''
958n/a conditional = self.cpp.condition()
959n/a if not conditional:
960n/a cpp_if = cpp_endif = ''
961n/a else:
962n/a cpp_if = "#if " + conditional
963n/a cpp_endif = "#endif /* " + conditional + " */"
964n/a
965n/a if methoddef_define and f.name not in clinic.ifndef_symbols:
966n/a clinic.ifndef_symbols.add(f.name)
967n/a methoddef_ifndef = normalize_snippet("""
968n/a #ifndef {methoddef_name}
969n/a #define {methoddef_name}
970n/a #endif /* !defined({methoddef_name}) */
971n/a """)
972n/a
973n/a
974n/a # add ';' to the end of parser_prototype and impl_prototype
975n/a # (they mustn't be None, but they could be an empty string.)
976n/a assert parser_prototype is not None
977n/a if parser_prototype:
978n/a assert not parser_prototype.endswith(';')
979n/a parser_prototype += ';'
980n/a
981n/a if impl_prototype is None:
982n/a impl_prototype = impl_definition
983n/a if impl_prototype:
984n/a impl_prototype += ";"
985n/a
986n/a parser_definition = parser_definition.replace("{return_value_declaration}", return_value_declaration)
987n/a
988n/a d = {
989n/a "docstring_prototype" : docstring_prototype,
990n/a "docstring_definition" : docstring_definition,
991n/a "impl_prototype" : impl_prototype,
992n/a "methoddef_define" : methoddef_define,
993n/a "parser_prototype" : parser_prototype,
994n/a "parser_definition" : parser_definition,
995n/a "impl_definition" : impl_definition,
996n/a "cpp_if" : cpp_if,
997n/a "cpp_endif" : cpp_endif,
998n/a "methoddef_ifndef" : methoddef_ifndef,
999n/a }
1000n/a
1001n/a # make sure we didn't forget to assign something,
1002n/a # and wrap each non-empty value in \n's
1003n/a d2 = {}
1004n/a for name, value in d.items():
1005n/a assert value is not None, "got a None value for template " + repr(name)
1006n/a if value:
1007n/a value = '\n' + value + '\n'
1008n/a d2[name] = value
1009n/a return d2
1010n/a
1011n/a @staticmethod
1012n/a def group_to_variable_name(group):
1013n/a adjective = "left_" if group < 0 else "right_"
1014n/a return "group_" + adjective + str(abs(group))
1015n/a
1016n/a def render_option_group_parsing(self, f, template_dict):
1017n/a # positional only, grouped, optional arguments!
1018n/a # can be optional on the left or right.
1019n/a # here's an example:
1020n/a #
1021n/a # [ [ [ A1 A2 ] B1 B2 B3 ] C1 C2 ] D1 D2 D3 [ E1 E2 E3 [ F1 F2 F3 ] ]
1022n/a #
1023n/a # Here group D are required, and all other groups are optional.
1024n/a # (Group D's "group" is actually None.)
1025n/a # We can figure out which sets of arguments we have based on
1026n/a # how many arguments are in the tuple.
1027n/a #
1028n/a # Note that you need to count up on both sides. For example,
1029n/a # you could have groups C+D, or C+D+E, or C+D+E+F.
1030n/a #
1031n/a # What if the number of arguments leads us to an ambiguous result?
1032n/a # Clinic prefers groups on the left. So in the above example,
1033n/a # five arguments would map to B+C, not C+D.
1034n/a
1035n/a add, output = text_accumulator()
1036n/a parameters = list(f.parameters.values())
1037n/a if isinstance(parameters[0].converter, self_converter):
1038n/a del parameters[0]
1039n/a
1040n/a groups = []
1041n/a group = None
1042n/a left = []
1043n/a right = []
1044n/a required = []
1045n/a last = unspecified
1046n/a
1047n/a for p in parameters:
1048n/a group_id = p.group
1049n/a if group_id != last:
1050n/a last = group_id
1051n/a group = []
1052n/a if group_id < 0:
1053n/a left.append(group)
1054n/a elif group_id == 0:
1055n/a group = required
1056n/a else:
1057n/a right.append(group)
1058n/a group.append(p)
1059n/a
1060n/a count_min = sys.maxsize
1061n/a count_max = -1
1062n/a
1063n/a add("switch (PyTuple_GET_SIZE(args)) {\n")
1064n/a for subset in permute_optional_groups(left, required, right):
1065n/a count = len(subset)
1066n/a count_min = min(count_min, count)
1067n/a count_max = max(count_max, count)
1068n/a
1069n/a if count == 0:
1070n/a add(""" case 0:
1071n/a break;
1072n/a""")
1073n/a continue
1074n/a
1075n/a group_ids = {p.group for p in subset} # eliminate duplicates
1076n/a d = {}
1077n/a d['count'] = count
1078n/a d['name'] = f.name
1079n/a d['format_units'] = "".join(p.converter.format_unit for p in subset)
1080n/a
1081n/a parse_arguments = []
1082n/a for p in subset:
1083n/a p.converter.parse_argument(parse_arguments)
1084n/a d['parse_arguments'] = ", ".join(parse_arguments)
1085n/a
1086n/a group_ids.discard(0)
1087n/a lines = [self.group_to_variable_name(g) + " = 1;" for g in group_ids]
1088n/a lines = "\n".join(lines)
1089n/a
1090n/a s = """
1091n/a case {count}:
1092n/a if (!PyArg_ParseTuple(args, "{format_units}:{name}", {parse_arguments})) {{
1093n/a goto exit;
1094n/a }}
1095n/a {group_booleans}
1096n/a break;
1097n/a"""[1:]
1098n/a s = linear_format(s, group_booleans=lines)
1099n/a s = s.format_map(d)
1100n/a add(s)
1101n/a
1102n/a add(" default:\n")
1103n/a s = ' PyErr_SetString(PyExc_TypeError, "{} requires {} to {} arguments");\n'
1104n/a add(s.format(f.full_name, count_min, count_max))
1105n/a add(' goto exit;\n')
1106n/a add("}")
1107n/a template_dict['option_group_parsing'] = format_escape(output())
1108n/a
1109n/a def render_function(self, clinic, f):
1110n/a if not f:
1111n/a return ""
1112n/a
1113n/a add, output = text_accumulator()
1114n/a data = CRenderData()
1115n/a
1116n/a assert f.parameters, "We should always have a 'self' at this point!"
1117n/a parameters = f.render_parameters
1118n/a converters = [p.converter for p in parameters]
1119n/a
1120n/a templates = self.output_templates(f)
1121n/a
1122n/a f_self = parameters[0]
1123n/a selfless = parameters[1:]
1124n/a assert isinstance(f_self.converter, self_converter), "No self parameter in " + repr(f.full_name) + "!"
1125n/a
1126n/a last_group = 0
1127n/a first_optional = len(selfless)
1128n/a positional = selfless and selfless[-1].is_positional_only()
1129n/a new_or_init = f.kind in (METHOD_NEW, METHOD_INIT)
1130n/a default_return_converter = (not f.return_converter or
1131n/a f.return_converter.type == 'PyObject *')
1132n/a has_option_groups = False
1133n/a
1134n/a # offset i by -1 because first_optional needs to ignore self
1135n/a for i, p in enumerate(parameters, -1):
1136n/a c = p.converter
1137n/a
1138n/a if (i != -1) and (p.default is not unspecified):
1139n/a first_optional = min(first_optional, i)
1140n/a
1141n/a # insert group variable
1142n/a group = p.group
1143n/a if last_group != group:
1144n/a last_group = group
1145n/a if group:
1146n/a group_name = self.group_to_variable_name(group)
1147n/a data.impl_arguments.append(group_name)
1148n/a data.declarations.append("int " + group_name + " = 0;")
1149n/a data.impl_parameters.append("int " + group_name)
1150n/a has_option_groups = True
1151n/a
1152n/a c.render(p, data)
1153n/a
1154n/a if has_option_groups and (not positional):
1155n/a fail("You cannot use optional groups ('[' and ']')\nunless all parameters are positional-only ('/').")
1156n/a
1157n/a # HACK
1158n/a # when we're METH_O, but have a custom return converter,
1159n/a # we use "impl_parameters" for the parsing function
1160n/a # because that works better. but that means we must
1161n/a # suppress actually declaring the impl's parameters
1162n/a # as variables in the parsing function. but since it's
1163n/a # METH_O, we have exactly one anyway, so we know exactly
1164n/a # where it is.
1165n/a if ("METH_O" in templates['methoddef_define'] and
1166n/a '{impl_parameters}' in templates['parser_prototype']):
1167n/a data.declarations.pop(0)
1168n/a
1169n/a template_dict = {}
1170n/a
1171n/a full_name = f.full_name
1172n/a template_dict['full_name'] = full_name
1173n/a
1174n/a if new_or_init:
1175n/a name = f.cls.name
1176n/a else:
1177n/a name = f.name
1178n/a
1179n/a template_dict['name'] = name
1180n/a
1181n/a if f.c_basename:
1182n/a c_basename = f.c_basename
1183n/a else:
1184n/a fields = full_name.split(".")
1185n/a if fields[-1] == '__new__':
1186n/a fields.pop()
1187n/a c_basename = "_".join(fields)
1188n/a
1189n/a template_dict['c_basename'] = c_basename
1190n/a
1191n/a methoddef_name = "{}_METHODDEF".format(c_basename.upper())
1192n/a template_dict['methoddef_name'] = methoddef_name
1193n/a
1194n/a template_dict['docstring'] = self.docstring_for_c_string(f)
1195n/a
1196n/a template_dict['self_name'] = template_dict['self_type'] = template_dict['self_type_check'] = ''
1197n/a f_self.converter.set_template_dict(template_dict)
1198n/a
1199n/a f.return_converter.render(f, data)
1200n/a template_dict['impl_return_type'] = f.return_converter.type
1201n/a
1202n/a template_dict['declarations'] = format_escape("\n".join(data.declarations))
1203n/a template_dict['initializers'] = "\n\n".join(data.initializers)
1204n/a template_dict['modifications'] = '\n\n'.join(data.modifications)
1205n/a template_dict['keywords'] = '"' + '", "'.join(data.keywords) + '"'
1206n/a template_dict['format_units'] = ''.join(data.format_units)
1207n/a template_dict['parse_arguments'] = ', '.join(data.parse_arguments)
1208n/a template_dict['impl_parameters'] = ", ".join(data.impl_parameters)
1209n/a template_dict['impl_arguments'] = ", ".join(data.impl_arguments)
1210n/a template_dict['return_conversion'] = format_escape("".join(data.return_conversion).rstrip())
1211n/a template_dict['cleanup'] = format_escape("".join(data.cleanup))
1212n/a template_dict['return_value'] = data.return_value
1213n/a
1214n/a # used by unpack tuple code generator
1215n/a ignore_self = -1 if isinstance(converters[0], self_converter) else 0
1216n/a unpack_min = first_optional
1217n/a unpack_max = len(selfless)
1218n/a template_dict['unpack_min'] = str(unpack_min)
1219n/a template_dict['unpack_max'] = str(unpack_max)
1220n/a
1221n/a if has_option_groups:
1222n/a self.render_option_group_parsing(f, template_dict)
1223n/a
1224n/a # buffers, not destination
1225n/a for name, destination in clinic.destination_buffers.items():
1226n/a template = templates[name]
1227n/a if has_option_groups:
1228n/a template = linear_format(template,
1229n/a option_group_parsing=template_dict['option_group_parsing'])
1230n/a template = linear_format(template,
1231n/a declarations=template_dict['declarations'],
1232n/a return_conversion=template_dict['return_conversion'],
1233n/a initializers=template_dict['initializers'],
1234n/a modifications=template_dict['modifications'],
1235n/a cleanup=template_dict['cleanup'],
1236n/a )
1237n/a
1238n/a # Only generate the "exit:" label
1239n/a # if we have any gotos
1240n/a need_exit_label = "goto exit;" in template
1241n/a template = linear_format(template,
1242n/a exit_label="exit:" if need_exit_label else ''
1243n/a )
1244n/a
1245n/a s = template.format_map(template_dict)
1246n/a
1247n/a # mild hack:
1248n/a # reflow long impl declarations
1249n/a if name in {"impl_prototype", "impl_definition"}:
1250n/a s = wrap_declarations(s)
1251n/a
1252n/a if clinic.line_prefix:
1253n/a s = indent_all_lines(s, clinic.line_prefix)
1254n/a if clinic.line_suffix:
1255n/a s = suffix_all_lines(s, clinic.line_suffix)
1256n/a
1257n/a destination.append(s)
1258n/a
1259n/a return clinic.get_destination('block').dump()
1260n/a
1261n/a
1262n/a
1263n/a
1264n/a@contextlib.contextmanager
1265n/adef OverrideStdioWith(stdout):
1266n/a saved_stdout = sys.stdout
1267n/a sys.stdout = stdout
1268n/a try:
1269n/a yield
1270n/a finally:
1271n/a assert sys.stdout is stdout
1272n/a sys.stdout = saved_stdout
1273n/a
1274n/a
1275n/adef create_regex(before, after, word=True, whole_line=True):
1276n/a """Create an re object for matching marker lines."""
1277n/a group_re = r"\w+" if word else ".+"
1278n/a pattern = r'{}({}){}'
1279n/a if whole_line:
1280n/a pattern = '^' + pattern + '$'
1281n/a pattern = pattern.format(re.escape(before), group_re, re.escape(after))
1282n/a return re.compile(pattern)
1283n/a
1284n/a
1285n/aclass Block:
1286n/a r"""
1287n/a Represents a single block of text embedded in
1288n/a another file. If dsl_name is None, the block represents
1289n/a verbatim text, raw original text from the file, in
1290n/a which case "input" will be the only non-false member.
1291n/a If dsl_name is not None, the block represents a Clinic
1292n/a block.
1293n/a
1294n/a input is always str, with embedded \n characters.
1295n/a input represents the original text from the file;
1296n/a if it's a Clinic block, it is the original text with
1297n/a the body_prefix and redundant leading whitespace removed.
1298n/a
1299n/a dsl_name is either str or None. If str, it's the text
1300n/a found on the start line of the block between the square
1301n/a brackets.
1302n/a
1303n/a signatures is either list or None. If it's a list,
1304n/a it may only contain clinic.Module, clinic.Class, and
1305n/a clinic.Function objects. At the moment it should
1306n/a contain at most one of each.
1307n/a
1308n/a output is either str or None. If str, it's the output
1309n/a from this block, with embedded '\n' characters.
1310n/a
1311n/a indent is either str or None. It's the leading whitespace
1312n/a that was found on every line of input. (If body_prefix is
1313n/a not empty, this is the indent *after* removing the
1314n/a body_prefix.)
1315n/a
1316n/a preindent is either str or None. It's the whitespace that
1317n/a was found in front of every line of input *before* the
1318n/a "body_prefix" (see the Language object). If body_prefix
1319n/a is empty, preindent must always be empty too.
1320n/a
1321n/a To illustrate indent and preindent: Assume that '_'
1322n/a represents whitespace. If the block processed was in a
1323n/a Python file, and looked like this:
1324n/a ____#/*[python]
1325n/a ____#__for a in range(20):
1326n/a ____#____print(a)
1327n/a ____#[python]*/
1328n/a "preindent" would be "____" and "indent" would be "__".
1329n/a
1330n/a """
1331n/a def __init__(self, input, dsl_name=None, signatures=None, output=None, indent='', preindent=''):
1332n/a assert isinstance(input, str)
1333n/a self.input = input
1334n/a self.dsl_name = dsl_name
1335n/a self.signatures = signatures or []
1336n/a self.output = output
1337n/a self.indent = indent
1338n/a self.preindent = preindent
1339n/a
1340n/a def __repr__(self):
1341n/a dsl_name = self.dsl_name or "text"
1342n/a def summarize(s):
1343n/a s = repr(s)
1344n/a if len(s) > 30:
1345n/a return s[:26] + "..." + s[0]
1346n/a return s
1347n/a return "".join((
1348n/a "<Block ", dsl_name, " input=", summarize(self.input), " output=", summarize(self.output), ">"))
1349n/a
1350n/a
1351n/aclass BlockParser:
1352n/a """
1353n/a Block-oriented parser for Argument Clinic.
1354n/a Iterator, yields Block objects.
1355n/a """
1356n/a
1357n/a def __init__(self, input, language, *, verify=True):
1358n/a """
1359n/a "input" should be a str object
1360n/a with embedded \n characters.
1361n/a
1362n/a "language" should be a Language object.
1363n/a """
1364n/a language.validate()
1365n/a
1366n/a self.input = collections.deque(reversed(input.splitlines(keepends=True)))
1367n/a self.block_start_line_number = self.line_number = 0
1368n/a
1369n/a self.language = language
1370n/a before, _, after = language.start_line.partition('{dsl_name}')
1371n/a assert _ == '{dsl_name}'
1372n/a self.find_start_re = create_regex(before, after, whole_line=False)
1373n/a self.start_re = create_regex(before, after)
1374n/a self.verify = verify
1375n/a self.last_checksum_re = None
1376n/a self.last_dsl_name = None
1377n/a self.dsl_name = None
1378n/a self.first_block = True
1379n/a
1380n/a def __iter__(self):
1381n/a return self
1382n/a
1383n/a def __next__(self):
1384n/a while True:
1385n/a if not self.input:
1386n/a raise StopIteration
1387n/a
1388n/a if self.dsl_name:
1389n/a return_value = self.parse_clinic_block(self.dsl_name)
1390n/a self.dsl_name = None
1391n/a self.first_block = False
1392n/a return return_value
1393n/a block = self.parse_verbatim_block()
1394n/a if self.first_block and not block.input:
1395n/a continue
1396n/a self.first_block = False
1397n/a return block
1398n/a
1399n/a
1400n/a def is_start_line(self, line):
1401n/a match = self.start_re.match(line.lstrip())
1402n/a return match.group(1) if match else None
1403n/a
1404n/a def _line(self, lookahead=False):
1405n/a self.line_number += 1
1406n/a line = self.input.pop()
1407n/a if not lookahead:
1408n/a self.language.parse_line(line)
1409n/a return line
1410n/a
1411n/a def parse_verbatim_block(self):
1412n/a add, output = text_accumulator()
1413n/a self.block_start_line_number = self.line_number
1414n/a
1415n/a while self.input:
1416n/a line = self._line()
1417n/a dsl_name = self.is_start_line(line)
1418n/a if dsl_name:
1419n/a self.dsl_name = dsl_name
1420n/a break
1421n/a add(line)
1422n/a
1423n/a return Block(output())
1424n/a
1425n/a def parse_clinic_block(self, dsl_name):
1426n/a input_add, input_output = text_accumulator()
1427n/a self.block_start_line_number = self.line_number + 1
1428n/a stop_line = self.language.stop_line.format(dsl_name=dsl_name)
1429n/a body_prefix = self.language.body_prefix.format(dsl_name=dsl_name)
1430n/a
1431n/a def is_stop_line(line):
1432n/a # make sure to recognize stop line even if it
1433n/a # doesn't end with EOL (it could be the very end of the file)
1434n/a if not line.startswith(stop_line):
1435n/a return False
1436n/a remainder = line[len(stop_line):]
1437n/a return (not remainder) or remainder.isspace()
1438n/a
1439n/a # consume body of program
1440n/a while self.input:
1441n/a line = self._line()
1442n/a if is_stop_line(line) or self.is_start_line(line):
1443n/a break
1444n/a if body_prefix:
1445n/a line = line.lstrip()
1446n/a assert line.startswith(body_prefix)
1447n/a line = line[len(body_prefix):]
1448n/a input_add(line)
1449n/a
1450n/a # consume output and checksum line, if present.
1451n/a if self.last_dsl_name == dsl_name:
1452n/a checksum_re = self.last_checksum_re
1453n/a else:
1454n/a before, _, after = self.language.checksum_line.format(dsl_name=dsl_name, arguments='{arguments}').partition('{arguments}')
1455n/a assert _ == '{arguments}'
1456n/a checksum_re = create_regex(before, after, word=False)
1457n/a self.last_dsl_name = dsl_name
1458n/a self.last_checksum_re = checksum_re
1459n/a
1460n/a # scan forward for checksum line
1461n/a output_add, output_output = text_accumulator()
1462n/a arguments = None
1463n/a while self.input:
1464n/a line = self._line(lookahead=True)
1465n/a match = checksum_re.match(line.lstrip())
1466n/a arguments = match.group(1) if match else None
1467n/a if arguments:
1468n/a break
1469n/a output_add(line)
1470n/a if self.is_start_line(line):
1471n/a break
1472n/a
1473n/a output = output_output()
1474n/a if arguments:
1475n/a d = {}
1476n/a for field in shlex.split(arguments):
1477n/a name, equals, value = field.partition('=')
1478n/a if not equals:
1479n/a fail("Mangled Argument Clinic marker line: {!r}".format(line))
1480n/a d[name.strip()] = value.strip()
1481n/a
1482n/a if self.verify:
1483n/a if 'input' in d:
1484n/a checksum = d['output']
1485n/a input_checksum = d['input']
1486n/a else:
1487n/a checksum = d['checksum']
1488n/a input_checksum = None
1489n/a
1490n/a computed = compute_checksum(output, len(checksum))
1491n/a if checksum != computed:
1492n/a fail("Checksum mismatch!\nExpected: {}\nComputed: {}\n"
1493n/a "Suggested fix: remove all generated code including "
1494n/a "the end marker,\n"
1495n/a "or use the '-f' option."
1496n/a .format(checksum, computed))
1497n/a else:
1498n/a # put back output
1499n/a output_lines = output.splitlines(keepends=True)
1500n/a self.line_number -= len(output_lines)
1501n/a self.input.extend(reversed(output_lines))
1502n/a output = None
1503n/a
1504n/a return Block(input_output(), dsl_name, output=output)
1505n/a
1506n/a
1507n/aclass BlockPrinter:
1508n/a
1509n/a def __init__(self, language, f=None):
1510n/a self.language = language
1511n/a self.f = f or io.StringIO()
1512n/a
1513n/a def print_block(self, block):
1514n/a input = block.input
1515n/a output = block.output
1516n/a dsl_name = block.dsl_name
1517n/a write = self.f.write
1518n/a
1519n/a assert not ((dsl_name == None) ^ (output == None)), "you must specify dsl_name and output together, dsl_name " + repr(dsl_name)
1520n/a
1521n/a if not dsl_name:
1522n/a write(input)
1523n/a return
1524n/a
1525n/a write(self.language.start_line.format(dsl_name=dsl_name))
1526n/a write("\n")
1527n/a
1528n/a body_prefix = self.language.body_prefix.format(dsl_name=dsl_name)
1529n/a if not body_prefix:
1530n/a write(input)
1531n/a else:
1532n/a for line in input.split('\n'):
1533n/a write(body_prefix)
1534n/a write(line)
1535n/a write("\n")
1536n/a
1537n/a write(self.language.stop_line.format(dsl_name=dsl_name))
1538n/a write("\n")
1539n/a
1540n/a input = ''.join(block.input)
1541n/a output = ''.join(block.output)
1542n/a if output:
1543n/a if not output.endswith('\n'):
1544n/a output += '\n'
1545n/a write(output)
1546n/a
1547n/a arguments="output={} input={}".format(compute_checksum(output, 16), compute_checksum(input, 16))
1548n/a write(self.language.checksum_line.format(dsl_name=dsl_name, arguments=arguments))
1549n/a write("\n")
1550n/a
1551n/a def write(self, text):
1552n/a self.f.write(text)
1553n/a
1554n/a
1555n/aclass BufferSeries:
1556n/a """
1557n/a Behaves like a "defaultlist".
1558n/a When you ask for an index that doesn't exist yet,
1559n/a the object grows the list until that item exists.
1560n/a So o[n] will always work.
1561n/a
1562n/a Supports negative indices for actual items.
1563n/a e.g. o[-1] is an element immediately preceding o[0].
1564n/a """
1565n/a
1566n/a def __init__(self):
1567n/a self._start = 0
1568n/a self._array = []
1569n/a self._constructor = _text_accumulator
1570n/a
1571n/a def __getitem__(self, i):
1572n/a i -= self._start
1573n/a if i < 0:
1574n/a self._start += i
1575n/a prefix = [self._constructor() for x in range(-i)]
1576n/a self._array = prefix + self._array
1577n/a i = 0
1578n/a while i >= len(self._array):
1579n/a self._array.append(self._constructor())
1580n/a return self._array[i]
1581n/a
1582n/a def clear(self):
1583n/a for ta in self._array:
1584n/a ta._text.clear()
1585n/a
1586n/a def dump(self):
1587n/a texts = [ta.output() for ta in self._array]
1588n/a return "".join(texts)
1589n/a
1590n/a
1591n/aclass Destination:
1592n/a def __init__(self, name, type, clinic, *args):
1593n/a self.name = name
1594n/a self.type = type
1595n/a self.clinic = clinic
1596n/a valid_types = ('buffer', 'file', 'suppress')
1597n/a if type not in valid_types:
1598n/a fail("Invalid destination type " + repr(type) + " for " + name + " , must be " + ', '.join(valid_types))
1599n/a extra_arguments = 1 if type == "file" else 0
1600n/a if len(args) < extra_arguments:
1601n/a fail("Not enough arguments for destination " + name + " new " + type)
1602n/a if len(args) > extra_arguments:
1603n/a fail("Too many arguments for destination " + name + " new " + type)
1604n/a if type =='file':
1605n/a d = {}
1606n/a filename = clinic.filename
1607n/a d['path'] = filename
1608n/a dirname, basename = os.path.split(filename)
1609n/a if not dirname:
1610n/a dirname = '.'
1611n/a d['dirname'] = dirname
1612n/a d['basename'] = basename
1613n/a d['basename_root'], d['basename_extension'] = os.path.splitext(filename)
1614n/a self.filename = args[0].format_map(d)
1615n/a
1616n/a self.buffers = BufferSeries()
1617n/a
1618n/a def __repr__(self):
1619n/a if self.type == 'file':
1620n/a file_repr = " " + repr(self.filename)
1621n/a else:
1622n/a file_repr = ''
1623n/a return "".join(("<Destination ", self.name, " ", self.type, file_repr, ">"))
1624n/a
1625n/a def clear(self):
1626n/a if self.type != 'buffer':
1627n/a fail("Can't clear destination" + self.name + " , it's not of type buffer")
1628n/a self.buffers.clear()
1629n/a
1630n/a def dump(self):
1631n/a return self.buffers.dump()
1632n/a
1633n/a
1634n/a# maps strings to Language objects.
1635n/a# "languages" maps the name of the language ("C", "Python").
1636n/a# "extensions" maps the file extension ("c", "py").
1637n/alanguages = { 'C': CLanguage, 'Python': PythonLanguage }
1638n/aextensions = { name: CLanguage for name in "c cc cpp cxx h hh hpp hxx".split() }
1639n/aextensions['py'] = PythonLanguage
1640n/a
1641n/a
1642n/a# maps strings to callables.
1643n/a# these callables must be of the form:
1644n/a# def foo(name, default, *, ...)
1645n/a# The callable may have any number of keyword-only parameters.
1646n/a# The callable must return a CConverter object.
1647n/a# The callable should not call builtins.print.
1648n/aconverters = {}
1649n/a
1650n/a# maps strings to callables.
1651n/a# these callables follow the same rules as those for "converters" above.
1652n/a# note however that they will never be called with keyword-only parameters.
1653n/alegacy_converters = {}
1654n/a
1655n/a
1656n/a# maps strings to callables.
1657n/a# these callables must be of the form:
1658n/a# def foo(*, ...)
1659n/a# The callable may have any number of keyword-only parameters.
1660n/a# The callable must return a CConverter object.
1661n/a# The callable should not call builtins.print.
1662n/areturn_converters = {}
1663n/a
1664n/aclinic = None
1665n/aclass Clinic:
1666n/a
1667n/a presets_text = """
1668n/apreset block
1669n/aeverything block
1670n/amethoddef_ifndef buffer 1
1671n/adocstring_prototype suppress
1672n/aparser_prototype suppress
1673n/acpp_if suppress
1674n/acpp_endif suppress
1675n/a
1676n/apreset original
1677n/aeverything block
1678n/amethoddef_ifndef buffer 1
1679n/adocstring_prototype suppress
1680n/aparser_prototype suppress
1681n/acpp_if suppress
1682n/acpp_endif suppress
1683n/a
1684n/apreset file
1685n/aeverything file
1686n/amethoddef_ifndef file 1
1687n/adocstring_prototype suppress
1688n/aparser_prototype suppress
1689n/aimpl_definition block
1690n/a
1691n/apreset buffer
1692n/aeverything buffer
1693n/amethoddef_ifndef buffer 1
1694n/aimpl_definition block
1695n/adocstring_prototype suppress
1696n/aimpl_prototype suppress
1697n/aparser_prototype suppress
1698n/a
1699n/apreset partial-buffer
1700n/aeverything buffer
1701n/amethoddef_ifndef buffer 1
1702n/adocstring_prototype block
1703n/aimpl_prototype suppress
1704n/amethoddef_define block
1705n/aparser_prototype block
1706n/aimpl_definition block
1707n/a
1708n/a"""
1709n/a
1710n/a def __init__(self, language, printer=None, *, force=False, verify=True, filename=None):
1711n/a # maps strings to Parser objects.
1712n/a # (instantiated from the "parsers" global.)
1713n/a self.parsers = {}
1714n/a self.language = language
1715n/a if printer:
1716n/a fail("Custom printers are broken right now")
1717n/a self.printer = printer or BlockPrinter(language)
1718n/a self.verify = verify
1719n/a self.force = force
1720n/a self.filename = filename
1721n/a self.modules = collections.OrderedDict()
1722n/a self.classes = collections.OrderedDict()
1723n/a self.functions = []
1724n/a
1725n/a self.line_prefix = self.line_suffix = ''
1726n/a
1727n/a self.destinations = {}
1728n/a self.add_destination("block", "buffer")
1729n/a self.add_destination("suppress", "suppress")
1730n/a self.add_destination("buffer", "buffer")
1731n/a if filename:
1732n/a self.add_destination("file", "file", "{dirname}/clinic/{basename}.h")
1733n/a
1734n/a d = self.get_destination_buffer
1735n/a self.destination_buffers = collections.OrderedDict((
1736n/a ('cpp_if', d('file')),
1737n/a ('docstring_prototype', d('suppress')),
1738n/a ('docstring_definition', d('file')),
1739n/a ('methoddef_define', d('file')),
1740n/a ('impl_prototype', d('file')),
1741n/a ('parser_prototype', d('suppress')),
1742n/a ('parser_definition', d('file')),
1743n/a ('cpp_endif', d('file')),
1744n/a ('methoddef_ifndef', d('file', 1)),
1745n/a ('impl_definition', d('block')),
1746n/a ))
1747n/a
1748n/a self.destination_buffers_stack = []
1749n/a self.ifndef_symbols = set()
1750n/a
1751n/a self.presets = {}
1752n/a preset = None
1753n/a for line in self.presets_text.strip().split('\n'):
1754n/a line = line.strip()
1755n/a if not line:
1756n/a continue
1757n/a name, value, *options = line.split()
1758n/a if name == 'preset':
1759n/a self.presets[value] = preset = collections.OrderedDict()
1760n/a continue
1761n/a
1762n/a if len(options):
1763n/a index = int(options[0])
1764n/a else:
1765n/a index = 0
1766n/a buffer = self.get_destination_buffer(value, index)
1767n/a
1768n/a if name == 'everything':
1769n/a for name in self.destination_buffers:
1770n/a preset[name] = buffer
1771n/a continue
1772n/a
1773n/a assert name in self.destination_buffers
1774n/a preset[name] = buffer
1775n/a
1776n/a global clinic
1777n/a clinic = self
1778n/a
1779n/a def add_destination(self, name, type, *args):
1780n/a if name in self.destinations:
1781n/a fail("Destination already exists: " + repr(name))
1782n/a self.destinations[name] = Destination(name, type, self, *args)
1783n/a
1784n/a def get_destination(self, name):
1785n/a d = self.destinations.get(name)
1786n/a if not d:
1787n/a fail("Destination does not exist: " + repr(name))
1788n/a return d
1789n/a
1790n/a def get_destination_buffer(self, name, item=0):
1791n/a d = self.get_destination(name)
1792n/a return d.buffers[item]
1793n/a
1794n/a def parse(self, input):
1795n/a printer = self.printer
1796n/a self.block_parser = BlockParser(input, self.language, verify=self.verify)
1797n/a for block in self.block_parser:
1798n/a dsl_name = block.dsl_name
1799n/a if dsl_name:
1800n/a if dsl_name not in self.parsers:
1801n/a assert dsl_name in parsers, "No parser to handle {!r} block.".format(dsl_name)
1802n/a self.parsers[dsl_name] = parsers[dsl_name](self)
1803n/a parser = self.parsers[dsl_name]
1804n/a try:
1805n/a parser.parse(block)
1806n/a except Exception:
1807n/a fail('Exception raised during parsing:\n' +
1808n/a traceback.format_exc().rstrip())
1809n/a printer.print_block(block)
1810n/a
1811n/a second_pass_replacements = {}
1812n/a
1813n/a # these are destinations not buffers
1814n/a for name, destination in self.destinations.items():
1815n/a if destination.type == 'suppress':
1816n/a continue
1817n/a output = destination.dump()
1818n/a
1819n/a if output:
1820n/a
1821n/a block = Block("", dsl_name="clinic", output=output)
1822n/a
1823n/a if destination.type == 'buffer':
1824n/a block.input = "dump " + name + "\n"
1825n/a warn("Destination buffer " + repr(name) + " not empty at end of file, emptying.")
1826n/a printer.write("\n")
1827n/a printer.print_block(block)
1828n/a continue
1829n/a
1830n/a if destination.type == 'file':
1831n/a try:
1832n/a dirname = os.path.dirname(destination.filename)
1833n/a try:
1834n/a os.makedirs(dirname)
1835n/a except FileExistsError:
1836n/a if not os.path.isdir(dirname):
1837n/a fail("Can't write to destination {}, "
1838n/a "can't make directory {}!".format(
1839n/a destination.filename, dirname))
1840n/a if self.verify:
1841n/a with open(destination.filename, "rt") as f:
1842n/a parser_2 = BlockParser(f.read(), language=self.language)
1843n/a blocks = list(parser_2)
1844n/a if (len(blocks) != 1) or (blocks[0].input != 'preserve\n'):
1845n/a fail("Modified destination file " + repr(destination.filename) + ", not overwriting!")
1846n/a except FileNotFoundError:
1847n/a pass
1848n/a
1849n/a block.input = 'preserve\n'
1850n/a printer_2 = BlockPrinter(self.language)
1851n/a printer_2.print_block(block)
1852n/a with open(destination.filename, "wt") as f:
1853n/a f.write(printer_2.f.getvalue())
1854n/a continue
1855n/a text = printer.f.getvalue()
1856n/a
1857n/a if second_pass_replacements:
1858n/a printer_2 = BlockPrinter(self.language)
1859n/a parser_2 = BlockParser(text, self.language)
1860n/a changed = False
1861n/a for block in parser_2:
1862n/a if block.dsl_name:
1863n/a for id, replacement in second_pass_replacements.items():
1864n/a if id in block.output:
1865n/a changed = True
1866n/a block.output = block.output.replace(id, replacement)
1867n/a printer_2.print_block(block)
1868n/a if changed:
1869n/a text = printer_2.f.getvalue()
1870n/a
1871n/a return text
1872n/a
1873n/a
1874n/a def _module_and_class(self, fields):
1875n/a """
1876n/a fields should be an iterable of field names.
1877n/a returns a tuple of (module, class).
1878n/a the module object could actually be self (a clinic object).
1879n/a this function is only ever used to find the parent of where
1880n/a a new class/module should go.
1881n/a """
1882n/a in_classes = False
1883n/a parent = module = self
1884n/a cls = None
1885n/a so_far = []
1886n/a
1887n/a for field in fields:
1888n/a so_far.append(field)
1889n/a if not in_classes:
1890n/a child = parent.modules.get(field)
1891n/a if child:
1892n/a parent = module = child
1893n/a continue
1894n/a in_classes = True
1895n/a if not hasattr(parent, 'classes'):
1896n/a return module, cls
1897n/a child = parent.classes.get(field)
1898n/a if not child:
1899n/a fail('Parent class or module ' + '.'.join(so_far) + " does not exist.")
1900n/a cls = parent = child
1901n/a
1902n/a return module, cls
1903n/a
1904n/a
1905n/adef parse_file(filename, *, force=False, verify=True, output=None, encoding='utf-8'):
1906n/a extension = os.path.splitext(filename)[1][1:]
1907n/a if not extension:
1908n/a fail("Can't extract file type for file " + repr(filename))
1909n/a
1910n/a try:
1911n/a language = extensions[extension](filename)
1912n/a except KeyError:
1913n/a fail("Can't identify file type for file " + repr(filename))
1914n/a
1915n/a with open(filename, 'r', encoding=encoding) as f:
1916n/a raw = f.read()
1917n/a
1918n/a # exit quickly if there are no clinic markers in the file
1919n/a find_start_re = BlockParser("", language).find_start_re
1920n/a if not find_start_re.search(raw):
1921n/a return
1922n/a
1923n/a clinic = Clinic(language, force=force, verify=verify, filename=filename)
1924n/a cooked = clinic.parse(raw)
1925n/a if (cooked == raw) and not force:
1926n/a return
1927n/a
1928n/a directory = os.path.dirname(filename) or '.'
1929n/a
1930n/a with tempfile.TemporaryDirectory(prefix="clinic", dir=directory) as tmpdir:
1931n/a bytes = cooked.encode(encoding)
1932n/a tmpfilename = os.path.join(tmpdir, os.path.basename(filename))
1933n/a with open(tmpfilename, "wb") as f:
1934n/a f.write(bytes)
1935n/a os.replace(tmpfilename, output or filename)
1936n/a
1937n/a
1938n/adef compute_checksum(input, length=None):
1939n/a input = input or ''
1940n/a s = hashlib.sha1(input.encode('utf-8')).hexdigest()
1941n/a if length:
1942n/a s = s[:length]
1943n/a return s
1944n/a
1945n/a
1946n/a
1947n/a
1948n/aclass PythonParser:
1949n/a def __init__(self, clinic):
1950n/a pass
1951n/a
1952n/a def parse(self, block):
1953n/a s = io.StringIO()
1954n/a with OverrideStdioWith(s):
1955n/a exec(block.input)
1956n/a block.output = s.getvalue()
1957n/a
1958n/a
1959n/aclass Module:
1960n/a def __init__(self, name, module=None):
1961n/a self.name = name
1962n/a self.module = self.parent = module
1963n/a
1964n/a self.modules = collections.OrderedDict()
1965n/a self.classes = collections.OrderedDict()
1966n/a self.functions = []
1967n/a
1968n/a def __repr__(self):
1969n/a return "<clinic.Module " + repr(self.name) + " at " + str(id(self)) + ">"
1970n/a
1971n/aclass Class:
1972n/a def __init__(self, name, module=None, cls=None, typedef=None, type_object=None):
1973n/a self.name = name
1974n/a self.module = module
1975n/a self.cls = cls
1976n/a self.typedef = typedef
1977n/a self.type_object = type_object
1978n/a self.parent = cls or module
1979n/a
1980n/a self.classes = collections.OrderedDict()
1981n/a self.functions = []
1982n/a
1983n/a def __repr__(self):
1984n/a return "<clinic.Class " + repr(self.name) + " at " + str(id(self)) + ">"
1985n/a
1986n/aunsupported_special_methods = set("""
1987n/a
1988n/a__abs__
1989n/a__add__
1990n/a__and__
1991n/a__bytes__
1992n/a__call__
1993n/a__complex__
1994n/a__delitem__
1995n/a__divmod__
1996n/a__eq__
1997n/a__float__
1998n/a__floordiv__
1999n/a__ge__
2000n/a__getattr__
2001n/a__getattribute__
2002n/a__getitem__
2003n/a__gt__
2004n/a__hash__
2005n/a__iadd__
2006n/a__iand__
2007n/a__ifloordiv__
2008n/a__ilshift__
2009n/a__imatmul__
2010n/a__imod__
2011n/a__imul__
2012n/a__index__
2013n/a__int__
2014n/a__invert__
2015n/a__ior__
2016n/a__ipow__
2017n/a__irshift__
2018n/a__isub__
2019n/a__iter__
2020n/a__itruediv__
2021n/a__ixor__
2022n/a__le__
2023n/a__len__
2024n/a__lshift__
2025n/a__lt__
2026n/a__matmul__
2027n/a__mod__
2028n/a__mul__
2029n/a__neg__
2030n/a__new__
2031n/a__next__
2032n/a__or__
2033n/a__pos__
2034n/a__pow__
2035n/a__radd__
2036n/a__rand__
2037n/a__rdivmod__
2038n/a__repr__
2039n/a__rfloordiv__
2040n/a__rlshift__
2041n/a__rmatmul__
2042n/a__rmod__
2043n/a__rmul__
2044n/a__ror__
2045n/a__round__
2046n/a__rpow__
2047n/a__rrshift__
2048n/a__rshift__
2049n/a__rsub__
2050n/a__rtruediv__
2051n/a__rxor__
2052n/a__setattr__
2053n/a__setitem__
2054n/a__str__
2055n/a__sub__
2056n/a__truediv__
2057n/a__xor__
2058n/a
2059n/a""".strip().split())
2060n/a
2061n/a
2062n/aINVALID, CALLABLE, STATIC_METHOD, CLASS_METHOD, METHOD_INIT, METHOD_NEW = """
2063n/aINVALID, CALLABLE, STATIC_METHOD, CLASS_METHOD, METHOD_INIT, METHOD_NEW
2064n/a""".replace(",", "").strip().split()
2065n/a
2066n/aclass Function:
2067n/a """
2068n/a Mutable duck type for inspect.Function.
2069n/a
2070n/a docstring - a str containing
2071n/a * embedded line breaks
2072n/a * text outdented to the left margin
2073n/a * no trailing whitespace.
2074n/a It will always be true that
2075n/a (not docstring) or ((not docstring[0].isspace()) and (docstring.rstrip() == docstring))
2076n/a """
2077n/a
2078n/a def __init__(self, parameters=None, *, name,
2079n/a module, cls=None, c_basename=None,
2080n/a full_name=None,
2081n/a return_converter, return_annotation=_empty,
2082n/a docstring=None, kind=CALLABLE, coexist=False,
2083n/a docstring_only=False):
2084n/a self.parameters = parameters or collections.OrderedDict()
2085n/a self.return_annotation = return_annotation
2086n/a self.name = name
2087n/a self.full_name = full_name
2088n/a self.module = module
2089n/a self.cls = cls
2090n/a self.parent = cls or module
2091n/a self.c_basename = c_basename
2092n/a self.return_converter = return_converter
2093n/a self.docstring = docstring or ''
2094n/a self.kind = kind
2095n/a self.coexist = coexist
2096n/a self.self_converter = None
2097n/a # docstring_only means "don't generate a machine-readable
2098n/a # signature, just a normal docstring". it's True for
2099n/a # functions with optional groups because we can't represent
2100n/a # those accurately with inspect.Signature in 3.4.
2101n/a self.docstring_only = docstring_only
2102n/a
2103n/a self.rendered_parameters = None
2104n/a
2105n/a __render_parameters__ = None
2106n/a @property
2107n/a def render_parameters(self):
2108n/a if not self.__render_parameters__:
2109n/a self.__render_parameters__ = l = []
2110n/a for p in self.parameters.values():
2111n/a p = p.copy()
2112n/a p.converter.pre_render()
2113n/a l.append(p)
2114n/a return self.__render_parameters__
2115n/a
2116n/a @property
2117n/a def methoddef_flags(self):
2118n/a if self.kind in (METHOD_INIT, METHOD_NEW):
2119n/a return None
2120n/a flags = []
2121n/a if self.kind == CLASS_METHOD:
2122n/a flags.append('METH_CLASS')
2123n/a elif self.kind == STATIC_METHOD:
2124n/a flags.append('METH_STATIC')
2125n/a else:
2126n/a assert self.kind == CALLABLE, "unknown kind: " + repr(self.kind)
2127n/a if self.coexist:
2128n/a flags.append('METH_COEXIST')
2129n/a return '|'.join(flags)
2130n/a
2131n/a def __repr__(self):
2132n/a return '<clinic.Function ' + self.name + '>'
2133n/a
2134n/a def copy(self, **overrides):
2135n/a kwargs = {
2136n/a 'name': self.name, 'module': self.module, 'parameters': self.parameters,
2137n/a 'cls': self.cls, 'c_basename': self.c_basename,
2138n/a 'full_name': self.full_name,
2139n/a 'return_converter': self.return_converter, 'return_annotation': self.return_annotation,
2140n/a 'docstring': self.docstring, 'kind': self.kind, 'coexist': self.coexist,
2141n/a 'docstring_only': self.docstring_only,
2142n/a }
2143n/a kwargs.update(overrides)
2144n/a f = Function(**kwargs)
2145n/a
2146n/a parameters = collections.OrderedDict()
2147n/a for name, value in f.parameters.items():
2148n/a value = value.copy(function=f)
2149n/a parameters[name] = value
2150n/a f.parameters = parameters
2151n/a return f
2152n/a
2153n/a
2154n/aclass Parameter:
2155n/a """
2156n/a Mutable duck type of inspect.Parameter.
2157n/a """
2158n/a
2159n/a def __init__(self, name, kind, *, default=_empty,
2160n/a function, converter, annotation=_empty,
2161n/a docstring=None, group=0):
2162n/a self.name = name
2163n/a self.kind = kind
2164n/a self.default = default
2165n/a self.function = function
2166n/a self.converter = converter
2167n/a self.annotation = annotation
2168n/a self.docstring = docstring or ''
2169n/a self.group = group
2170n/a
2171n/a def __repr__(self):
2172n/a return '<clinic.Parameter ' + self.name + '>'
2173n/a
2174n/a def is_keyword_only(self):
2175n/a return self.kind == inspect.Parameter.KEYWORD_ONLY
2176n/a
2177n/a def is_positional_only(self):
2178n/a return self.kind == inspect.Parameter.POSITIONAL_ONLY
2179n/a
2180n/a def copy(self, **overrides):
2181n/a kwargs = {
2182n/a 'name': self.name, 'kind': self.kind, 'default':self.default,
2183n/a 'function': self.function, 'converter': self.converter, 'annotation': self.annotation,
2184n/a 'docstring': self.docstring, 'group': self.group,
2185n/a }
2186n/a kwargs.update(overrides)
2187n/a if 'converter' not in overrides:
2188n/a converter = copy.copy(self.converter)
2189n/a converter.function = kwargs['function']
2190n/a kwargs['converter'] = converter
2191n/a return Parameter(**kwargs)
2192n/a
2193n/a
2194n/a
2195n/aclass LandMine:
2196n/a # try to access any
2197n/a def __init__(self, message):
2198n/a self.__message__ = message
2199n/a
2200n/a def __repr__(self):
2201n/a return '<LandMine ' + repr(self.__message__) + ">"
2202n/a
2203n/a def __getattribute__(self, name):
2204n/a if name in ('__repr__', '__message__'):
2205n/a return super().__getattribute__(name)
2206n/a # raise RuntimeError(repr(name))
2207n/a fail("Stepped on a land mine, trying to access attribute " + repr(name) + ":\n" + self.__message__)
2208n/a
2209n/a
2210n/adef add_c_converter(f, name=None):
2211n/a if not name:
2212n/a name = f.__name__
2213n/a if not name.endswith('_converter'):
2214n/a return f
2215n/a name = name[:-len('_converter')]
2216n/a converters[name] = f
2217n/a return f
2218n/a
2219n/adef add_default_legacy_c_converter(cls):
2220n/a # automatically add converter for default format unit
2221n/a # (but without stomping on the existing one if it's already
2222n/a # set, in case you subclass)
2223n/a if ((cls.format_unit not in ('O&', '')) and
2224n/a (cls.format_unit not in legacy_converters)):
2225n/a legacy_converters[cls.format_unit] = cls
2226n/a return cls
2227n/a
2228n/adef add_legacy_c_converter(format_unit, **kwargs):
2229n/a """
2230n/a Adds a legacy converter.
2231n/a """
2232n/a def closure(f):
2233n/a if not kwargs:
2234n/a added_f = f
2235n/a else:
2236n/a added_f = functools.partial(f, **kwargs)
2237n/a if format_unit:
2238n/a legacy_converters[format_unit] = added_f
2239n/a return f
2240n/a return closure
2241n/a
2242n/aclass CConverterAutoRegister(type):
2243n/a def __init__(cls, name, bases, classdict):
2244n/a add_c_converter(cls)
2245n/a add_default_legacy_c_converter(cls)
2246n/a
2247n/aclass CConverter(metaclass=CConverterAutoRegister):
2248n/a """
2249n/a For the init function, self, name, function, and default
2250n/a must be keyword-or-positional parameters. All other
2251n/a parameters must be keyword-only.
2252n/a """
2253n/a
2254n/a # The C name to use for this variable.
2255n/a name = None
2256n/a
2257n/a # The Python name to use for this variable.
2258n/a py_name = None
2259n/a
2260n/a # The C type to use for this variable.
2261n/a # 'type' should be a Python string specifying the type, e.g. "int".
2262n/a # If this is a pointer type, the type string should end with ' *'.
2263n/a type = None
2264n/a
2265n/a # The Python default value for this parameter, as a Python value.
2266n/a # Or the magic value "unspecified" if there is no default.
2267n/a # Or the magic value "unknown" if this value is a cannot be evaluated
2268n/a # at Argument-Clinic-preprocessing time (but is presumed to be valid
2269n/a # at runtime).
2270n/a default = unspecified
2271n/a
2272n/a # If not None, default must be isinstance() of this type.
2273n/a # (You can also specify a tuple of types.)
2274n/a default_type = None
2275n/a
2276n/a # "default" converted into a C value, as a string.
2277n/a # Or None if there is no default.
2278n/a c_default = None
2279n/a
2280n/a # "default" converted into a Python value, as a string.
2281n/a # Or None if there is no default.
2282n/a py_default = None
2283n/a
2284n/a # The default value used to initialize the C variable when
2285n/a # there is no default, but not specifying a default may
2286n/a # result in an "uninitialized variable" warning. This can
2287n/a # easily happen when using option groups--although
2288n/a # properly-written code won't actually use the variable,
2289n/a # the variable does get passed in to the _impl. (Ah, if
2290n/a # only dataflow analysis could inline the static function!)
2291n/a #
2292n/a # This value is specified as a string.
2293n/a # Every non-abstract subclass should supply a valid value.
2294n/a c_ignored_default = 'NULL'
2295n/a
2296n/a # The C converter *function* to be used, if any.
2297n/a # (If this is not None, format_unit must be 'O&'.)
2298n/a converter = None
2299n/a
2300n/a # Should Argument Clinic add a '&' before the name of
2301n/a # the variable when passing it into the _impl function?
2302n/a impl_by_reference = False
2303n/a
2304n/a # Should Argument Clinic add a '&' before the name of
2305n/a # the variable when passing it into PyArg_ParseTuple (AndKeywords)?
2306n/a parse_by_reference = True
2307n/a
2308n/a #############################################################
2309n/a #############################################################
2310n/a ## You shouldn't need to read anything below this point to ##
2311n/a ## write your own converter functions. ##
2312n/a #############################################################
2313n/a #############################################################
2314n/a
2315n/a # The "format unit" to specify for this variable when
2316n/a # parsing arguments using PyArg_ParseTuple (AndKeywords).
2317n/a # Custom converters should always use the default value of 'O&'.
2318n/a format_unit = 'O&'
2319n/a
2320n/a # What encoding do we want for this variable? Only used
2321n/a # by format units starting with 'e'.
2322n/a encoding = None
2323n/a
2324n/a # Should this object be required to be a subclass of a specific type?
2325n/a # If not None, should be a string representing a pointer to a
2326n/a # PyTypeObject (e.g. "&PyUnicode_Type").
2327n/a # Only used by the 'O!' format unit (and the "object" converter).
2328n/a subclass_of = None
2329n/a
2330n/a # Do we want an adjacent '_length' variable for this variable?
2331n/a # Only used by format units ending with '#'.
2332n/a length = False
2333n/a
2334n/a # Should we show this parameter in the generated
2335n/a # __text_signature__? This is *almost* always True.
2336n/a # (It's only False for __new__, __init__, and METH_STATIC functions.)
2337n/a show_in_signature = True
2338n/a
2339n/a # Overrides the name used in a text signature.
2340n/a # The name used for a "self" parameter must be one of
2341n/a # self, type, or module; however users can set their own.
2342n/a # This lets the self_converter overrule the user-settable
2343n/a # name, *just* for the text signature.
2344n/a # Only set by self_converter.
2345n/a signature_name = None
2346n/a
2347n/a # keep in sync with self_converter.__init__!
2348n/a def __init__(self, name, py_name, function, default=unspecified, *, c_default=None, py_default=None, annotation=unspecified, **kwargs):
2349n/a self.name = name
2350n/a self.py_name = py_name
2351n/a
2352n/a if default is not unspecified:
2353n/a if self.default_type and not isinstance(default, (self.default_type, Unknown)):
2354n/a if isinstance(self.default_type, type):
2355n/a types_str = self.default_type.__name__
2356n/a else:
2357n/a types_str = ', '.join((cls.__name__ for cls in self.default_type))
2358n/a fail("{}: default value {!r} for field {} is not of type {}".format(
2359n/a self.__class__.__name__, default, name, types_str))
2360n/a self.default = default
2361n/a
2362n/a if c_default:
2363n/a self.c_default = c_default
2364n/a if py_default:
2365n/a self.py_default = py_default
2366n/a
2367n/a if annotation != unspecified:
2368n/a fail("The 'annotation' parameter is not currently permitted.")
2369n/a
2370n/a # this is deliberate, to prevent you from caching information
2371n/a # about the function in the init.
2372n/a # (that breaks if we get cloned.)
2373n/a # so after this change we will noisily fail.
2374n/a self.function = LandMine("Don't access members of self.function inside converter_init!")
2375n/a self.converter_init(**kwargs)
2376n/a self.function = function
2377n/a
2378n/a def converter_init(self):
2379n/a pass
2380n/a
2381n/a def is_optional(self):
2382n/a return (self.default is not unspecified)
2383n/a
2384n/a def _render_self(self, parameter, data):
2385n/a self.parameter = parameter
2386n/a original_name = self.name
2387n/a name = ensure_legal_c_identifier(original_name)
2388n/a
2389n/a # impl_arguments
2390n/a s = ("&" if self.impl_by_reference else "") + name
2391n/a data.impl_arguments.append(s)
2392n/a if self.length:
2393n/a data.impl_arguments.append(self.length_name())
2394n/a
2395n/a # impl_parameters
2396n/a data.impl_parameters.append(self.simple_declaration(by_reference=self.impl_by_reference))
2397n/a if self.length:
2398n/a data.impl_parameters.append("Py_ssize_clean_t " + self.length_name())
2399n/a
2400n/a def _render_non_self(self, parameter, data):
2401n/a self.parameter = parameter
2402n/a original_name = self.name
2403n/a name = ensure_legal_c_identifier(original_name)
2404n/a
2405n/a # declarations
2406n/a d = self.declaration()
2407n/a data.declarations.append(d)
2408n/a
2409n/a # initializers
2410n/a initializers = self.initialize()
2411n/a if initializers:
2412n/a data.initializers.append('/* initializers for ' + name + ' */\n' + initializers.rstrip())
2413n/a
2414n/a # modifications
2415n/a modifications = self.modify()
2416n/a if modifications:
2417n/a data.modifications.append('/* modifications for ' + name + ' */\n' + modifications.rstrip())
2418n/a
2419n/a # keywords
2420n/a if parameter.is_positional_only():
2421n/a data.keywords.append('')
2422n/a else:
2423n/a data.keywords.append(parameter.name)
2424n/a
2425n/a # format_units
2426n/a if self.is_optional() and '|' not in data.format_units:
2427n/a data.format_units.append('|')
2428n/a if parameter.is_keyword_only() and '$' not in data.format_units:
2429n/a data.format_units.append('$')
2430n/a data.format_units.append(self.format_unit)
2431n/a
2432n/a # parse_arguments
2433n/a self.parse_argument(data.parse_arguments)
2434n/a
2435n/a # cleanup
2436n/a cleanup = self.cleanup()
2437n/a if cleanup:
2438n/a data.cleanup.append('/* Cleanup for ' + name + ' */\n' + cleanup.rstrip() + "\n")
2439n/a
2440n/a def render(self, parameter, data):
2441n/a """
2442n/a parameter is a clinic.Parameter instance.
2443n/a data is a CRenderData instance.
2444n/a """
2445n/a self._render_self(parameter, data)
2446n/a self._render_non_self(parameter, data)
2447n/a
2448n/a def length_name(self):
2449n/a """Computes the name of the associated "length" variable."""
2450n/a if not self.length:
2451n/a return None
2452n/a return ensure_legal_c_identifier(self.name) + "_length"
2453n/a
2454n/a # Why is this one broken out separately?
2455n/a # For "positional-only" function parsing,
2456n/a # which generates a bunch of PyArg_ParseTuple calls.
2457n/a def parse_argument(self, list):
2458n/a assert not (self.converter and self.encoding)
2459n/a if self.format_unit == 'O&':
2460n/a assert self.converter
2461n/a list.append(self.converter)
2462n/a
2463n/a if self.encoding:
2464n/a list.append(c_repr(self.encoding))
2465n/a elif self.subclass_of:
2466n/a list.append(self.subclass_of)
2467n/a
2468n/a legal_name = ensure_legal_c_identifier(self.name)
2469n/a s = ("&" if self.parse_by_reference else "") + legal_name
2470n/a list.append(s)
2471n/a
2472n/a if self.length:
2473n/a list.append("&" + self.length_name())
2474n/a
2475n/a #
2476n/a # All the functions after here are intended as extension points.
2477n/a #
2478n/a
2479n/a def simple_declaration(self, by_reference=False):
2480n/a """
2481n/a Computes the basic declaration of the variable.
2482n/a Used in computing the prototype declaration and the
2483n/a variable declaration.
2484n/a """
2485n/a prototype = [self.type]
2486n/a if by_reference or not self.type.endswith('*'):
2487n/a prototype.append(" ")
2488n/a if by_reference:
2489n/a prototype.append('*')
2490n/a prototype.append(ensure_legal_c_identifier(self.name))
2491n/a return "".join(prototype)
2492n/a
2493n/a def declaration(self):
2494n/a """
2495n/a The C statement to declare this variable.
2496n/a """
2497n/a declaration = [self.simple_declaration()]
2498n/a default = self.c_default
2499n/a if not default and self.parameter.group:
2500n/a default = self.c_ignored_default
2501n/a if default:
2502n/a declaration.append(" = ")
2503n/a declaration.append(default)
2504n/a declaration.append(";")
2505n/a if self.length:
2506n/a declaration.append('\nPy_ssize_clean_t ')
2507n/a declaration.append(self.length_name())
2508n/a declaration.append(';')
2509n/a return "".join(declaration)
2510n/a
2511n/a def initialize(self):
2512n/a """
2513n/a The C statements required to set up this variable before parsing.
2514n/a Returns a string containing this code indented at column 0.
2515n/a If no initialization is necessary, returns an empty string.
2516n/a """
2517n/a return ""
2518n/a
2519n/a def modify(self):
2520n/a """
2521n/a The C statements required to modify this variable after parsing.
2522n/a Returns a string containing this code indented at column 0.
2523n/a If no initialization is necessary, returns an empty string.
2524n/a """
2525n/a return ""
2526n/a
2527n/a def cleanup(self):
2528n/a """
2529n/a The C statements required to clean up after this variable.
2530n/a Returns a string containing this code indented at column 0.
2531n/a If no cleanup is necessary, returns an empty string.
2532n/a """
2533n/a return ""
2534n/a
2535n/a def pre_render(self):
2536n/a """
2537n/a A second initialization function, like converter_init,
2538n/a called just before rendering.
2539n/a You are permitted to examine self.function here.
2540n/a """
2541n/a pass
2542n/a
2543n/a
2544n/aclass bool_converter(CConverter):
2545n/a type = 'int'
2546n/a default_type = bool
2547n/a format_unit = 'p'
2548n/a c_ignored_default = '0'
2549n/a
2550n/a def converter_init(self):
2551n/a if self.default is not unspecified:
2552n/a self.default = bool(self.default)
2553n/a self.c_default = str(int(self.default))
2554n/a
2555n/aclass char_converter(CConverter):
2556n/a type = 'char'
2557n/a default_type = (bytes, bytearray)
2558n/a format_unit = 'c'
2559n/a c_ignored_default = "'\0'"
2560n/a
2561n/a def converter_init(self):
2562n/a if isinstance(self.default, self.default_type) and (len(self.default) != 1):
2563n/a fail("char_converter: illegal default value " + repr(self.default))
2564n/a
2565n/a
2566n/a@add_legacy_c_converter('B', bitwise=True)
2567n/aclass unsigned_char_converter(CConverter):
2568n/a type = 'unsigned char'
2569n/a default_type = int
2570n/a format_unit = 'b'
2571n/a c_ignored_default = "'\0'"
2572n/a
2573n/a def converter_init(self, *, bitwise=False):
2574n/a if bitwise:
2575n/a self.format_unit = 'B'
2576n/a
2577n/aclass byte_converter(unsigned_char_converter): pass
2578n/a
2579n/aclass short_converter(CConverter):
2580n/a type = 'short'
2581n/a default_type = int
2582n/a format_unit = 'h'
2583n/a c_ignored_default = "0"
2584n/a
2585n/aclass unsigned_short_converter(CConverter):
2586n/a type = 'unsigned short'
2587n/a default_type = int
2588n/a format_unit = 'H'
2589n/a c_ignored_default = "0"
2590n/a
2591n/a def converter_init(self, *, bitwise=False):
2592n/a if not bitwise:
2593n/a fail("Unsigned shorts must be bitwise (for now).")
2594n/a
2595n/a@add_legacy_c_converter('C', accept={str})
2596n/aclass int_converter(CConverter):
2597n/a type = 'int'
2598n/a default_type = int
2599n/a format_unit = 'i'
2600n/a c_ignored_default = "0"
2601n/a
2602n/a def converter_init(self, *, accept={int}, type=None):
2603n/a if accept == {str}:
2604n/a self.format_unit = 'C'
2605n/a elif accept != {int}:
2606n/a fail("int_converter: illegal 'accept' argument " + repr(accept))
2607n/a if type != None:
2608n/a self.type = type
2609n/a
2610n/aclass unsigned_int_converter(CConverter):
2611n/a type = 'unsigned int'
2612n/a default_type = int
2613n/a format_unit = 'I'
2614n/a c_ignored_default = "0"
2615n/a
2616n/a def converter_init(self, *, bitwise=False):
2617n/a if not bitwise:
2618n/a fail("Unsigned ints must be bitwise (for now).")
2619n/a
2620n/aclass long_converter(CConverter):
2621n/a type = 'long'
2622n/a default_type = int
2623n/a format_unit = 'l'
2624n/a c_ignored_default = "0"
2625n/a
2626n/aclass unsigned_long_converter(CConverter):
2627n/a type = 'unsigned long'
2628n/a default_type = int
2629n/a format_unit = 'k'
2630n/a c_ignored_default = "0"
2631n/a
2632n/a def converter_init(self, *, bitwise=False):
2633n/a if not bitwise:
2634n/a fail("Unsigned longs must be bitwise (for now).")
2635n/a
2636n/aclass long_long_converter(CConverter):
2637n/a type = 'long long'
2638n/a default_type = int
2639n/a format_unit = 'L'
2640n/a c_ignored_default = "0"
2641n/a
2642n/aclass unsigned_long_long_converter(CConverter):
2643n/a type = 'unsigned long long'
2644n/a default_type = int
2645n/a format_unit = 'K'
2646n/a c_ignored_default = "0"
2647n/a
2648n/a def converter_init(self, *, bitwise=False):
2649n/a if not bitwise:
2650n/a fail("Unsigned long long must be bitwise (for now).")
2651n/a
2652n/aclass Py_ssize_t_converter(CConverter):
2653n/a type = 'Py_ssize_t'
2654n/a default_type = int
2655n/a format_unit = 'n'
2656n/a c_ignored_default = "0"
2657n/a
2658n/a
2659n/aclass float_converter(CConverter):
2660n/a type = 'float'
2661n/a default_type = float
2662n/a format_unit = 'f'
2663n/a c_ignored_default = "0.0"
2664n/a
2665n/aclass double_converter(CConverter):
2666n/a type = 'double'
2667n/a default_type = float
2668n/a format_unit = 'd'
2669n/a c_ignored_default = "0.0"
2670n/a
2671n/a
2672n/aclass Py_complex_converter(CConverter):
2673n/a type = 'Py_complex'
2674n/a default_type = complex
2675n/a format_unit = 'D'
2676n/a c_ignored_default = "{0.0, 0.0}"
2677n/a
2678n/a
2679n/aclass object_converter(CConverter):
2680n/a type = 'PyObject *'
2681n/a format_unit = 'O'
2682n/a
2683n/a def converter_init(self, *, converter=None, type=None, subclass_of=None):
2684n/a if converter:
2685n/a if subclass_of:
2686n/a fail("object: Cannot pass in both 'converter' and 'subclass_of'")
2687n/a self.format_unit = 'O&'
2688n/a self.converter = converter
2689n/a elif subclass_of:
2690n/a self.format_unit = 'O!'
2691n/a self.subclass_of = subclass_of
2692n/a
2693n/a if type is not None:
2694n/a self.type = type
2695n/a
2696n/a
2697n/a#
2698n/a# We define three conventions for buffer types in the 'accept' argument:
2699n/a#
2700n/a# buffer : any object supporting the buffer interface
2701n/a# rwbuffer: any object supporting the buffer interface, but must be writeable
2702n/a# robuffer: any object supporting the buffer interface, but must not be writeable
2703n/a#
2704n/a
2705n/aclass buffer: pass
2706n/aclass rwbuffer: pass
2707n/aclass robuffer: pass
2708n/a
2709n/adef str_converter_key(types, encoding, zeroes):
2710n/a return (frozenset(types), bool(encoding), bool(zeroes))
2711n/a
2712n/astr_converter_argument_map = {}
2713n/a
2714n/aclass str_converter(CConverter):
2715n/a type = 'const char *'
2716n/a default_type = (str, Null, NoneType)
2717n/a format_unit = 's'
2718n/a
2719n/a def converter_init(self, *, accept={str}, encoding=None, zeroes=False):
2720n/a
2721n/a key = str_converter_key(accept, encoding, zeroes)
2722n/a format_unit = str_converter_argument_map.get(key)
2723n/a if not format_unit:
2724n/a fail("str_converter: illegal combination of arguments", key)
2725n/a
2726n/a self.format_unit = format_unit
2727n/a self.length = bool(zeroes)
2728n/a if encoding:
2729n/a if self.default not in (Null, None, unspecified):
2730n/a fail("str_converter: Argument Clinic doesn't support default values for encoded strings")
2731n/a self.encoding = encoding
2732n/a self.type = 'char *'
2733n/a # sorry, clinic can't support preallocated buffers
2734n/a # for es# and et#
2735n/a self.c_default = "NULL"
2736n/a
2737n/a def cleanup(self):
2738n/a if self.encoding:
2739n/a name = ensure_legal_c_identifier(self.name)
2740n/a return "".join(["if (", name, ") {\n PyMem_FREE(", name, ");\n}\n"])
2741n/a
2742n/a#
2743n/a# This is the fourth or fifth rewrite of registering all the
2744n/a# crazy string converter format units. Previous approaches hid
2745n/a# bugs--generally mismatches between the semantics of the format
2746n/a# unit and the arguments necessary to represent those semantics
2747n/a# properly. Hopefully with this approach we'll get it 100% right.
2748n/a#
2749n/a# The r() function (short for "register") both registers the
2750n/a# mapping from arguments to format unit *and* registers the
2751n/a# legacy C converter for that format unit.
2752n/a#
2753n/adef r(format_unit, *, accept, encoding=False, zeroes=False):
2754n/a if not encoding and format_unit != 's':
2755n/a # add the legacy c converters here too.
2756n/a #
2757n/a # note: add_legacy_c_converter can't work for
2758n/a # es, es#, et, or et#
2759n/a # because of their extra encoding argument
2760n/a #
2761n/a # also don't add the converter for 's' because
2762n/a # the metaclass for CConverter adds it for us.
2763n/a kwargs = {}
2764n/a if accept != {str}:
2765n/a kwargs['accept'] = accept
2766n/a if zeroes:
2767n/a kwargs['zeroes'] = True
2768n/a added_f = functools.partial(str_converter, **kwargs)
2769n/a legacy_converters[format_unit] = added_f
2770n/a
2771n/a d = str_converter_argument_map
2772n/a key = str_converter_key(accept, encoding, zeroes)
2773n/a if key in d:
2774n/a sys.exit("Duplicate keys specified for str_converter_argument_map!")
2775n/a d[key] = format_unit
2776n/a
2777n/ar('es', encoding=True, accept={str})
2778n/ar('es#', encoding=True, zeroes=True, accept={str})
2779n/ar('et', encoding=True, accept={bytes, bytearray, str})
2780n/ar('et#', encoding=True, zeroes=True, accept={bytes, bytearray, str})
2781n/ar('s', accept={str})
2782n/ar('s#', zeroes=True, accept={robuffer, str})
2783n/ar('y', accept={robuffer})
2784n/ar('y#', zeroes=True, accept={robuffer})
2785n/ar('z', accept={str, NoneType})
2786n/ar('z#', zeroes=True, accept={robuffer, str, NoneType})
2787n/adel r
2788n/a
2789n/a
2790n/aclass PyBytesObject_converter(CConverter):
2791n/a type = 'PyBytesObject *'
2792n/a format_unit = 'S'
2793n/a # accept = {bytes}
2794n/a
2795n/aclass PyByteArrayObject_converter(CConverter):
2796n/a type = 'PyByteArrayObject *'
2797n/a format_unit = 'Y'
2798n/a # accept = {bytearray}
2799n/a
2800n/aclass unicode_converter(CConverter):
2801n/a type = 'PyObject *'
2802n/a default_type = (str, Null, NoneType)
2803n/a format_unit = 'U'
2804n/a
2805n/a@add_legacy_c_converter('u#', zeroes=True)
2806n/a@add_legacy_c_converter('Z', accept={str, NoneType})
2807n/a@add_legacy_c_converter('Z#', accept={str, NoneType}, zeroes=True)
2808n/aclass Py_UNICODE_converter(CConverter):
2809n/a type = 'Py_UNICODE *'
2810n/a default_type = (str, Null, NoneType)
2811n/a format_unit = 'u'
2812n/a
2813n/a def converter_init(self, *, accept={str}, zeroes=False):
2814n/a format_unit = 'Z' if accept=={str, NoneType} else 'u'
2815n/a if zeroes:
2816n/a format_unit += '#'
2817n/a self.length = True
2818n/a self.format_unit = format_unit
2819n/a
2820n/a@add_legacy_c_converter('s*', accept={str, buffer})
2821n/a@add_legacy_c_converter('z*', accept={str, buffer, NoneType})
2822n/a@add_legacy_c_converter('w*', accept={rwbuffer})
2823n/aclass Py_buffer_converter(CConverter):
2824n/a type = 'Py_buffer'
2825n/a format_unit = 'y*'
2826n/a impl_by_reference = True
2827n/a c_ignored_default = "{NULL, NULL}"
2828n/a
2829n/a def converter_init(self, *, accept={buffer}):
2830n/a if self.default not in (unspecified, None):
2831n/a fail("The only legal default value for Py_buffer is None.")
2832n/a
2833n/a self.c_default = self.c_ignored_default
2834n/a
2835n/a if accept == {str, buffer, NoneType}:
2836n/a format_unit = 'z*'
2837n/a elif accept == {str, buffer}:
2838n/a format_unit = 's*'
2839n/a elif accept == {buffer}:
2840n/a format_unit = 'y*'
2841n/a elif accept == {rwbuffer}:
2842n/a format_unit = 'w*'
2843n/a else:
2844n/a fail("Py_buffer_converter: illegal combination of arguments")
2845n/a
2846n/a self.format_unit = format_unit
2847n/a
2848n/a def cleanup(self):
2849n/a name = ensure_legal_c_identifier(self.name)
2850n/a return "".join(["if (", name, ".obj) {\n PyBuffer_Release(&", name, ");\n}\n"])
2851n/a
2852n/a
2853n/adef correct_name_for_self(f):
2854n/a if f.kind in (CALLABLE, METHOD_INIT):
2855n/a if f.cls:
2856n/a return "PyObject *", "self"
2857n/a return "PyObject *", "module"
2858n/a if f.kind == STATIC_METHOD:
2859n/a return "void *", "null"
2860n/a if f.kind in (CLASS_METHOD, METHOD_NEW):
2861n/a return "PyTypeObject *", "type"
2862n/a raise RuntimeError("Unhandled type of function f: " + repr(f.kind))
2863n/a
2864n/adef required_type_for_self_for_parser(f):
2865n/a type, _ = correct_name_for_self(f)
2866n/a if f.kind in (METHOD_INIT, METHOD_NEW, STATIC_METHOD, CLASS_METHOD):
2867n/a return type
2868n/a return None
2869n/a
2870n/a
2871n/aclass self_converter(CConverter):
2872n/a """
2873n/a A special-case converter:
2874n/a this is the default converter used for "self".
2875n/a """
2876n/a type = None
2877n/a format_unit = ''
2878n/a
2879n/a def converter_init(self, *, type=None):
2880n/a self.specified_type = type
2881n/a
2882n/a def pre_render(self):
2883n/a f = self.function
2884n/a default_type, default_name = correct_name_for_self(f)
2885n/a self.signature_name = default_name
2886n/a self.type = self.specified_type or self.type or default_type
2887n/a
2888n/a kind = self.function.kind
2889n/a new_or_init = kind in (METHOD_NEW, METHOD_INIT)
2890n/a
2891n/a if (kind == STATIC_METHOD) or new_or_init:
2892n/a self.show_in_signature = False
2893n/a
2894n/a # tp_new (METHOD_NEW) functions are of type newfunc:
2895n/a # typedef PyObject *(*newfunc)(struct _typeobject *, PyObject *, PyObject *);
2896n/a # PyTypeObject is a typedef for struct _typeobject.
2897n/a #
2898n/a # tp_init (METHOD_INIT) functions are of type initproc:
2899n/a # typedef int (*initproc)(PyObject *, PyObject *, PyObject *);
2900n/a #
2901n/a # All other functions generated by Argument Clinic are stored in
2902n/a # PyMethodDef structures, in the ml_meth slot, which is of type PyCFunction:
2903n/a # typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
2904n/a # However! We habitually cast these functions to PyCFunction,
2905n/a # since functions that accept keyword arguments don't fit this signature
2906n/a # but are stored there anyway. So strict type equality isn't important
2907n/a # for these functions.
2908n/a #
2909n/a # So:
2910n/a #
2911n/a # * The name of the first parameter to the impl and the parsing function will always
2912n/a # be self.name.
2913n/a #
2914n/a # * The type of the first parameter to the impl will always be of self.type.
2915n/a #
2916n/a # * If the function is neither tp_new (METHOD_NEW) nor tp_init (METHOD_INIT):
2917n/a # * The type of the first parameter to the parsing function is also self.type.
2918n/a # This means that if you step into the parsing function, your "self" parameter
2919n/a # is of the correct type, which may make debugging more pleasant.
2920n/a #
2921n/a # * Else if the function is tp_new (METHOD_NEW):
2922n/a # * The type of the first parameter to the parsing function is "PyTypeObject *",
2923n/a # so the type signature of the function call is an exact match.
2924n/a # * If self.type != "PyTypeObject *", we cast the first parameter to self.type
2925n/a # in the impl call.
2926n/a #
2927n/a # * Else if the function is tp_init (METHOD_INIT):
2928n/a # * The type of the first parameter to the parsing function is "PyObject *",
2929n/a # so the type signature of the function call is an exact match.
2930n/a # * If self.type != "PyObject *", we cast the first parameter to self.type
2931n/a # in the impl call.
2932n/a
2933n/a @property
2934n/a def parser_type(self):
2935n/a return required_type_for_self_for_parser(self.function) or self.type
2936n/a
2937n/a def render(self, parameter, data):
2938n/a """
2939n/a parameter is a clinic.Parameter instance.
2940n/a data is a CRenderData instance.
2941n/a """
2942n/a if self.function.kind == STATIC_METHOD:
2943n/a return
2944n/a
2945n/a self._render_self(parameter, data)
2946n/a
2947n/a if self.type != self.parser_type:
2948n/a # insert cast to impl_argument[0], aka self.
2949n/a # we know we're in the first slot in all the CRenderData lists,
2950n/a # because we render parameters in order, and self is always first.
2951n/a assert len(data.impl_arguments) == 1
2952n/a assert data.impl_arguments[0] == self.name
2953n/a data.impl_arguments[0] = '(' + self.type + ")" + data.impl_arguments[0]
2954n/a
2955n/a def set_template_dict(self, template_dict):
2956n/a template_dict['self_name'] = self.name
2957n/a template_dict['self_type'] = self.parser_type
2958n/a kind = self.function.kind
2959n/a cls = self.function.cls
2960n/a
2961n/a if ((kind in (METHOD_NEW, METHOD_INIT)) and cls and cls.typedef):
2962n/a if kind == METHOD_NEW:
2963n/a passed_in_type = self.name
2964n/a else:
2965n/a passed_in_type = 'Py_TYPE({})'.format(self.name)
2966n/a
2967n/a line = '({passed_in_type} == {type_object}) &&\n '
2968n/a d = {
2969n/a 'type_object': self.function.cls.type_object,
2970n/a 'passed_in_type': passed_in_type
2971n/a }
2972n/a template_dict['self_type_check'] = line.format_map(d)
2973n/a
2974n/a
2975n/a
2976n/adef add_c_return_converter(f, name=None):
2977n/a if not name:
2978n/a name = f.__name__
2979n/a if not name.endswith('_return_converter'):
2980n/a return f
2981n/a name = name[:-len('_return_converter')]
2982n/a return_converters[name] = f
2983n/a return f
2984n/a
2985n/a
2986n/aclass CReturnConverterAutoRegister(type):
2987n/a def __init__(cls, name, bases, classdict):
2988n/a add_c_return_converter(cls)
2989n/a
2990n/aclass CReturnConverter(metaclass=CReturnConverterAutoRegister):
2991n/a
2992n/a # The C type to use for this variable.
2993n/a # 'type' should be a Python string specifying the type, e.g. "int".
2994n/a # If this is a pointer type, the type string should end with ' *'.
2995n/a type = 'PyObject *'
2996n/a
2997n/a # The Python default value for this parameter, as a Python value.
2998n/a # Or the magic value "unspecified" if there is no default.
2999n/a default = None
3000n/a
3001n/a def __init__(self, *, py_default=None, **kwargs):
3002n/a self.py_default = py_default
3003n/a try:
3004n/a self.return_converter_init(**kwargs)
3005n/a except TypeError as e:
3006n/a s = ', '.join(name + '=' + repr(value) for name, value in kwargs.items())
3007n/a sys.exit(self.__class__.__name__ + '(' + s + ')\n' + str(e))
3008n/a
3009n/a def return_converter_init(self):
3010n/a pass
3011n/a
3012n/a def declare(self, data, name="_return_value"):
3013n/a line = []
3014n/a add = line.append
3015n/a add(self.type)
3016n/a if not self.type.endswith('*'):
3017n/a add(' ')
3018n/a add(name + ';')
3019n/a data.declarations.append(''.join(line))
3020n/a data.return_value = name
3021n/a
3022n/a def err_occurred_if(self, expr, data):
3023n/a data.return_conversion.append('if (({}) && PyErr_Occurred()) {{\n goto exit;\n}}\n'.format(expr))
3024n/a
3025n/a def err_occurred_if_null_pointer(self, variable, data):
3026n/a data.return_conversion.append('if ({} == NULL) {{\n goto exit;\n}}\n'.format(variable))
3027n/a
3028n/a def render(self, function, data):
3029n/a """
3030n/a function is a clinic.Function instance.
3031n/a data is a CRenderData instance.
3032n/a """
3033n/a pass
3034n/a
3035n/aadd_c_return_converter(CReturnConverter, 'object')
3036n/a
3037n/aclass NoneType_return_converter(CReturnConverter):
3038n/a def render(self, function, data):
3039n/a self.declare(data)
3040n/a data.return_conversion.append('''
3041n/aif (_return_value != Py_None) {
3042n/a goto exit;
3043n/a}
3044n/areturn_value = Py_None;
3045n/aPy_INCREF(Py_None);
3046n/a'''.strip())
3047n/a
3048n/aclass bool_return_converter(CReturnConverter):
3049n/a type = 'int'
3050n/a
3051n/a def render(self, function, data):
3052n/a self.declare(data)
3053n/a self.err_occurred_if("_return_value == -1", data)
3054n/a data.return_conversion.append('return_value = PyBool_FromLong((long)_return_value);\n')
3055n/a
3056n/aclass long_return_converter(CReturnConverter):
3057n/a type = 'long'
3058n/a conversion_fn = 'PyLong_FromLong'
3059n/a cast = ''
3060n/a unsigned_cast = ''
3061n/a
3062n/a def render(self, function, data):
3063n/a self.declare(data)
3064n/a self.err_occurred_if("_return_value == {}-1".format(self.unsigned_cast), data)
3065n/a data.return_conversion.append(
3066n/a ''.join(('return_value = ', self.conversion_fn, '(', self.cast, '_return_value);\n')))
3067n/a
3068n/aclass int_return_converter(long_return_converter):
3069n/a type = 'int'
3070n/a cast = '(long)'
3071n/a
3072n/aclass init_return_converter(long_return_converter):
3073n/a """
3074n/a Special return converter for __init__ functions.
3075n/a """
3076n/a type = 'int'
3077n/a cast = '(long)'
3078n/a
3079n/a def render(self, function, data):
3080n/a pass
3081n/a
3082n/aclass unsigned_long_return_converter(long_return_converter):
3083n/a type = 'unsigned long'
3084n/a conversion_fn = 'PyLong_FromUnsignedLong'
3085n/a unsigned_cast = '(unsigned long)'
3086n/a
3087n/aclass unsigned_int_return_converter(unsigned_long_return_converter):
3088n/a type = 'unsigned int'
3089n/a cast = '(unsigned long)'
3090n/a unsigned_cast = '(unsigned int)'
3091n/a
3092n/aclass Py_ssize_t_return_converter(long_return_converter):
3093n/a type = 'Py_ssize_t'
3094n/a conversion_fn = 'PyLong_FromSsize_t'
3095n/a
3096n/aclass size_t_return_converter(long_return_converter):
3097n/a type = 'size_t'
3098n/a conversion_fn = 'PyLong_FromSize_t'
3099n/a unsigned_cast = '(size_t)'
3100n/a
3101n/a
3102n/aclass double_return_converter(CReturnConverter):
3103n/a type = 'double'
3104n/a cast = ''
3105n/a
3106n/a def render(self, function, data):
3107n/a self.declare(data)
3108n/a self.err_occurred_if("_return_value == -1.0", data)
3109n/a data.return_conversion.append(
3110n/a 'return_value = PyFloat_FromDouble(' + self.cast + '_return_value);\n')
3111n/a
3112n/aclass float_return_converter(double_return_converter):
3113n/a type = 'float'
3114n/a cast = '(double)'
3115n/a
3116n/a
3117n/aclass DecodeFSDefault_return_converter(CReturnConverter):
3118n/a type = 'char *'
3119n/a
3120n/a def render(self, function, data):
3121n/a self.declare(data)
3122n/a self.err_occurred_if_null_pointer("_return_value", data)
3123n/a data.return_conversion.append(
3124n/a 'return_value = PyUnicode_DecodeFSDefault(_return_value);\n')
3125n/a
3126n/a
3127n/adef eval_ast_expr(node, globals, *, filename='-'):
3128n/a """
3129n/a Takes an ast.Expr node. Compiles and evaluates it.
3130n/a Returns the result of the expression.
3131n/a
3132n/a globals represents the globals dict the expression
3133n/a should see. (There's no equivalent for "locals" here.)
3134n/a """
3135n/a
3136n/a if isinstance(node, ast.Expr):
3137n/a node = node.value
3138n/a
3139n/a node = ast.Expression(node)
3140n/a co = compile(node, filename, 'eval')
3141n/a fn = types.FunctionType(co, globals)
3142n/a return fn()
3143n/a
3144n/a
3145n/aclass IndentStack:
3146n/a def __init__(self):
3147n/a self.indents = []
3148n/a self.margin = None
3149n/a
3150n/a def _ensure(self):
3151n/a if not self.indents:
3152n/a fail('IndentStack expected indents, but none are defined.')
3153n/a
3154n/a def measure(self, line):
3155n/a """
3156n/a Returns the length of the line's margin.
3157n/a """
3158n/a if '\t' in line:
3159n/a fail('Tab characters are illegal in the Argument Clinic DSL.')
3160n/a stripped = line.lstrip()
3161n/a if not len(stripped):
3162n/a # we can't tell anything from an empty line
3163n/a # so just pretend it's indented like our current indent
3164n/a self._ensure()
3165n/a return self.indents[-1]
3166n/a return len(line) - len(stripped)
3167n/a
3168n/a def infer(self, line):
3169n/a """
3170n/a Infer what is now the current margin based on this line.
3171n/a Returns:
3172n/a 1 if we have indented (or this is the first margin)
3173n/a 0 if the margin has not changed
3174n/a -N if we have dedented N times
3175n/a """
3176n/a indent = self.measure(line)
3177n/a margin = ' ' * indent
3178n/a if not self.indents:
3179n/a self.indents.append(indent)
3180n/a self.margin = margin
3181n/a return 1
3182n/a current = self.indents[-1]
3183n/a if indent == current:
3184n/a return 0
3185n/a if indent > current:
3186n/a self.indents.append(indent)
3187n/a self.margin = margin
3188n/a return 1
3189n/a # indent < current
3190n/a if indent not in self.indents:
3191n/a fail("Illegal outdent.")
3192n/a outdent_count = 0
3193n/a while indent != current:
3194n/a self.indents.pop()
3195n/a current = self.indents[-1]
3196n/a outdent_count -= 1
3197n/a self.margin = margin
3198n/a return outdent_count
3199n/a
3200n/a @property
3201n/a def depth(self):
3202n/a """
3203n/a Returns how many margins are currently defined.
3204n/a """
3205n/a return len(self.indents)
3206n/a
3207n/a def indent(self, line):
3208n/a """
3209n/a Indents a line by the currently defined margin.
3210n/a """
3211n/a return self.margin + line
3212n/a
3213n/a def dedent(self, line):
3214n/a """
3215n/a Dedents a line by the currently defined margin.
3216n/a (The inverse of 'indent'.)
3217n/a """
3218n/a margin = self.margin
3219n/a indent = self.indents[-1]
3220n/a if not line.startswith(margin):
3221n/a fail('Cannot dedent, line does not start with the previous margin:')
3222n/a return line[indent:]
3223n/a
3224n/a
3225n/aclass DSLParser:
3226n/a def __init__(self, clinic):
3227n/a self.clinic = clinic
3228n/a
3229n/a self.directives = {}
3230n/a for name in dir(self):
3231n/a # functions that start with directive_ are added to directives
3232n/a _, s, key = name.partition("directive_")
3233n/a if s:
3234n/a self.directives[key] = getattr(self, name)
3235n/a
3236n/a # functions that start with at_ are too, with an @ in front
3237n/a _, s, key = name.partition("at_")
3238n/a if s:
3239n/a self.directives['@' + key] = getattr(self, name)
3240n/a
3241n/a self.reset()
3242n/a
3243n/a def reset(self):
3244n/a self.function = None
3245n/a self.state = self.state_dsl_start
3246n/a self.parameter_indent = None
3247n/a self.keyword_only = False
3248n/a self.positional_only = False
3249n/a self.group = 0
3250n/a self.parameter_state = self.ps_start
3251n/a self.seen_positional_with_default = False
3252n/a self.indent = IndentStack()
3253n/a self.kind = CALLABLE
3254n/a self.coexist = False
3255n/a self.parameter_continuation = ''
3256n/a self.preserve_output = False
3257n/a
3258n/a def directive_version(self, required):
3259n/a global version
3260n/a if version_comparitor(version, required) < 0:
3261n/a fail("Insufficient Clinic version!\n Version: " + version + "\n Required: " + required)
3262n/a
3263n/a def directive_module(self, name):
3264n/a fields = name.split('.')
3265n/a new = fields.pop()
3266n/a module, cls = self.clinic._module_and_class(fields)
3267n/a if cls:
3268n/a fail("Can't nest a module inside a class!")
3269n/a
3270n/a if name in module.classes:
3271n/a fail("Already defined module " + repr(name) + "!")
3272n/a
3273n/a m = Module(name, module)
3274n/a module.modules[name] = m
3275n/a self.block.signatures.append(m)
3276n/a
3277n/a def directive_class(self, name, typedef, type_object):
3278n/a fields = name.split('.')
3279n/a in_classes = False
3280n/a parent = self
3281n/a name = fields.pop()
3282n/a so_far = []
3283n/a module, cls = self.clinic._module_and_class(fields)
3284n/a
3285n/a parent = cls or module
3286n/a if name in parent.classes:
3287n/a fail("Already defined class " + repr(name) + "!")
3288n/a
3289n/a c = Class(name, module, cls, typedef, type_object)
3290n/a parent.classes[name] = c
3291n/a self.block.signatures.append(c)
3292n/a
3293n/a def directive_set(self, name, value):
3294n/a if name not in ("line_prefix", "line_suffix"):
3295n/a fail("unknown variable", repr(name))
3296n/a
3297n/a value = value.format_map({
3298n/a 'block comment start': '/*',
3299n/a 'block comment end': '*/',
3300n/a })
3301n/a
3302n/a self.clinic.__dict__[name] = value
3303n/a
3304n/a def directive_destination(self, name, command, *args):
3305n/a if command == 'new':
3306n/a self.clinic.add_destination(name, *args)
3307n/a return
3308n/a
3309n/a if command == 'clear':
3310n/a self.clinic.get_destination(name).clear()
3311n/a fail("unknown destination command", repr(command))
3312n/a
3313n/a
3314n/a def directive_output(self, command_or_name, destination=''):
3315n/a fd = self.clinic.destination_buffers
3316n/a
3317n/a if command_or_name == "preset":
3318n/a preset = self.clinic.presets.get(destination)
3319n/a if not preset:
3320n/a fail("Unknown preset " + repr(destination) + "!")
3321n/a fd.update(preset)
3322n/a return
3323n/a
3324n/a if command_or_name == "push":
3325n/a self.clinic.destination_buffers_stack.append(fd.copy())
3326n/a return
3327n/a
3328n/a if command_or_name == "pop":
3329n/a if not self.clinic.destination_buffers_stack:
3330n/a fail("Can't 'output pop', stack is empty!")
3331n/a previous_fd = self.clinic.destination_buffers_stack.pop()
3332n/a fd.update(previous_fd)
3333n/a return
3334n/a
3335n/a # secret command for debugging!
3336n/a if command_or_name == "print":
3337n/a self.block.output.append(pprint.pformat(fd))
3338n/a self.block.output.append('\n')
3339n/a return
3340n/a
3341n/a d = self.clinic.get_destination(destination)
3342n/a
3343n/a if command_or_name == "everything":
3344n/a for name in list(fd):
3345n/a fd[name] = d
3346n/a return
3347n/a
3348n/a if command_or_name not in fd:
3349n/a fail("Invalid command / destination name " + repr(command_or_name) + ", must be one of:\n preset push pop print everything " + " ".join(fd))
3350n/a fd[command_or_name] = d
3351n/a
3352n/a def directive_dump(self, name):
3353n/a self.block.output.append(self.clinic.get_destination(name).dump())
3354n/a
3355n/a def directive_print(self, *args):
3356n/a self.block.output.append(' '.join(args))
3357n/a self.block.output.append('\n')
3358n/a
3359n/a def directive_preserve(self):
3360n/a if self.preserve_output:
3361n/a fail("Can't have preserve twice in one block!")
3362n/a self.preserve_output = True
3363n/a
3364n/a def at_classmethod(self):
3365n/a if self.kind is not CALLABLE:
3366n/a fail("Can't set @classmethod, function is not a normal callable")
3367n/a self.kind = CLASS_METHOD
3368n/a
3369n/a def at_staticmethod(self):
3370n/a if self.kind is not CALLABLE:
3371n/a fail("Can't set @staticmethod, function is not a normal callable")
3372n/a self.kind = STATIC_METHOD
3373n/a
3374n/a def at_coexist(self):
3375n/a if self.coexist:
3376n/a fail("Called @coexist twice!")
3377n/a self.coexist = True
3378n/a
3379n/a def parse(self, block):
3380n/a self.reset()
3381n/a self.block = block
3382n/a self.saved_output = self.block.output
3383n/a block.output = []
3384n/a block_start = self.clinic.block_parser.line_number
3385n/a lines = block.input.split('\n')
3386n/a for line_number, line in enumerate(lines, self.clinic.block_parser.block_start_line_number):
3387n/a if '\t' in line:
3388n/a fail('Tab characters are illegal in the Clinic DSL.\n\t' + repr(line), line_number=block_start)
3389n/a self.state(line)
3390n/a
3391n/a self.next(self.state_terminal)
3392n/a self.state(None)
3393n/a
3394n/a block.output.extend(self.clinic.language.render(clinic, block.signatures))
3395n/a
3396n/a if self.preserve_output:
3397n/a if block.output:
3398n/a fail("'preserve' only works for blocks that don't produce any output!")
3399n/a block.output = self.saved_output
3400n/a
3401n/a @staticmethod
3402n/a def ignore_line(line):
3403n/a # ignore comment-only lines
3404n/a if line.lstrip().startswith('#'):
3405n/a return True
3406n/a
3407n/a # Ignore empty lines too
3408n/a # (but not in docstring sections!)
3409n/a if not line.strip():
3410n/a return True
3411n/a
3412n/a return False
3413n/a
3414n/a @staticmethod
3415n/a def calculate_indent(line):
3416n/a return len(line) - len(line.strip())
3417n/a
3418n/a def next(self, state, line=None):
3419n/a # real_print(self.state.__name__, "->", state.__name__, ", line=", line)
3420n/a self.state = state
3421n/a if line is not None:
3422n/a self.state(line)
3423n/a
3424n/a def state_dsl_start(self, line):
3425n/a # self.block = self.ClinicOutputBlock(self)
3426n/a if self.ignore_line(line):
3427n/a return
3428n/a
3429n/a # is it a directive?
3430n/a fields = shlex.split(line)
3431n/a directive_name = fields[0]
3432n/a directive = self.directives.get(directive_name, None)
3433n/a if directive:
3434n/a try:
3435n/a directive(*fields[1:])
3436n/a except TypeError as e:
3437n/a fail(str(e))
3438n/a return
3439n/a
3440n/a self.next(self.state_modulename_name, line)
3441n/a
3442n/a def state_modulename_name(self, line):
3443n/a # looking for declaration, which establishes the leftmost column
3444n/a # line should be
3445n/a # modulename.fnname [as c_basename] [-> return annotation]
3446n/a # square brackets denote optional syntax.
3447n/a #
3448n/a # alternatively:
3449n/a # modulename.fnname [as c_basename] = modulename.existing_fn_name
3450n/a # clones the parameters and return converter from that
3451n/a # function. you can't modify them. you must enter a
3452n/a # new docstring.
3453n/a #
3454n/a # (but we might find a directive first!)
3455n/a #
3456n/a # this line is permitted to start with whitespace.
3457n/a # we'll call this number of spaces F (for "function").
3458n/a
3459n/a if not line.strip():
3460n/a return
3461n/a
3462n/a self.indent.infer(line)
3463n/a
3464n/a # are we cloning?
3465n/a before, equals, existing = line.rpartition('=')
3466n/a if equals:
3467n/a full_name, _, c_basename = before.partition(' as ')
3468n/a full_name = full_name.strip()
3469n/a c_basename = c_basename.strip()
3470n/a existing = existing.strip()
3471n/a if (is_legal_py_identifier(full_name) and
3472n/a (not c_basename or is_legal_c_identifier(c_basename)) and
3473n/a is_legal_py_identifier(existing)):
3474n/a # we're cloning!
3475n/a fields = [x.strip() for x in existing.split('.')]
3476n/a function_name = fields.pop()
3477n/a module, cls = self.clinic._module_and_class(fields)
3478n/a
3479n/a for existing_function in (cls or module).functions:
3480n/a if existing_function.name == function_name:
3481n/a break
3482n/a else:
3483n/a existing_function = None
3484n/a if not existing_function:
3485n/a print("class", cls, "module", module, "existing", existing)
3486n/a print("cls. functions", cls.functions)
3487n/a fail("Couldn't find existing function " + repr(existing) + "!")
3488n/a
3489n/a fields = [x.strip() for x in full_name.split('.')]
3490n/a function_name = fields.pop()
3491n/a module, cls = self.clinic._module_and_class(fields)
3492n/a
3493n/a if not (existing_function.kind == self.kind and existing_function.coexist == self.coexist):
3494n/a fail("'kind' of function and cloned function don't match! (@classmethod/@staticmethod/@coexist)")
3495n/a self.function = existing_function.copy(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename, docstring='')
3496n/a
3497n/a self.block.signatures.append(self.function)
3498n/a (cls or module).functions.append(self.function)
3499n/a self.next(self.state_function_docstring)
3500n/a return
3501n/a
3502n/a line, _, returns = line.partition('->')
3503n/a
3504n/a full_name, _, c_basename = line.partition(' as ')
3505n/a full_name = full_name.strip()
3506n/a c_basename = c_basename.strip() or None
3507n/a
3508n/a if not is_legal_py_identifier(full_name):
3509n/a fail("Illegal function name: {}".format(full_name))
3510n/a if c_basename and not is_legal_c_identifier(c_basename):
3511n/a fail("Illegal C basename: {}".format(c_basename))
3512n/a
3513n/a return_converter = None
3514n/a if returns:
3515n/a ast_input = "def x() -> {}: pass".format(returns)
3516n/a module = None
3517n/a try:
3518n/a module = ast.parse(ast_input)
3519n/a except SyntaxError:
3520n/a pass
3521n/a if not module:
3522n/a fail("Badly-formed annotation for " + full_name + ": " + returns)
3523n/a try:
3524n/a name, legacy, kwargs = self.parse_converter(module.body[0].returns)
3525n/a if legacy:
3526n/a fail("Legacy converter {!r} not allowed as a return converter"
3527n/a .format(name))
3528n/a if name not in return_converters:
3529n/a fail("No available return converter called " + repr(name))
3530n/a return_converter = return_converters[name](**kwargs)
3531n/a except ValueError:
3532n/a fail("Badly-formed annotation for " + full_name + ": " + returns)
3533n/a
3534n/a fields = [x.strip() for x in full_name.split('.')]
3535n/a function_name = fields.pop()
3536n/a module, cls = self.clinic._module_and_class(fields)
3537n/a
3538n/a fields = full_name.split('.')
3539n/a if fields[-1] == '__new__':
3540n/a if (self.kind != CLASS_METHOD) or (not cls):
3541n/a fail("__new__ must be a class method!")
3542n/a self.kind = METHOD_NEW
3543n/a elif fields[-1] == '__init__':
3544n/a if (self.kind != CALLABLE) or (not cls):
3545n/a fail("__init__ must be a normal method, not a class or static method!")
3546n/a self.kind = METHOD_INIT
3547n/a if not return_converter:
3548n/a return_converter = init_return_converter()
3549n/a elif fields[-1] in unsupported_special_methods:
3550n/a fail(fields[-1] + " is a special method and cannot be converted to Argument Clinic! (Yet.)")
3551n/a
3552n/a if not return_converter:
3553n/a return_converter = CReturnConverter()
3554n/a
3555n/a if not module:
3556n/a fail("Undefined module used in declaration of " + repr(full_name.strip()) + ".")
3557n/a self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,
3558n/a return_converter=return_converter, kind=self.kind, coexist=self.coexist)
3559n/a self.block.signatures.append(self.function)
3560n/a
3561n/a # insert a self converter automatically
3562n/a type, name = correct_name_for_self(self.function)
3563n/a kwargs = {}
3564n/a if cls and type == "PyObject *":
3565n/a kwargs['type'] = cls.typedef
3566n/a sc = self.function.self_converter = self_converter(name, name, self.function, **kwargs)
3567n/a p_self = Parameter(sc.name, inspect.Parameter.POSITIONAL_ONLY, function=self.function, converter=sc)
3568n/a self.function.parameters[sc.name] = p_self
3569n/a
3570n/a (cls or module).functions.append(self.function)
3571n/a self.next(self.state_parameters_start)
3572n/a
3573n/a # Now entering the parameters section. The rules, formally stated:
3574n/a #
3575n/a # * All lines must be indented with spaces only.
3576n/a # * The first line must be a parameter declaration.
3577n/a # * The first line must be indented.
3578n/a # * This first line establishes the indent for parameters.
3579n/a # * We'll call this number of spaces P (for "parameter").
3580n/a # * Thenceforth:
3581n/a # * Lines indented with P spaces specify a parameter.
3582n/a # * Lines indented with > P spaces are docstrings for the previous
3583n/a # parameter.
3584n/a # * We'll call this number of spaces D (for "docstring").
3585n/a # * All subsequent lines indented with >= D spaces are stored as
3586n/a # part of the per-parameter docstring.
3587n/a # * All lines will have the first D spaces of the indent stripped
3588n/a # before they are stored.
3589n/a # * It's illegal to have a line starting with a number of spaces X
3590n/a # such that P < X < D.
3591n/a # * A line with < P spaces is the first line of the function
3592n/a # docstring, which ends processing for parameters and per-parameter
3593n/a # docstrings.
3594n/a # * The first line of the function docstring must be at the same
3595n/a # indent as the function declaration.
3596n/a # * It's illegal to have any line in the parameters section starting
3597n/a # with X spaces such that F < X < P. (As before, F is the indent
3598n/a # of the function declaration.)
3599n/a #
3600n/a # Also, currently Argument Clinic places the following restrictions on groups:
3601n/a # * Each group must contain at least one parameter.
3602n/a # * Each group may contain at most one group, which must be the furthest
3603n/a # thing in the group from the required parameters. (The nested group
3604n/a # must be the first in the group when it's before the required
3605n/a # parameters, and the last thing in the group when after the required
3606n/a # parameters.)
3607n/a # * There may be at most one (top-level) group to the left or right of
3608n/a # the required parameters.
3609n/a # * You must specify a slash, and it must be after all parameters.
3610n/a # (In other words: either all parameters are positional-only,
3611n/a # or none are.)
3612n/a #
3613n/a # Said another way:
3614n/a # * Each group must contain at least one parameter.
3615n/a # * All left square brackets before the required parameters must be
3616n/a # consecutive. (You can't have a left square bracket followed
3617n/a # by a parameter, then another left square bracket. You can't
3618n/a # have a left square bracket, a parameter, a right square bracket,
3619n/a # and then a left square bracket.)
3620n/a # * All right square brackets after the required parameters must be
3621n/a # consecutive.
3622n/a #
3623n/a # These rules are enforced with a single state variable:
3624n/a # "parameter_state". (Previously the code was a miasma of ifs and
3625n/a # separate boolean state variables.) The states are:
3626n/a #
3627n/a # [ [ a, b, ] c, ] d, e, f=3, [ g, h, [ i ] ] <- line
3628n/a # 01 2 3 4 5 6 <- state transitions
3629n/a #
3630n/a # 0: ps_start. before we've seen anything. legal transitions are to 1 or 3.
3631n/a # 1: ps_left_square_before. left square brackets before required parameters.
3632n/a # 2: ps_group_before. in a group, before required parameters.
3633n/a # 3: ps_required. required parameters, positional-or-keyword or positional-only
3634n/a # (we don't know yet). (renumber left groups!)
3635n/a # 4: ps_optional. positional-or-keyword or positional-only parameters that
3636n/a # now must have default values.
3637n/a # 5: ps_group_after. in a group, after required parameters.
3638n/a # 6: ps_right_square_after. right square brackets after required parameters.
3639n/a ps_start, ps_left_square_before, ps_group_before, ps_required, \
3640n/a ps_optional, ps_group_after, ps_right_square_after = range(7)
3641n/a
3642n/a def state_parameters_start(self, line):
3643n/a if self.ignore_line(line):
3644n/a return
3645n/a
3646n/a # if this line is not indented, we have no parameters
3647n/a if not self.indent.infer(line):
3648n/a return self.next(self.state_function_docstring, line)
3649n/a
3650n/a self.parameter_continuation = ''
3651n/a return self.next(self.state_parameter, line)
3652n/a
3653n/a
3654n/a def to_required(self):
3655n/a """
3656n/a Transition to the "required" parameter state.
3657n/a """
3658n/a if self.parameter_state != self.ps_required:
3659n/a self.parameter_state = self.ps_required
3660n/a for p in self.function.parameters.values():
3661n/a p.group = -p.group
3662n/a
3663n/a def state_parameter(self, line):
3664n/a if self.parameter_continuation:
3665n/a line = self.parameter_continuation + ' ' + line.lstrip()
3666n/a self.parameter_continuation = ''
3667n/a
3668n/a if self.ignore_line(line):
3669n/a return
3670n/a
3671n/a assert self.indent.depth == 2
3672n/a indent = self.indent.infer(line)
3673n/a if indent == -1:
3674n/a # we outdented, must be to definition column
3675n/a return self.next(self.state_function_docstring, line)
3676n/a
3677n/a if indent == 1:
3678n/a # we indented, must be to new parameter docstring column
3679n/a return self.next(self.state_parameter_docstring_start, line)
3680n/a
3681n/a line = line.rstrip()
3682n/a if line.endswith('\\'):
3683n/a self.parameter_continuation = line[:-1]
3684n/a return
3685n/a
3686n/a line = line.lstrip()
3687n/a
3688n/a if line in ('*', '/', '[', ']'):
3689n/a self.parse_special_symbol(line)
3690n/a return
3691n/a
3692n/a if self.parameter_state in (self.ps_start, self.ps_required):
3693n/a self.to_required()
3694n/a elif self.parameter_state == self.ps_left_square_before:
3695n/a self.parameter_state = self.ps_group_before
3696n/a elif self.parameter_state == self.ps_group_before:
3697n/a if not self.group:
3698n/a self.to_required()
3699n/a elif self.parameter_state in (self.ps_group_after, self.ps_optional):
3700n/a pass
3701n/a else:
3702n/a fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ".a)")
3703n/a
3704n/a # handle "as" for parameters too
3705n/a c_name = None
3706n/a name, have_as_token, trailing = line.partition(' as ')
3707n/a if have_as_token:
3708n/a name = name.strip()
3709n/a if ' ' not in name:
3710n/a fields = trailing.strip().split(' ')
3711n/a if not fields:
3712n/a fail("Invalid 'as' clause!")
3713n/a c_name = fields[0]
3714n/a if c_name.endswith(':'):
3715n/a name += ':'
3716n/a c_name = c_name[:-1]
3717n/a fields[0] = name
3718n/a line = ' '.join(fields)
3719n/a
3720n/a base, equals, default = line.rpartition('=')
3721n/a if not equals:
3722n/a base = default
3723n/a default = None
3724n/a
3725n/a module = None
3726n/a try:
3727n/a ast_input = "def x({}): pass".format(base)
3728n/a module = ast.parse(ast_input)
3729n/a except SyntaxError:
3730n/a try:
3731n/a # the last = was probably inside a function call, like
3732n/a # c: int(accept={str})
3733n/a # so assume there was no actual default value.
3734n/a default = None
3735n/a ast_input = "def x({}): pass".format(line)
3736n/a module = ast.parse(ast_input)
3737n/a except SyntaxError:
3738n/a pass
3739n/a if not module:
3740n/a fail("Function " + self.function.name + " has an invalid parameter declaration:\n\t" + line)
3741n/a
3742n/a function_args = module.body[0].args
3743n/a
3744n/a if len(function_args.args) > 1:
3745n/a fail("Function " + self.function.name + " has an invalid parameter declaration (comma?):\n\t" + line)
3746n/a if function_args.defaults or function_args.kw_defaults:
3747n/a fail("Function " + self.function.name + " has an invalid parameter declaration (default value?):\n\t" + line)
3748n/a if function_args.vararg or function_args.kwarg:
3749n/a fail("Function " + self.function.name + " has an invalid parameter declaration (*args? **kwargs?):\n\t" + line)
3750n/a
3751n/a parameter = function_args.args[0]
3752n/a
3753n/a parameter_name = parameter.arg
3754n/a name, legacy, kwargs = self.parse_converter(parameter.annotation)
3755n/a
3756n/a if not default:
3757n/a if self.parameter_state == self.ps_optional:
3758n/a fail("Can't have a parameter without a default (" + repr(parameter_name) + ")\nafter a parameter with a default!")
3759n/a value = unspecified
3760n/a if 'py_default' in kwargs:
3761n/a fail("You can't specify py_default without specifying a default value!")
3762n/a else:
3763n/a if self.parameter_state == self.ps_required:
3764n/a self.parameter_state = self.ps_optional
3765n/a default = default.strip()
3766n/a bad = False
3767n/a ast_input = "x = {}".format(default)
3768n/a bad = False
3769n/a try:
3770n/a module = ast.parse(ast_input)
3771n/a
3772n/a if 'c_default' not in kwargs:
3773n/a # we can only represent very simple data values in C.
3774n/a # detect whether default is okay, via a blacklist
3775n/a # of disallowed ast nodes.
3776n/a class DetectBadNodes(ast.NodeVisitor):
3777n/a bad = False
3778n/a def bad_node(self, node):
3779n/a self.bad = True
3780n/a
3781n/a # inline function call
3782n/a visit_Call = bad_node
3783n/a # inline if statement ("x = 3 if y else z")
3784n/a visit_IfExp = bad_node
3785n/a
3786n/a # comprehensions and generator expressions
3787n/a visit_ListComp = visit_SetComp = bad_node
3788n/a visit_DictComp = visit_GeneratorExp = bad_node
3789n/a
3790n/a # literals for advanced types
3791n/a visit_Dict = visit_Set = bad_node
3792n/a visit_List = visit_Tuple = bad_node
3793n/a
3794n/a # "starred": "a = [1, 2, 3]; *a"
3795n/a visit_Starred = bad_node
3796n/a
3797n/a # allow ellipsis, for now
3798n/a # visit_Ellipsis = bad_node
3799n/a
3800n/a blacklist = DetectBadNodes()
3801n/a blacklist.visit(module)
3802n/a bad = blacklist.bad
3803n/a else:
3804n/a # if they specify a c_default, we can be more lenient about the default value.
3805n/a # but at least make an attempt at ensuring it's a valid expression.
3806n/a try:
3807n/a value = eval(default)
3808n/a if value == unspecified:
3809n/a fail("'unspecified' is not a legal default value!")
3810n/a except NameError:
3811n/a pass # probably a named constant
3812n/a except Exception as e:
3813n/a fail("Malformed expression given as default value\n"
3814n/a "{!r} caused {!r}".format(default, e))
3815n/a if bad:
3816n/a fail("Unsupported expression as default value: " + repr(default))
3817n/a
3818n/a expr = module.body[0].value
3819n/a # mild hack: explicitly support NULL as a default value
3820n/a if isinstance(expr, ast.Name) and expr.id == 'NULL':
3821n/a value = NULL
3822n/a py_default = 'None'
3823n/a c_default = "NULL"
3824n/a elif (isinstance(expr, ast.BinOp) or
3825n/a (isinstance(expr, ast.UnaryOp) and not isinstance(expr.operand, ast.Num))):
3826n/a c_default = kwargs.get("c_default")
3827n/a if not (isinstance(c_default, str) and c_default):
3828n/a fail("When you specify an expression (" + repr(default) + ") as your default value,\nyou MUST specify a valid c_default.")
3829n/a py_default = default
3830n/a value = unknown
3831n/a elif isinstance(expr, ast.Attribute):
3832n/a a = []
3833n/a n = expr
3834n/a while isinstance(n, ast.Attribute):
3835n/a a.append(n.attr)
3836n/a n = n.value
3837n/a if not isinstance(n, ast.Name):
3838n/a fail("Unsupported default value " + repr(default) + " (looked like a Python constant)")
3839n/a a.append(n.id)
3840n/a py_default = ".".join(reversed(a))
3841n/a
3842n/a c_default = kwargs.get("c_default")
3843n/a if not (isinstance(c_default, str) and c_default):
3844n/a fail("When you specify a named constant (" + repr(py_default) + ") as your default value,\nyou MUST specify a valid c_default.")
3845n/a
3846n/a try:
3847n/a value = eval(py_default)
3848n/a except NameError:
3849n/a value = unknown
3850n/a else:
3851n/a value = ast.literal_eval(expr)
3852n/a py_default = repr(value)
3853n/a if isinstance(value, (bool, None.__class__)):
3854n/a c_default = "Py_" + py_default
3855n/a elif isinstance(value, str):
3856n/a c_default = c_repr(value)
3857n/a else:
3858n/a c_default = py_default
3859n/a
3860n/a except SyntaxError as e:
3861n/a fail("Syntax error: " + repr(e.text))
3862n/a except (ValueError, AttributeError):
3863n/a value = unknown
3864n/a c_default = kwargs.get("c_default")
3865n/a py_default = default
3866n/a if not (isinstance(c_default, str) and c_default):
3867n/a fail("When you specify a named constant (" + repr(py_default) + ") as your default value,\nyou MUST specify a valid c_default.")
3868n/a
3869n/a kwargs.setdefault('c_default', c_default)
3870n/a kwargs.setdefault('py_default', py_default)
3871n/a
3872n/a dict = legacy_converters if legacy else converters
3873n/a legacy_str = "legacy " if legacy else ""
3874n/a if name not in dict:
3875n/a fail('{} is not a valid {}converter'.format(name, legacy_str))
3876n/a # if you use a c_name for the parameter, we just give that name to the converter
3877n/a # but the parameter object gets the python name
3878n/a converter = dict[name](c_name or parameter_name, parameter_name, self.function, value, **kwargs)
3879n/a
3880n/a kind = inspect.Parameter.KEYWORD_ONLY if self.keyword_only else inspect.Parameter.POSITIONAL_OR_KEYWORD
3881n/a
3882n/a if isinstance(converter, self_converter):
3883n/a if len(self.function.parameters) == 1:
3884n/a if (self.parameter_state != self.ps_required):
3885n/a fail("A 'self' parameter cannot be marked optional.")
3886n/a if value is not unspecified:
3887n/a fail("A 'self' parameter cannot have a default value.")
3888n/a if self.group:
3889n/a fail("A 'self' parameter cannot be in an optional group.")
3890n/a kind = inspect.Parameter.POSITIONAL_ONLY
3891n/a self.parameter_state = self.ps_start
3892n/a self.function.parameters.clear()
3893n/a else:
3894n/a fail("A 'self' parameter, if specified, must be the very first thing in the parameter block.")
3895n/a
3896n/a p = Parameter(parameter_name, kind, function=self.function, converter=converter, default=value, group=self.group)
3897n/a
3898n/a if parameter_name in self.function.parameters:
3899n/a fail("You can't have two parameters named " + repr(parameter_name) + "!")
3900n/a self.function.parameters[parameter_name] = p
3901n/a
3902n/a def parse_converter(self, annotation):
3903n/a if isinstance(annotation, ast.Str):
3904n/a return annotation.s, True, {}
3905n/a
3906n/a if isinstance(annotation, ast.Name):
3907n/a return annotation.id, False, {}
3908n/a
3909n/a if not isinstance(annotation, ast.Call):
3910n/a fail("Annotations must be either a name, a function call, or a string.")
3911n/a
3912n/a name = annotation.func.id
3913n/a symbols = globals()
3914n/a
3915n/a kwargs = {node.arg: eval_ast_expr(node.value, symbols) for node in annotation.keywords}
3916n/a return name, False, kwargs
3917n/a
3918n/a def parse_special_symbol(self, symbol):
3919n/a if symbol == '*':
3920n/a if self.keyword_only:
3921n/a fail("Function " + self.function.name + " uses '*' more than once.")
3922n/a self.keyword_only = True
3923n/a elif symbol == '[':
3924n/a if self.parameter_state in (self.ps_start, self.ps_left_square_before):
3925n/a self.parameter_state = self.ps_left_square_before
3926n/a elif self.parameter_state in (self.ps_required, self.ps_group_after):
3927n/a self.parameter_state = self.ps_group_after
3928n/a else:
3929n/a fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ".b)")
3930n/a self.group += 1
3931n/a self.function.docstring_only = True
3932n/a elif symbol == ']':
3933n/a if not self.group:
3934n/a fail("Function " + self.function.name + " has a ] without a matching [.")
3935n/a if not any(p.group == self.group for p in self.function.parameters.values()):
3936n/a fail("Function " + self.function.name + " has an empty group.\nAll groups must contain at least one parameter.")
3937n/a self.group -= 1
3938n/a if self.parameter_state in (self.ps_left_square_before, self.ps_group_before):
3939n/a self.parameter_state = self.ps_group_before
3940n/a elif self.parameter_state in (self.ps_group_after, self.ps_right_square_after):
3941n/a self.parameter_state = self.ps_right_square_after
3942n/a else:
3943n/a fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ".c)")
3944n/a elif symbol == '/':
3945n/a if self.positional_only:
3946n/a fail("Function " + self.function.name + " uses '/' more than once.")
3947n/a self.positional_only = True
3948n/a # ps_required and ps_optional are allowed here, that allows positional-only without option groups
3949n/a # to work (and have default values!)
3950n/a if (self.parameter_state not in (self.ps_required, self.ps_optional, self.ps_right_square_after, self.ps_group_before)) or self.group:
3951n/a fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ".d)")
3952n/a if self.keyword_only:
3953n/a fail("Function " + self.function.name + " mixes keyword-only and positional-only parameters, which is unsupported.")
3954n/a # fixup preceding parameters
3955n/a for p in self.function.parameters.values():
3956n/a if (p.kind != inspect.Parameter.POSITIONAL_OR_KEYWORD and not isinstance(p.converter, self_converter)):
3957n/a fail("Function " + self.function.name + " mixes keyword-only and positional-only parameters, which is unsupported.")
3958n/a p.kind = inspect.Parameter.POSITIONAL_ONLY
3959n/a
3960n/a def state_parameter_docstring_start(self, line):
3961n/a self.parameter_docstring_indent = len(self.indent.margin)
3962n/a assert self.indent.depth == 3
3963n/a return self.next(self.state_parameter_docstring, line)
3964n/a
3965n/a # every line of the docstring must start with at least F spaces,
3966n/a # where F > P.
3967n/a # these F spaces will be stripped.
3968n/a def state_parameter_docstring(self, line):
3969n/a stripped = line.strip()
3970n/a if stripped.startswith('#'):
3971n/a return
3972n/a
3973n/a indent = self.indent.measure(line)
3974n/a if indent < self.parameter_docstring_indent:
3975n/a self.indent.infer(line)
3976n/a assert self.indent.depth < 3
3977n/a if self.indent.depth == 2:
3978n/a # back to a parameter
3979n/a return self.next(self.state_parameter, line)
3980n/a assert self.indent.depth == 1
3981n/a return self.next(self.state_function_docstring, line)
3982n/a
3983n/a assert self.function.parameters
3984n/a last_parameter = next(reversed(list(self.function.parameters.values())))
3985n/a
3986n/a new_docstring = last_parameter.docstring
3987n/a
3988n/a if new_docstring:
3989n/a new_docstring += '\n'
3990n/a if stripped:
3991n/a new_docstring += self.indent.dedent(line)
3992n/a
3993n/a last_parameter.docstring = new_docstring
3994n/a
3995n/a # the final stanza of the DSL is the docstring.
3996n/a def state_function_docstring(self, line):
3997n/a if self.group:
3998n/a fail("Function " + self.function.name + " has a ] without a matching [.")
3999n/a
4000n/a stripped = line.strip()
4001n/a if stripped.startswith('#'):
4002n/a return
4003n/a
4004n/a new_docstring = self.function.docstring
4005n/a if new_docstring:
4006n/a new_docstring += "\n"
4007n/a if stripped:
4008n/a line = self.indent.dedent(line).rstrip()
4009n/a else:
4010n/a line = ''
4011n/a new_docstring += line
4012n/a self.function.docstring = new_docstring
4013n/a
4014n/a def format_docstring(self):
4015n/a f = self.function
4016n/a
4017n/a new_or_init = f.kind in (METHOD_NEW, METHOD_INIT)
4018n/a if new_or_init and not f.docstring:
4019n/a # don't render a docstring at all, no signature, nothing.
4020n/a return f.docstring
4021n/a
4022n/a text, add, output = _text_accumulator()
4023n/a parameters = f.render_parameters
4024n/a
4025n/a ##
4026n/a ## docstring first line
4027n/a ##
4028n/a
4029n/a if new_or_init:
4030n/a # classes get *just* the name of the class
4031n/a # not __new__, not __init__, and not module.classname
4032n/a assert f.cls
4033n/a add(f.cls.name)
4034n/a else:
4035n/a add(f.name)
4036n/a add('(')
4037n/a
4038n/a # populate "right_bracket_count" field for every parameter
4039n/a assert parameters, "We should always have a self parameter. " + repr(f)
4040n/a assert isinstance(parameters[0].converter, self_converter)
4041n/a # self is always positional-only.
4042n/a assert parameters[0].is_positional_only()
4043n/a parameters[0].right_bracket_count = 0
4044n/a positional_only = True
4045n/a for p in parameters[1:]:
4046n/a if not p.is_positional_only():
4047n/a positional_only = False
4048n/a else:
4049n/a assert positional_only
4050n/a if positional_only:
4051n/a p.right_bracket_count = abs(p.group)
4052n/a else:
4053n/a # don't put any right brackets around non-positional-only parameters, ever.
4054n/a p.right_bracket_count = 0
4055n/a
4056n/a right_bracket_count = 0
4057n/a
4058n/a def fix_right_bracket_count(desired):
4059n/a nonlocal right_bracket_count
4060n/a s = ''
4061n/a while right_bracket_count < desired:
4062n/a s += '['
4063n/a right_bracket_count += 1
4064n/a while right_bracket_count > desired:
4065n/a s += ']'
4066n/a right_bracket_count -= 1
4067n/a return s
4068n/a
4069n/a need_slash = False
4070n/a added_slash = False
4071n/a need_a_trailing_slash = False
4072n/a
4073n/a # we only need a trailing slash:
4074n/a # * if this is not a "docstring_only" signature
4075n/a # * and if the last *shown* parameter is
4076n/a # positional only
4077n/a if not f.docstring_only:
4078n/a for p in reversed(parameters):
4079n/a if not p.converter.show_in_signature:
4080n/a continue
4081n/a if p.is_positional_only():
4082n/a need_a_trailing_slash = True
4083n/a break
4084n/a
4085n/a
4086n/a added_star = False
4087n/a
4088n/a first_parameter = True
4089n/a last_p = parameters[-1]
4090n/a line_length = len(''.join(text))
4091n/a indent = " " * line_length
4092n/a def add_parameter(text):
4093n/a nonlocal line_length
4094n/a nonlocal first_parameter
4095n/a if first_parameter:
4096n/a s = text
4097n/a first_parameter = False
4098n/a else:
4099n/a s = ' ' + text
4100n/a if line_length + len(s) >= 72:
4101n/a add('\n')
4102n/a add(indent)
4103n/a line_length = len(indent)
4104n/a s = text
4105n/a line_length += len(s)
4106n/a add(s)
4107n/a
4108n/a for p in parameters:
4109n/a if not p.converter.show_in_signature:
4110n/a continue
4111n/a assert p.name
4112n/a
4113n/a is_self = isinstance(p.converter, self_converter)
4114n/a if is_self and f.docstring_only:
4115n/a # this isn't a real machine-parsable signature,
4116n/a # so let's not print the "self" parameter
4117n/a continue
4118n/a
4119n/a if p.is_positional_only():
4120n/a need_slash = not f.docstring_only
4121n/a elif need_slash and not (added_slash or p.is_positional_only()):
4122n/a added_slash = True
4123n/a add_parameter('/,')
4124n/a
4125n/a if p.is_keyword_only() and not added_star:
4126n/a added_star = True
4127n/a add_parameter('*,')
4128n/a
4129n/a p_add, p_output = text_accumulator()
4130n/a p_add(fix_right_bracket_count(p.right_bracket_count))
4131n/a
4132n/a if isinstance(p.converter, self_converter):
4133n/a # annotate first parameter as being a "self".
4134n/a #
4135n/a # if inspect.Signature gets this function,
4136n/a # and it's already bound, the self parameter
4137n/a # will be stripped off.
4138n/a #
4139n/a # if it's not bound, it should be marked
4140n/a # as positional-only.
4141n/a #
4142n/a # note: we don't print "self" for __init__,
4143n/a # because this isn't actually the signature
4144n/a # for __init__. (it can't be, __init__ doesn't
4145n/a # have a docstring.) if this is an __init__
4146n/a # (or __new__), then this signature is for
4147n/a # calling the class to construct a new instance.
4148n/a p_add('$')
4149n/a
4150n/a name = p.converter.signature_name or p.name
4151n/a p_add(name)
4152n/a
4153n/a if p.converter.is_optional():
4154n/a p_add('=')
4155n/a value = p.converter.py_default
4156n/a if not value:
4157n/a value = repr(p.converter.default)
4158n/a p_add(value)
4159n/a
4160n/a if (p != last_p) or need_a_trailing_slash:
4161n/a p_add(',')
4162n/a
4163n/a add_parameter(p_output())
4164n/a
4165n/a add(fix_right_bracket_count(0))
4166n/a if need_a_trailing_slash:
4167n/a add_parameter('/')
4168n/a add(')')
4169n/a
4170n/a # PEP 8 says:
4171n/a #
4172n/a # The Python standard library will not use function annotations
4173n/a # as that would result in a premature commitment to a particular
4174n/a # annotation style. Instead, the annotations are left for users
4175n/a # to discover and experiment with useful annotation styles.
4176n/a #
4177n/a # therefore this is commented out:
4178n/a #
4179n/a # if f.return_converter.py_default:
4180n/a # add(' -> ')
4181n/a # add(f.return_converter.py_default)
4182n/a
4183n/a if not f.docstring_only:
4184n/a add("\n" + sig_end_marker + "\n")
4185n/a
4186n/a docstring_first_line = output()
4187n/a
4188n/a # now fix up the places where the brackets look wrong
4189n/a docstring_first_line = docstring_first_line.replace(', ]', ',] ')
4190n/a
4191n/a # okay. now we're officially building the "parameters" section.
4192n/a # create substitution text for {parameters}
4193n/a spacer_line = False
4194n/a for p in parameters:
4195n/a if not p.docstring.strip():
4196n/a continue
4197n/a if spacer_line:
4198n/a add('\n')
4199n/a else:
4200n/a spacer_line = True
4201n/a add(" ")
4202n/a add(p.name)
4203n/a add('\n')
4204n/a add(textwrap.indent(rstrip_lines(p.docstring.rstrip()), " "))
4205n/a parameters = output()
4206n/a if parameters:
4207n/a parameters += '\n'
4208n/a
4209n/a ##
4210n/a ## docstring body
4211n/a ##
4212n/a
4213n/a docstring = f.docstring.rstrip()
4214n/a lines = [line.rstrip() for line in docstring.split('\n')]
4215n/a
4216n/a # Enforce the summary line!
4217n/a # The first line of a docstring should be a summary of the function.
4218n/a # It should fit on one line (80 columns? 79 maybe?) and be a paragraph
4219n/a # by itself.
4220n/a #
4221n/a # Argument Clinic enforces the following rule:
4222n/a # * either the docstring is empty,
4223n/a # * or it must have a summary line.
4224n/a #
4225n/a # Guido said Clinic should enforce this:
4226n/a # http://mail.python.org/pipermail/python-dev/2013-June/127110.html
4227n/a
4228n/a if len(lines) >= 2:
4229n/a if lines[1]:
4230n/a fail("Docstring for " + f.full_name + " does not have a summary line!\n" +
4231n/a "Every non-blank function docstring must start with\n" +
4232n/a "a single line summary followed by an empty line.")
4233n/a elif len(lines) == 1:
4234n/a # the docstring is only one line right now--the summary line.
4235n/a # add an empty line after the summary line so we have space
4236n/a # between it and the {parameters} we're about to add.
4237n/a lines.append('')
4238n/a
4239n/a parameters_marker_count = len(docstring.split('{parameters}')) - 1
4240n/a if parameters_marker_count > 1:
4241n/a fail('You may not specify {parameters} more than once in a docstring!')
4242n/a
4243n/a if not parameters_marker_count:
4244n/a # insert after summary line
4245n/a lines.insert(2, '{parameters}')
4246n/a
4247n/a # insert at front of docstring
4248n/a lines.insert(0, docstring_first_line)
4249n/a
4250n/a docstring = "\n".join(lines)
4251n/a
4252n/a add(docstring)
4253n/a docstring = output()
4254n/a
4255n/a docstring = linear_format(docstring, parameters=parameters)
4256n/a docstring = docstring.rstrip()
4257n/a
4258n/a return docstring
4259n/a
4260n/a def state_terminal(self, line):
4261n/a """
4262n/a Called when processing the block is done.
4263n/a """
4264n/a assert not line
4265n/a
4266n/a if not self.function:
4267n/a return
4268n/a
4269n/a if self.keyword_only:
4270n/a values = self.function.parameters.values()
4271n/a if not values:
4272n/a no_parameter_after_star = True
4273n/a else:
4274n/a last_parameter = next(reversed(list(values)))
4275n/a no_parameter_after_star = last_parameter.kind != inspect.Parameter.KEYWORD_ONLY
4276n/a if no_parameter_after_star:
4277n/a fail("Function " + self.function.name + " specifies '*' without any parameters afterwards.")
4278n/a
4279n/a # remove trailing whitespace from all parameter docstrings
4280n/a for name, value in self.function.parameters.items():
4281n/a if not value:
4282n/a continue
4283n/a value.docstring = value.docstring.rstrip()
4284n/a
4285n/a self.function.docstring = self.format_docstring()
4286n/a
4287n/a
4288n/a
4289n/a
4290n/a# maps strings to callables.
4291n/a# the callable should return an object
4292n/a# that implements the clinic parser
4293n/a# interface (__init__ and parse).
4294n/a#
4295n/a# example parsers:
4296n/a# "clinic", handles the Clinic DSL
4297n/a# "python", handles running Python code
4298n/a#
4299n/aparsers = {'clinic' : DSLParser, 'python': PythonParser}
4300n/a
4301n/a
4302n/aclinic = None
4303n/a
4304n/a
4305n/adef main(argv):
4306n/a import sys
4307n/a
4308n/a if sys.version_info.major < 3 or sys.version_info.minor < 3:
4309n/a sys.exit("Error: clinic.py requires Python 3.3 or greater.")
4310n/a
4311n/a import argparse
4312n/a cmdline = argparse.ArgumentParser()
4313n/a cmdline.add_argument("-f", "--force", action='store_true')
4314n/a cmdline.add_argument("-o", "--output", type=str)
4315n/a cmdline.add_argument("-v", "--verbose", action='store_true')
4316n/a cmdline.add_argument("--converters", action='store_true')
4317n/a cmdline.add_argument("--make", action='store_true')
4318n/a cmdline.add_argument("filename", type=str, nargs="*")
4319n/a ns = cmdline.parse_args(argv)
4320n/a
4321n/a if ns.converters:
4322n/a if ns.filename:
4323n/a print("Usage error: can't specify --converters and a filename at the same time.")
4324n/a print()
4325n/a cmdline.print_usage()
4326n/a sys.exit(-1)
4327n/a converters = []
4328n/a return_converters = []
4329n/a ignored = set("""
4330n/a add_c_converter
4331n/a add_c_return_converter
4332n/a add_default_legacy_c_converter
4333n/a add_legacy_c_converter
4334n/a """.strip().split())
4335n/a module = globals()
4336n/a for name in module:
4337n/a for suffix, ids in (
4338n/a ("_return_converter", return_converters),
4339n/a ("_converter", converters),
4340n/a ):
4341n/a if name in ignored:
4342n/a continue
4343n/a if name.endswith(suffix):
4344n/a ids.append((name, name[:-len(suffix)]))
4345n/a break
4346n/a print()
4347n/a
4348n/a print("Legacy converters:")
4349n/a legacy = sorted(legacy_converters)
4350n/a print(' ' + ' '.join(c for c in legacy if c[0].isupper()))
4351n/a print(' ' + ' '.join(c for c in legacy if c[0].islower()))
4352n/a print()
4353n/a
4354n/a for title, attribute, ids in (
4355n/a ("Converters", 'converter_init', converters),
4356n/a ("Return converters", 'return_converter_init', return_converters),
4357n/a ):
4358n/a print(title + ":")
4359n/a longest = -1
4360n/a for name, short_name in ids:
4361n/a longest = max(longest, len(short_name))
4362n/a for name, short_name in sorted(ids, key=lambda x: x[1].lower()):
4363n/a cls = module[name]
4364n/a callable = getattr(cls, attribute, None)
4365n/a if not callable:
4366n/a continue
4367n/a signature = inspect.signature(callable)
4368n/a parameters = []
4369n/a for parameter_name, parameter in signature.parameters.items():
4370n/a if parameter.kind == inspect.Parameter.KEYWORD_ONLY:
4371n/a if parameter.default != inspect.Parameter.empty:
4372n/a s = '{}={!r}'.format(parameter_name, parameter.default)
4373n/a else:
4374n/a s = parameter_name
4375n/a parameters.append(s)
4376n/a print(' {}({})'.format(short_name, ', '.join(parameters)))
4377n/a print()
4378n/a print("All converters also accept (c_default=None, py_default=None, annotation=None).")
4379n/a print("All return converters also accept (py_default=None).")
4380n/a sys.exit(0)
4381n/a
4382n/a if ns.make:
4383n/a if ns.output or ns.filename:
4384n/a print("Usage error: can't use -o or filenames with --make.")
4385n/a print()
4386n/a cmdline.print_usage()
4387n/a sys.exit(-1)
4388n/a for root, dirs, files in os.walk('.'):
4389n/a for rcs_dir in ('.svn', '.git', '.hg', 'build', 'externals'):
4390n/a if rcs_dir in dirs:
4391n/a dirs.remove(rcs_dir)
4392n/a for filename in files:
4393n/a if not (filename.endswith('.c') or filename.endswith('.h')):
4394n/a continue
4395n/a path = os.path.join(root, filename)
4396n/a if ns.verbose:
4397n/a print(path)
4398n/a parse_file(path, force=ns.force, verify=not ns.force)
4399n/a return
4400n/a
4401n/a if not ns.filename:
4402n/a cmdline.print_usage()
4403n/a sys.exit(-1)
4404n/a
4405n/a if ns.output and len(ns.filename) > 1:
4406n/a print("Usage error: can't use -o with multiple filenames.")
4407n/a print()
4408n/a cmdline.print_usage()
4409n/a sys.exit(-1)
4410n/a
4411n/a for filename in ns.filename:
4412n/a if ns.verbose:
4413n/a print(filename)
4414n/a parse_file(filename, output=ns.output, force=ns.force, verify=not ns.force)
4415n/a
4416n/a
4417n/aif __name__ == "__main__":
4418n/a sys.exit(main(sys.argv[1:]))