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