Skip to content

synthesis

Full documentation pages are generated for docstring reference only and may contain symbols imported from other modules. Imported symbols are not distinguished from locally defined symbols and will appear in any module that they are imported into. For better information on where symbols should be imported from, review the sourcecode on the github.

FoSpy.blocks.synthesis

_debug module-attribute

_debug = Debug()

Debug

Source code in FoSpy/_debug.py
class Debug:
    def __init__(self):
        self.on = False

        frame = inspect.currentframe().f_back
        self.module_name = frame.f_globals.get("__name__", "<unknown>")
        self.label = f"|(Debug message from {self.module_name})"
        self.label_width = len(self.label)

    def _get_text_width(self, module=None):
        if module:
            label = f"|(Debug message from {module} via {self.module_name})"
            label_width = len(label)
        else:
            label = self.label
            label_width = self.label_width

        text_width = DEBUG_WIDTH - label_width
        return text_width, label, label_width


    def msg(self,msg, module=None):
        if not self.on:
            return

        text_width, label, label_width = self._get_text_width(module)

        wrapped = textwrap.fill(str(msg), width=text_width)

        for line in wrapped.splitlines():
            print(f'{line:<{text_width}}{label:>{label_width}}')

    def pmsg(self,msg,module=None,**kwargs):
        if not self.on:
            return

        text_width, label, label_width = self._get_text_width(module)

        buf = io.StringIO()
        pprint(msg,stream=buf, width=text_width,**kwargs)
        txt = buf.getvalue()
        for line in txt.splitlines():
            print(f'{line:<{text_width}}{label:>{label_width}}')

label instance-attribute

label = f'|(Debug message from {self.module_name})'

label_width instance-attribute

label_width = len(self.label)

module_name instance-attribute

module_name = frame.f_globals.get('__name__', '<unknown>')

on instance-attribute

on = False

__init__

__init__()
Source code in FoSpy/_debug.py
def __init__(self):
    self.on = False

    frame = inspect.currentframe().f_back
    self.module_name = frame.f_globals.get("__name__", "<unknown>")
    self.label = f"|(Debug message from {self.module_name})"
    self.label_width = len(self.label)

_get_text_width

_get_text_width(module=None)
Source code in FoSpy/_debug.py
def _get_text_width(self, module=None):
    if module:
        label = f"|(Debug message from {module} via {self.module_name})"
        label_width = len(label)
    else:
        label = self.label
        label_width = self.label_width

    text_width = DEBUG_WIDTH - label_width
    return text_width, label, label_width

msg

msg(msg, module=None)
Source code in FoSpy/_debug.py
def msg(self,msg, module=None):
    if not self.on:
        return

    text_width, label, label_width = self._get_text_width(module)

    wrapped = textwrap.fill(str(msg), width=text_width)

    for line in wrapped.splitlines():
        print(f'{line:<{text_width}}{label:>{label_width}}')

pmsg

pmsg(msg, module=None, **kwargs)
Source code in FoSpy/_debug.py
def pmsg(self,msg,module=None,**kwargs):
    if not self.on:
        return

    text_width, label, label_width = self._get_text_width(module)

    buf = io.StringIO()
    pprint(msg,stream=buf, width=text_width,**kwargs)
    txt = buf.getvalue()
    for line in txt.splitlines():
        print(f'{line:<{text_width}}{label:>{label_width}}')

FileBlock

Bases: SingleBlock

Represents a set of blocks loaded from a file.

All public attributes of FileBlock objects are either SingleBlock or ListBlock objects. Attributes without a header at the start of the file are parsed into {"metadata": blockDict} before passing to FileBlock.

Noteable Subclasses:

Synthesis(FileBlock)
TemplateSet(FileBlock)

Source code in FoSpy/blocks/files.py
@SingleBlock.setup_dispatch(from_key="_fos_type", allow_self=False)
class FileBlock(SingleBlock):
    """
    Represents a set of blocks loaded from a file.

    All public attributes of `FileBlock` objects are either `SingleBlock` or
    `ListBlock` objects. Attributes without a header at the start of the file
    are parsed into `{"metadata": blockDict}` before passing to `FileBlock`.

    Noteable Subclasses:
    ```
    Synthesis(FileBlock)
    TemplateSet(FileBlock)
    ```
    """
    def __init__(self, blockDict, _sourceFile=None, **kwargs):
        """
        Optionally specify _sourceFile before constructing from blockDict using parent `SingleBlock` constructor.
        """
        self._sourceFile = _sourceFile

        self._tempdir = tempfile.TemporaryDirectory()
        self._temppath = Path(self._tempdir.name)
        atexit.register(self.cleanup)

        super().__init__(blockDict, **kwargs)
        self.refresh_attachments()

    def __setattr__(self, name, value):
        if name == "_sourceFile":
            if str(value).endswith(".fosx"):
                self._ext_file = value
                self._ext_dir, value = _get_ext_dir(value)
            else:
                self._ext_file = None
                self._ext_dir = None
        super().__setattr__(name, value)  

    @classmethod
    def add_dispatch(cls, blockDict, dispatch_key, **kwargs):
        _ = SingleBlock.add_dispatch(blockDict, dispatch_key, **kwargs)

        from .. import _errors as err
        from ._blockUtils import _unwrap_block

        from .metadata import MetaData
        metadata = blockDict.get("metadata", None)

        if metadata is None:
            raise err.MissingPropertyError("metadata", cls, blockDict=blockDict)

        metadata = _unwrap_block(metadata)
        fos_type = metadata.pop("fos_type", None)
        if fos_type is None:
            raise err.MissingPropertyError("fos_type", MetaData, blockDict=metadata)

        return {dispatch_key: fos_type}


    def cleanup(self):
        if self._tempdir is not None:
            self._tempdir.cleanup()

    def get_id(self):
        try:
            if self._sourceFile is not None:
                from pathlib import Path
                fp = Path(self._sourceFile)
                return "file_name", str(fp.name)
            else:
                return "file_name", "<Unsaved FileBlock>"
        except Exception:
            return super().get_id()

    @classmethod
    def fromFile(cls, filepath):
        abspath = os.path.abspath(filepath)
        pathstr = str(abspath)
        try:
            ext = pathstr.lower().split(".")[-1]
        except IndexError:
            raise ValueError(f"Could not determine extension for filepath: {pathstr}")

        if ext not in EXT_READ_MAP:
            raise ValueError(f"Unrecognized file extension '{ext}'. Supported extensions are: {list(EXT_READ_MAP.keys())}")

        blockDict = EXT_READ_MAP[ext](abspath)
        abspath = blockDict.pop("_sourceFile", abspath)

        return cls(blockDict, _sourceFile=abspath)

    def save(self, filepath:str=None, json_indent=4, **kwargs):
        """
        Sends a serialized dict to be written to file.

        Args:
            filepath:
                If specified, writes serialized dict to filepath. ks to `self._sourceFile`.
            json_indent:
                Indent to use for json.dump when saving as json
            **kwargs:
                Optional kwargs to pass to saving routine (unique to each file extension)

        Raises:
            ValueError:
                If _sourceFile is not specified (if `FileBlock` was copied from
                another object or constructed directly from a blockDict),
                filepath must be specified.
        """
        from warnings import warn
        saving_as = filepath is not None
        try:
            if not saving_as:
                if self._sourceFile is None:
                    raise ValueError("Synthesis object was constructed without a sourceFile. A save destination must be specified.")
                else:
                    filepath = self._sourceFile
            self._sourceFile = os.path.abspath(filepath)

            filepath = self._sourceFile if self._ext_file is None else self._ext_file
            if str(filepath).endswith(".fosx"):
                return self.package(filepath)
            self.refresh_attachments()
            pathstr = str(filepath)
            try:
                ext = pathstr.lower().split(".")[-1]
            except IndexError:
                raise ValueError(f"Could not determine extension for filepath: {pathstr}")


            ext = str(filepath).lower().split(".")[-1]

            if ext not in EXT_WRITE_MAP:
                raise ValueError(f"Unrecognized file extension '{ext}'. Supported extensions are: {list(EXT_WRITE_MAP.keys())}")

            blockDict = self.serialize(clean="fos" not in ext)

            EXT_WRITE_MAP[ext](blockDict, filepath, json_indent=json_indent, **kwargs)

        except Exception as e:
            if not saving_as:
                warn(f"Could not save file. Disconnected from source file for safety. Exception: {e}", RuntimeWarning)
                self._sourceFile = None
                return e
            else:
                raise e
        return True

    def get_file_name(self):
        if self._sourceFile is None:
            return "<Unsaved FileBlock>"

        if hasattr(self, "_ext_file") and self._ext_file is not None:
            filepath = self._ext_file
        else:
            filepath = self._sourceFile

        return os.path.basename(filepath)

    def copy(self, path=None):
        """
        Returns a deep copy of self by saving to a temp file and reloading.

        Save/Reload allows attachment tracking to remain intact where
        serialization/reconstruction would normally desync.
        """
        if path is None:
            # get temporary save location
            loc = self._temppath / "~temp~.fos"
        else:
            path = Path(path)
            loc = path

        # cache current source file
        src = self._sourceFile

        # save and restore cached source file
        self.save(loc)
        self._sourceFile = src

        # load copy from temp file
        copy = self.fromFile(loc)
        # desync copy from temp file
        copy._sourceFile = path
        return copy

    def check_attachments(self):
        pass

    def matches_file(self):
        reloaded = self.fromFile(self._sourceFile)

        return self.__eq__(reloaded, suppress_routine_paths=True)

    def package(self, pkg_fp):
        import shutil
        pkg_fp = Path(pkg_fp)
        pkg_dir = self._temppath / "~package~"
        pkg_dir.mkdir(exist_ok=True)

        attachment_dir = pkg_dir / "attachments"
        attachment_dir.mkdir(exist_ok=True)

        copy = self.copy()

        for attachment in copy.find_attachments():
            attachment.path = "attachments"

        copy._sourceFile = pkg_dir / (pkg_fp.stem + ".fos")
        copy.refresh_attachments(new_copy=True, overwrite=False)
        copy.save()

        pkg_fp = pkg_fp.parent / pkg_fp.stem
        fosx_fp = pkg_fp.with_suffix(".fosx")
        zip_fp = Path(shutil.make_archive(pkg_fp, "zip", pkg_dir))

        if fosx_fp.exists():
            fosx_fp.unlink()

        zip_fp.rename(fosx_fp)

        shutil.rmtree(pkg_dir)

_sourceFile instance-attribute

_sourceFile = _sourceFile

_tempdir instance-attribute

_tempdir = tempfile.TemporaryDirectory()

_temppath instance-attribute

_temppath = Path(self._tempdir.name)

__init__

__init__(blockDict, _sourceFile=None, **kwargs)

Optionally specify _sourceFile before constructing from blockDict using parent SingleBlock constructor.

Source code in FoSpy/blocks/files.py
def __init__(self, blockDict, _sourceFile=None, **kwargs):
    """
    Optionally specify _sourceFile before constructing from blockDict using parent `SingleBlock` constructor.
    """
    self._sourceFile = _sourceFile

    self._tempdir = tempfile.TemporaryDirectory()
    self._temppath = Path(self._tempdir.name)
    atexit.register(self.cleanup)

    super().__init__(blockDict, **kwargs)
    self.refresh_attachments()

__setattr__

__setattr__(name, value)
Source code in FoSpy/blocks/files.py
def __setattr__(self, name, value):
    if name == "_sourceFile":
        if str(value).endswith(".fosx"):
            self._ext_file = value
            self._ext_dir, value = _get_ext_dir(value)
        else:
            self._ext_file = None
            self._ext_dir = None
    super().__setattr__(name, value)  

add_dispatch classmethod

add_dispatch(blockDict, dispatch_key, **kwargs)
Source code in FoSpy/blocks/files.py
@classmethod
def add_dispatch(cls, blockDict, dispatch_key, **kwargs):
    _ = SingleBlock.add_dispatch(blockDict, dispatch_key, **kwargs)

    from .. import _errors as err
    from ._blockUtils import _unwrap_block

    from .metadata import MetaData
    metadata = blockDict.get("metadata", None)

    if metadata is None:
        raise err.MissingPropertyError("metadata", cls, blockDict=blockDict)

    metadata = _unwrap_block(metadata)
    fos_type = metadata.pop("fos_type", None)
    if fos_type is None:
        raise err.MissingPropertyError("fos_type", MetaData, blockDict=metadata)

    return {dispatch_key: fos_type}

check_attachments

check_attachments()
Source code in FoSpy/blocks/files.py
def check_attachments(self):
    pass

cleanup

cleanup()
Source code in FoSpy/blocks/files.py
def cleanup(self):
    if self._tempdir is not None:
        self._tempdir.cleanup()

copy

copy(path=None)

Returns a deep copy of self by saving to a temp file and reloading.

Save/Reload allows attachment tracking to remain intact where serialization/reconstruction would normally desync.

Source code in FoSpy/blocks/files.py
def copy(self, path=None):
    """
    Returns a deep copy of self by saving to a temp file and reloading.

    Save/Reload allows attachment tracking to remain intact where
    serialization/reconstruction would normally desync.
    """
    if path is None:
        # get temporary save location
        loc = self._temppath / "~temp~.fos"
    else:
        path = Path(path)
        loc = path

    # cache current source file
    src = self._sourceFile

    # save and restore cached source file
    self.save(loc)
    self._sourceFile = src

    # load copy from temp file
    copy = self.fromFile(loc)
    # desync copy from temp file
    copy._sourceFile = path
    return copy

fromFile classmethod

fromFile(filepath)
Source code in FoSpy/blocks/files.py
@classmethod
def fromFile(cls, filepath):
    abspath = os.path.abspath(filepath)
    pathstr = str(abspath)
    try:
        ext = pathstr.lower().split(".")[-1]
    except IndexError:
        raise ValueError(f"Could not determine extension for filepath: {pathstr}")

    if ext not in EXT_READ_MAP:
        raise ValueError(f"Unrecognized file extension '{ext}'. Supported extensions are: {list(EXT_READ_MAP.keys())}")

    blockDict = EXT_READ_MAP[ext](abspath)
    abspath = blockDict.pop("_sourceFile", abspath)

    return cls(blockDict, _sourceFile=abspath)

get_file_name

get_file_name()
Source code in FoSpy/blocks/files.py
def get_file_name(self):
    if self._sourceFile is None:
        return "<Unsaved FileBlock>"

    if hasattr(self, "_ext_file") and self._ext_file is not None:
        filepath = self._ext_file
    else:
        filepath = self._sourceFile

    return os.path.basename(filepath)

get_id

get_id()
Source code in FoSpy/blocks/files.py
def get_id(self):
    try:
        if self._sourceFile is not None:
            from pathlib import Path
            fp = Path(self._sourceFile)
            return "file_name", str(fp.name)
        else:
            return "file_name", "<Unsaved FileBlock>"
    except Exception:
        return super().get_id()

matches_file

matches_file()
Source code in FoSpy/blocks/files.py
def matches_file(self):
    reloaded = self.fromFile(self._sourceFile)

    return self.__eq__(reloaded, suppress_routine_paths=True)

package

package(pkg_fp)
Source code in FoSpy/blocks/files.py
def package(self, pkg_fp):
    import shutil
    pkg_fp = Path(pkg_fp)
    pkg_dir = self._temppath / "~package~"
    pkg_dir.mkdir(exist_ok=True)

    attachment_dir = pkg_dir / "attachments"
    attachment_dir.mkdir(exist_ok=True)

    copy = self.copy()

    for attachment in copy.find_attachments():
        attachment.path = "attachments"

    copy._sourceFile = pkg_dir / (pkg_fp.stem + ".fos")
    copy.refresh_attachments(new_copy=True, overwrite=False)
    copy.save()

    pkg_fp = pkg_fp.parent / pkg_fp.stem
    fosx_fp = pkg_fp.with_suffix(".fosx")
    zip_fp = Path(shutil.make_archive(pkg_fp, "zip", pkg_dir))

    if fosx_fp.exists():
        fosx_fp.unlink()

    zip_fp.rename(fosx_fp)

    shutil.rmtree(pkg_dir)

save

save(filepath=None, json_indent=4, **kwargs)

Sends a serialized dict to be written to file.

Parameters:

Name Type Description Default
filepath str

If specified, writes serialized dict to filepath. ks to self._sourceFile.

None
json_indent

Indent to use for json.dump when saving as json

4
**kwargs

Optional kwargs to pass to saving routine (unique to each file extension)

{}

Raises:

Type Description
ValueError

If _sourceFile is not specified (if FileBlock was copied from another object or constructed directly from a blockDict), filepath must be specified.

Source code in FoSpy/blocks/files.py
def save(self, filepath:str=None, json_indent=4, **kwargs):
    """
    Sends a serialized dict to be written to file.

    Args:
        filepath:
            If specified, writes serialized dict to filepath. ks to `self._sourceFile`.
        json_indent:
            Indent to use for json.dump when saving as json
        **kwargs:
            Optional kwargs to pass to saving routine (unique to each file extension)

    Raises:
        ValueError:
            If _sourceFile is not specified (if `FileBlock` was copied from
            another object or constructed directly from a blockDict),
            filepath must be specified.
    """
    from warnings import warn
    saving_as = filepath is not None
    try:
        if not saving_as:
            if self._sourceFile is None:
                raise ValueError("Synthesis object was constructed without a sourceFile. A save destination must be specified.")
            else:
                filepath = self._sourceFile
        self._sourceFile = os.path.abspath(filepath)

        filepath = self._sourceFile if self._ext_file is None else self._ext_file
        if str(filepath).endswith(".fosx"):
            return self.package(filepath)
        self.refresh_attachments()
        pathstr = str(filepath)
        try:
            ext = pathstr.lower().split(".")[-1]
        except IndexError:
            raise ValueError(f"Could not determine extension for filepath: {pathstr}")


        ext = str(filepath).lower().split(".")[-1]

        if ext not in EXT_WRITE_MAP:
            raise ValueError(f"Unrecognized file extension '{ext}'. Supported extensions are: {list(EXT_WRITE_MAP.keys())}")

        blockDict = self.serialize(clean="fos" not in ext)

        EXT_WRITE_MAP[ext](blockDict, filepath, json_indent=json_indent, **kwargs)

    except Exception as e:
        if not saving_as:
            warn(f"Could not save file. Disconnected from source file for safety. Exception: {e}", RuntimeWarning)
            self._sourceFile = None
            return e
        else:
            raise e
    return True

Synthesis

Bases: FileBlock

Represents a Synthesis loaded from a FOS file.

Source code in FoSpy/blocks/synthesis.py
@FileBlock.register_dispatch("synthesis", defaults={"metadata":{"fos_type":"synthesis"}})
@FileBlock.register_dispatch(None)
class Synthesis(FileBlock):
    """
    Represents a Synthesis loaded from a FOS file.
    """
    dispatch_from = FileBlock

    def insert_material(self, mat, idx=-1):
        # placeholder. modify for insertion at idx
        self.materials.append(mat)

    def insert_treatment(self, treat, idx=-1):
        # placeholder. modify for insertion at idx
        self.treatments.append(treat)

dispatch_from class-attribute instance-attribute

dispatch_from = FileBlock

insert_material

insert_material(mat, idx=-1)
Source code in FoSpy/blocks/synthesis.py
def insert_material(self, mat, idx=-1):
    # placeholder. modify for insertion at idx
    self.materials.append(mat)

insert_treatment

insert_treatment(treat, idx=-1)
Source code in FoSpy/blocks/synthesis.py
def insert_treatment(self, treat, idx=-1):
    # placeholder. modify for insertion at idx
    self.treatments.append(treat)