| 772 | |
| 773 | |
| 774 | class SingledispatchVisitor(TraverserVisitor): |
| 775 | current_path: str |
| 776 | |
| 777 | def __init__(self, errors: Errors) -> None: |
| 778 | super().__init__() |
| 779 | |
| 780 | # Map of main singledispatch function to list of registered implementations |
| 781 | self.singledispatch_impls: defaultdict[FuncDef, list[RegisterImplInfo]] = defaultdict(list) |
| 782 | |
| 783 | # Map of decorated function to the indices of any decorators to remove |
| 784 | self.decorators_to_remove: dict[FuncDef, list[int]] = {} |
| 785 | |
| 786 | self.errors: Errors = errors |
| 787 | self.func_stack_depth = 0 |
| 788 | |
| 789 | def visit_func_def(self, o: FuncDef) -> None: |
| 790 | self.func_stack_depth += 1 |
| 791 | super().visit_func_def(o) |
| 792 | self.func_stack_depth -= 1 |
| 793 | |
| 794 | def visit_decorator(self, dec: Decorator) -> None: |
| 795 | if dec.decorators: |
| 796 | decorators_to_store = dec.decorators.copy() |
| 797 | decorators_to_remove: list[int] = [] |
| 798 | # the index of the last non-register decorator before finding a register decorator |
| 799 | # when going through decorators from top to bottom |
| 800 | last_non_register: int | None = None |
| 801 | for i, d in enumerate(decorators_to_store): |
| 802 | impl = get_singledispatch_register_call_info(d, dec.func) |
| 803 | if impl is not None: |
| 804 | if self.func_stack_depth > 0: |
| 805 | self.errors.error( |
| 806 | "Registering nested functions not supported", self.current_path, d.line |
| 807 | ) |
| 808 | self.singledispatch_impls[impl.singledispatch_func].append( |
| 809 | (impl.dispatch_type, dec.func) |
| 810 | ) |
| 811 | decorators_to_remove.append(i) |
| 812 | if last_non_register is not None: |
| 813 | # found a register decorator after a non-register decorator, which we |
| 814 | # don't support because we'd have to make a copy of the function before |
| 815 | # calling the decorator so that we can call it later, which complicates |
| 816 | # the implementation for something that is probably not commonly used |
| 817 | self.errors.error( |
| 818 | "Calling decorator after registering function not supported", |
| 819 | self.current_path, |
| 820 | decorators_to_store[last_non_register].line, |
| 821 | ) |
| 822 | else: |
| 823 | if refers_to_fullname(d, "functools.singledispatch"): |
| 824 | if self.func_stack_depth > 0: |
| 825 | self.errors.error( |
| 826 | "Nested singledispatch functions not supported", |
| 827 | self.current_path, |
| 828 | d.line, |
| 829 | ) |
| 830 | decorators_to_remove.append(i) |
| 831 | # make sure that we still treat the function as a singledispatch function |
no outgoing calls
no test coverage detected
searching dependent graphs…