pydmt.api.builder — pydmt Documentation

# Source code for pydmt.api.builder

    """
    builder.py
    """

    import abc
    import os
    import shutil

    from collections.abc import Generator, Sequence

    from pydmt.utils.digester import Digester

    class Node(abc.ABC):
        """
        This is a source object which knows how to add his checksum to a checksum
        calculation
        """

        @abc.abstractmethod
        def add_to_digester(self, d: Digester) -> None:
            pass

        @abc.abstractmethod
        def get_name(self) -> str:
            pass

        @abc.abstractmethod
        def remove(self) -> None:
            pass

    class Builder(abc.ABC):
        """
        A Builder is what really builds things in the system.
        A builder knows on which inputs it relies and what
        outputs it generates (sometimes before build and sometimes
        after).
        """

        def get_name(self) -> str:
            """
            Override this to get better names
            :return: the name of this builder
            """
            return f"{self.__class__.__name__}"

        @abc.abstractmethod
        def __init__(self):
            """ initialize your builder here """

        @abc.abstractmethod
        def build(self) -> None:
            """
            this method actually does the building
            Just do whatever you want here. Options are:
            - Write pure python code
            - Call native code
            - Call external programs
            - A combination of the above
            If there are any problems then throw an exception.
            Try not to segfault the interpreter in this method...:)
            """

        @abc.abstractmethod
        def get_sources(self) -> Sequence[Node]:
            """
            return the name of the source files for this builder
            If the builder takes a whole folder the list all the filers in that folder.
            If a built takes all the .py files in a folder then list those.
            In the current implementation this method is not really that important because
            it is not used to calculate the signature of the input to the build.
            The @get_signature method is use for that.
            In the future the get_signature method will go away.
            """

        @abc.abstractmethod
        def get_targets(self) -> Sequence[Node]:
            """
            return list of targets
            """

        @abc.abstractmethod
        def yield_results(self) -> Generator[tuple[str, str], None, None]:
            """
            Return the signatures and names of results
            :return:
            """

        def get_signature(self) -> str:
            """
            return the sha1 of anything that identifies the sources of the build
            Techically this is the sha1 of the file content of the list of files
            returned from :func:`~get_sources`
            """
            d = Digester()
            for source in self.get_sources():
                source.add_to_digester(d)
            return d.get_hexdigest()

        def get_targets_as_string(self) -> str:
            return ",".join([x.get_name() for x in self.get_targets()])

    class File(Node):
        """
        This is a node which is a file
        """
        def __init__(self, filename: str):
            self.filename = filename

        def add_to_digester(self, d: Digester) -> None:
            d.add_file(self.filename)

        def get_name(self):
            return self.filename

        def remove(self):
            os.unlink(self.filename)

    class SourceFile(File):
        pass

    class TargetFile(File):
        pass

    class SourceFiles(Node):
        """
        This is a source of many files
        """
        def __init__(self, filenames: list[str], name: str):
            self.filenames = filenames
            self.name = name

        def add_to_digester(self, d: Digester) -> None:
            d.add_files(self.filenames)

        def get_name(self):
            return self.name

        def remove(self):
            for filename in self.filenames:
                os.unlink(filename)

    class Folder(Node):
        """
        This is a node representing a single Folder
        """
        def __init__(self, folder: str):
            self.folder = folder

        def add_to_digester(self, d: Digester) -> None:
            d.add_folders(folders=[self.folder])

        def get_name(self):
            return self.folder

        def remove(self):
            shutil.rmtree(self.folder)

    class SourceFolder(Folder):
        pass

    class TargetFolder(Folder):
        pass