| 434 | |
| 435 | |
| 436 | class _FuncBuilder: |
| 437 | def __init__(self, globals): |
| 438 | self.names = [] |
| 439 | self.src = [] |
| 440 | self.globals = globals |
| 441 | self.locals = {} |
| 442 | self.overwrite_errors = {} |
| 443 | self.unconditional_adds = {} |
| 444 | self.method_annotations = {} |
| 445 | |
| 446 | def add_fn(self, name, args, body, *, locals=None, return_type=MISSING, |
| 447 | overwrite_error=False, unconditional_add=False, decorator=None, |
| 448 | annotation_fields=None): |
| 449 | if locals is not None: |
| 450 | self.locals.update(locals) |
| 451 | |
| 452 | # Keep track if this method is allowed to be overwritten if it already |
| 453 | # exists in the class. The error is method-specific, so keep it with |
| 454 | # the name. We'll use this when we generate all of the functions in |
| 455 | # the add_fns_to_class call. overwrite_error is either True, in which |
| 456 | # case we'll raise an error, or it's a string, in which case we'll |
| 457 | # raise an error and append this string. |
| 458 | if overwrite_error: |
| 459 | self.overwrite_errors[name] = overwrite_error |
| 460 | |
| 461 | # Should this function always overwrite anything that's already in the |
| 462 | # class? The default is to not overwrite a function that already |
| 463 | # exists. |
| 464 | if unconditional_add: |
| 465 | self.unconditional_adds[name] = True |
| 466 | |
| 467 | self.names.append(name) |
| 468 | |
| 469 | if annotation_fields is not None: |
| 470 | self.method_annotations[name] = (annotation_fields, return_type) |
| 471 | |
| 472 | args = ','.join(args) |
| 473 | body = '\n'.join(body) |
| 474 | |
| 475 | # Compute the text of the entire function, add it to the text we're generating. |
| 476 | self.src.append(f'{f' {decorator}\n' if decorator else ''} def {name}({args}):\n{body}') |
| 477 | |
| 478 | def add_fns_to_class(self, cls): |
| 479 | # The source to all of the functions we're generating. |
| 480 | fns_src = '\n'.join(self.src) |
| 481 | |
| 482 | # The locals they use. |
| 483 | local_vars = ','.join(self.locals.keys()) |
| 484 | |
| 485 | # The names of all of the functions, used for the return value of the |
| 486 | # outer function. Need to handle the 0-tuple specially. |
| 487 | if len(self.names) == 0: |
| 488 | return_names = '()' |
| 489 | else: |
| 490 | return_names =f'({",".join(self.names)},)' |
| 491 | |
| 492 | # txt is the entire function we're going to execute, including the |
| 493 | # bodies of the functions we're defining. Here's a greatly simplified |
no outgoing calls
no test coverage detected
searching dependent graphs…