Viewing Generated Code

If you wish to see the source code generated by a constructed class you need to access the MethodMaker instances on that class.

There are helper functions that will do this for you and also retrieve the source code the MethodMaker objects would generate.

print_generated_code will print the source code for all methods along with any necessary globals and annotations associated with the method being generated.

get_generated_code will retrieve the GeneratedCode objects that will be used to create the class methods. These have source_code, globs and annotations attributes.

You can also directly access the MethodMakers with the get_methods function

from pathlib import Path
from pprint import pp

from ducktools.classbuilder import get_generated_code, print_generated_code
from ducktools.classbuilder.prefab import attribute, Prefab


class Example(Prefab):
    a: int
    b: int = 42
    c: list = attribute(default_factory=list)
    p: Path = Path("/usr/bin/python")

generated_code = get_generated_code(Example)

pp(generated_code)

Output:

{'__init__': GeneratorOutput(source_code='def __init__(self, a, b=42, c=None, p=_p_default): ...', globs={'_c_factory': <class 'list'>, '_p_default': PosixPath('/usr/bin/python')}, annotations={'a': <class 'int'>, 'b': <class 'int'>, 'c': <class 'list'>, 'p': <class 'pathlib.Path'>, 'return': None}),
 '__eq__': GeneratorOutput(source_code='def __eq__(self, other): ...', globs={}, annotations=None),
 '__repr__': GeneratorOutput(source_code='def __repr__(self): ...', globs={}, annotations=None),
 '__replace__': GeneratorOutput(source_code='def __replace__(self, /, **changes): ...', globs={}, annotations=None)}

print_generated_code(Example) cleans up the output to be more presentable and readable

Source:
    def __eq__(self, other):
        return (
            self.a == other.a
            and self.b == other.b
            and self.c == other.c
            and self.p == other.p
        ) if self.__class__ is other.__class__ else NotImplemented

    def __init__(self, a, b=42, c=None, p=_p_default):
        self.a = a
        self.b = b
        self.c = c if c is not None else _c_factory()
        self.p = p

    def __replace__(self, /, **changes):
        new_kwargs = {'a': self.a, 'b': self.b, 'c': self.c, 'p': self.p}
        new_kwargs |= changes
        return self.__class__(**new_kwargs)

    def __repr__(self):
        return f'{type(self).__qualname__}(a={self.a!r}, b={self.b!r}, c={self.c!r}, p={self.p!r})'


Globals:
    __init__: {'_c_factory': <class 'list'>, '_p_default': PosixPath('/usr/bin/python')}

Annotations:
    __init__: {'a': <class 'int'>, 'b': <class 'int'>, 'c': <class 'list'>, 'p': <class 'pathlib.Path'>, 'return': None}