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


[docs] class Node(abc.ABC): """ This is a source object which knows how to add his checksum to a checksum calculation """
[docs] @abc.abstractmethod def add_to_digester(self, d: Digester) -> None: pass
[docs] @abc.abstractmethod def get_name(self) -> str: pass
[docs] @abc.abstractmethod def remove(self) -> None: pass
[docs] 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). """
[docs] 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 """
[docs] @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...:) """
[docs] @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. """
[docs] @abc.abstractmethod def get_targets(self) -> Sequence[Node]: """ return list of targets """
[docs] @abc.abstractmethod def yield_results(self) -> Generator[tuple[str, str], None, None]: """ Return the signatures and names of results :return: """
[docs] 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()
[docs] def get_targets_as_string(self) -> str: return ",".join([x.get_name() for x in self.get_targets()])
[docs] class File(Node): """ This is a node which is a file """ def __init__(self, filename: str): self.filename = filename
[docs] def add_to_digester(self, d: Digester) -> None: d.add_file(self.filename)
[docs] def get_name(self): return self.filename
[docs] def remove(self): os.unlink(self.filename)
[docs] class SourceFile(File): pass
[docs] class TargetFile(File): pass
[docs] class SourceFiles(Node): """ This is a source of many files """ def __init__(self, filenames: list[str], name: str): self.filenames = filenames self.name = name
[docs] def add_to_digester(self, d: Digester) -> None: d.add_files(self.filenames)
[docs] def get_name(self): return self.name
[docs] def remove(self): for filename in self.filenames: os.unlink(filename)
[docs] class Folder(Node): """ This is a node representing a single Folder """ def __init__(self, folder: str): self.folder = folder
[docs] def add_to_digester(self, d: Digester) -> None: d.add_folders(folders=[self.folder])
[docs] def get_name(self): return self.folder
[docs] def remove(self): shutil.rmtree(self.folder)
[docs] class SourceFolder(Folder): pass
[docs] class TargetFolder(Folder): pass