Specialization of `convert_entity_to_ast` for classes.
(c, program_ctx)
| 492 | |
| 493 | |
| 494 | def convert_class_to_ast(c, program_ctx): |
| 495 | """Specialization of `convert_entity_to_ast` for classes.""" |
| 496 | # TODO(mdan): Revisit this altogether. Not sure we still need it. |
| 497 | converted_members = {} |
| 498 | method_filter = lambda m: tf_inspect.isfunction(m) or tf_inspect.ismethod(m) |
| 499 | members = tf_inspect.getmembers(c, predicate=method_filter) |
| 500 | if not members: |
| 501 | raise ValueError('cannot convert %s: no member methods' % c) |
| 502 | |
| 503 | # TODO(mdan): Don't clobber namespaces for each method in one class namespace. |
| 504 | # The assumption that one namespace suffices for all methods only holds if |
| 505 | # all methods were defined in the same module. |
| 506 | # If, instead, functions are imported from multiple modules and then spliced |
| 507 | # into the class, then each function has its own globals and __future__ |
| 508 | # imports that need to stay separate. |
| 509 | |
| 510 | # For example, C's methods could both have `global x` statements referring to |
| 511 | # mod1.x and mod2.x, but using one namespace for C would cause a conflict. |
| 512 | # from mod1 import f1 |
| 513 | # from mod2 import f2 |
| 514 | # class C(object): |
| 515 | # method1 = f1 |
| 516 | # method2 = f2 |
| 517 | |
| 518 | class_namespace = {} |
| 519 | future_features = None |
| 520 | for _, m in members: |
| 521 | # Only convert the members that are directly defined by the class. |
| 522 | if inspect_utils.getdefiningclass(m, c) is not c: |
| 523 | continue |
| 524 | (node,), _, entity_info = convert_func_to_ast( |
| 525 | m, program_ctx=program_ctx, do_rename=False) |
| 526 | class_namespace.update(entity_info.namespace) |
| 527 | converted_members[m] = node |
| 528 | |
| 529 | # TODO(mdan): Similarly check the globals. |
| 530 | if future_features is None: |
| 531 | future_features = entity_info.future_features |
| 532 | elif frozenset(future_features) ^ frozenset(entity_info.future_features): |
| 533 | # Note: we can support this case if ever needed. |
| 534 | raise ValueError( |
| 535 | 'cannot convert {}: if has methods built with mismatched future' |
| 536 | ' features: {} and {}'.format(c, future_features, |
| 537 | entity_info.future_features)) |
| 538 | namer = naming.Namer(class_namespace) |
| 539 | class_name = namer.class_name(c.__name__) |
| 540 | |
| 541 | # Process any base classes: if the superclass if of a whitelisted type, an |
| 542 | # absolute import line is generated. |
| 543 | output_nodes = [] |
| 544 | renames = {} |
| 545 | base_names = [] |
| 546 | for base in c.__bases__: |
| 547 | if isinstance(object, base): |
| 548 | base_names.append('object') |
| 549 | continue |
| 550 | if is_whitelisted_for_graph(base): |
| 551 | alias = namer.new_symbol(base.__name__, ()) |
no test coverage detected