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