Skip to content

Subspace

Subspace

Bases: ProfileContainer

Subspace.

Attributes:

Name Type Description
name str

Subsapce display name.

description str

Subsapce description.

Source code in src\omoospace\subspace.py
class Subspace(ProfileContainer):
    """Subspace.

    Attributes:
        name (str): Subsapce display name.
        description (str): Subsapce description.
    """

    name: str
    description: str

    def __init__(self, node: Node):
        self._node = node

    def __repr__(self):
        return self.node_name

    @property
    def name(self) -> str:
        """str: Subspace name. Prefer Subspace.yml, fallback to folder name."""
        name = self._get_data("name")
        return name if name else self.node_name

    @property
    def root_path(self) -> Path:
        """Path: Subspace root path (directory subspace only)."""
        for entity in self.entities:
            if entity.is_dir():
                return entity
        return None

    @property
    def profile_path(self) -> Path:
        """Path: Subspace profile file path."""
        if self.type == SubspaceType.DIRECTORY:
            return Path(self.root_path, "Subspace.yml").resolve()
        elif self.type != SubspaceType.DUMMY:
            entity = self.entities[0]
            node = self
            while True:
                parent = node.parent
                if not parent:
                    dir_parent = None
                    break
                elif parent.type == SubspaceType.DIRECTORY:
                    dir_parent = parent
                    break
                node = parent
            sub_route = (
                self.route[len(dir_parent.route) :] if dir_parent else self.route
            )
            profile_filename = "%s.Subspace.yml" % "_".join(sub_route)
            return Path(entity.parent, profile_filename).resolve()
        else:
            return None

    @property
    def type(self) -> SubspaceType:
        """SubspaceType: Subspace type."""
        if len(self.entities) != 0:
            if len(self.endpoint_entities) != 0:
                if self.root_path:
                    return SubspaceType.DIRECTORY
                else:
                    return SubspaceType.FILE
            else:
                return SubspaceType.PHANTOM
        else:
            return SubspaceType.DUMMY

    @property
    def endpoint_entities(self) -> list[Entity]:
        """list[Entity]: Subspace endpoint entities."""
        phantom_entities = []
        for entity in self.entities:
            node_names = format_name(entity.stem).split("_")
            if self.node_name == node_names[-1]:
                phantom_entities.append(entity)
        return phantom_entities

    @property
    def entities(self) -> list[Entity]:
        """list[Entity]: Subspace entities."""
        entities = self._node.data.entities

        def is_vaild(entity: Entity):
            exists = entity.exists()
            return exists

        # real file/directory only. remove those not exists.
        entities = list(filter(is_vaild, entities))
        self._node.data.entities = entities

        return entities

    @property
    def node_name(self) -> str:
        """str: Subspace node name."""
        return self._node.data.node_name

    @property
    def parent(self) -> "Subspace":
        """Subspace: Parent subspace."""
        node = self._node.parent
        return Subspace(node) if node else None

    @property
    def route(self) -> Route:
        """Route: Substance route."""
        path = self._node.path
        return path.split("/")[1:]

    @property
    def children(self) -> list["Subspace"]:
        """list["Subspace"]: Children subspace."""
        nodes = self._node.children
        return [Subspace(node) for node in nodes]

    def _add_entity(self, *entities: Entity):
        for entity in entities:
            if entity not in self.entities:
                self._node.data.entities.append(entity)

    def add(self, node_name: str) -> "Subspace":
        """Add dummy subpace to it.

        Args:
            node_name (str): New add subspace node name.

        Returns:
            Subspace: New added dummy subspace.
        """
        node = self._node.add(SubspaceData(node_name))
        return Subspace(node)

children: list[Subspace] property

list["Subspace"]: Children subspace.

endpoint_entities: list[Entity] property

list[Entity]: Subspace endpoint entities.

entities: list[Entity] property

list[Entity]: Subspace entities.

name: str property

str: Subspace name. Prefer Subspace.yml, fallback to folder name.

node_name: str property

str: Subspace node name.

parent: Subspace property

Subspace: Parent subspace.

profile_path: Path property

Path: Subspace profile file path.

root_path: Path property

Path: Subspace root path (directory subspace only).

route: Route property

Route: Substance route.

type: SubspaceType property

SubspaceType: Subspace type.

add(node_name)

Add dummy subpace to it.

Parameters:

Name Type Description Default
node_name str

New add subspace node name.

required

Returns:

Name Type Description
Subspace Subspace

New added dummy subspace.

Source code in src\omoospace\subspace.py
def add(self, node_name: str) -> "Subspace":
    """Add dummy subpace to it.

    Args:
        node_name (str): New add subspace node name.

    Returns:
        Subspace: New added dummy subspace.
    """
    node = self._node.add(SubspaceData(node_name))
    return Subspace(node)

SubspaceTree

Bases: Tree

Subspace tree.

Source code in src\omoospace\subspace.py
class SubspaceTree(Tree):
    """Subspace tree."""

    def __init__(self, path: str = None):
        """Initialize the subspace tree from the directory or certain entity.

        Args:
            path (str, optional): Build subspace from path. Defaults to None.
        """
        super().__init__()
        if path:
            path = Path(path).resolve()
            if path.is_dir():
                self.from_dir(path)
            elif path.is_file():
                self.from_entity(path)
            else:
                NotFoundError()

    def from_dir(self, search_dir: str, recursive: bool = True):
        """Build the subspace tree from directory.

        It will search all vaild entities in giving directory, then build the tree based on those entities.

        Args:
            search_dir (str): The search directory.
            recursive (bool, optional): Whether recursive or not. Defaults to True.
        """
        entities = get_entities(search_dir, recursive=recursive)
        for entity in entities:
            self.from_entity(entity)

    def get(self, route: Route) -> Subspace:
        """Get subspace by route.

        Args:
            route (Route): Input.

        Returns:
            Subspace: Result.
        """
        node = self.find(match=lambda node: node.path == "/" + "/".join(route))
        return Subspace(node) if node else None

    def add(self, node_name: str) -> Subspace:
        """Add dummy subpace to tree root.

        Args:
            node_name (str): New add subspace node name.

        Returns:
            Subspace: New added dummy subspace.
        """
        node = super().add(SubspaceData(node_name))
        return Subspace(node)

    def to_dict(self) -> dict:
        """Convert the tree to a common dict.

        Returns:
            dict: Result.
        """

        def mapper(node: Node, data: dict):
            data["data"] = Subspace(node)
            return data

        return self.to_dict_list(mapper=mapper)

    def from_entity(self, entity_path: str):
        """Build the subspace tree from one certain entity.

        The path must be real, not dummy.

        Args:
            entity_path (Entity): The giving entity.

        Raises:
            InvalidError: Invalid entity.
        """
        route_entities = get_route_entities(entity_path)

        node = self
        for i in range(len(route_entities)):
            d = route_entities[i]
            route = [d.get("node_name") for d in route_entities[: i + 1]]
            node_name = d.get("node_name")
            entities = d.get("entities")
            subspace = self.get(route)
            if not subspace:
                subspace = node.add(node_name)
            subspace._add_entity(*entities)
            node = subspace

__init__(path=None)

Initialize the subspace tree from the directory or certain entity.

Parameters:

Name Type Description Default
path str

Build subspace from path. Defaults to None.

None
Source code in src\omoospace\subspace.py
def __init__(self, path: str = None):
    """Initialize the subspace tree from the directory or certain entity.

    Args:
        path (str, optional): Build subspace from path. Defaults to None.
    """
    super().__init__()
    if path:
        path = Path(path).resolve()
        if path.is_dir():
            self.from_dir(path)
        elif path.is_file():
            self.from_entity(path)
        else:
            NotFoundError()

add(node_name)

Add dummy subpace to tree root.

Parameters:

Name Type Description Default
node_name str

New add subspace node name.

required

Returns:

Name Type Description
Subspace Subspace

New added dummy subspace.

Source code in src\omoospace\subspace.py
def add(self, node_name: str) -> Subspace:
    """Add dummy subpace to tree root.

    Args:
        node_name (str): New add subspace node name.

    Returns:
        Subspace: New added dummy subspace.
    """
    node = super().add(SubspaceData(node_name))
    return Subspace(node)

from_dir(search_dir, recursive=True)

Build the subspace tree from directory.

It will search all vaild entities in giving directory, then build the tree based on those entities.

Parameters:

Name Type Description Default
search_dir str

The search directory.

required
recursive bool

Whether recursive or not. Defaults to True.

True
Source code in src\omoospace\subspace.py
def from_dir(self, search_dir: str, recursive: bool = True):
    """Build the subspace tree from directory.

    It will search all vaild entities in giving directory, then build the tree based on those entities.

    Args:
        search_dir (str): The search directory.
        recursive (bool, optional): Whether recursive or not. Defaults to True.
    """
    entities = get_entities(search_dir, recursive=recursive)
    for entity in entities:
        self.from_entity(entity)

from_entity(entity_path)

Build the subspace tree from one certain entity.

The path must be real, not dummy.

Parameters:

Name Type Description Default
entity_path Entity

The giving entity.

required

Raises:

Type Description
InvalidError

Invalid entity.

Source code in src\omoospace\subspace.py
def from_entity(self, entity_path: str):
    """Build the subspace tree from one certain entity.

    The path must be real, not dummy.

    Args:
        entity_path (Entity): The giving entity.

    Raises:
        InvalidError: Invalid entity.
    """
    route_entities = get_route_entities(entity_path)

    node = self
    for i in range(len(route_entities)):
        d = route_entities[i]
        route = [d.get("node_name") for d in route_entities[: i + 1]]
        node_name = d.get("node_name")
        entities = d.get("entities")
        subspace = self.get(route)
        if not subspace:
            subspace = node.add(node_name)
        subspace._add_entity(*entities)
        node = subspace

get(route)

Get subspace by route.

Parameters:

Name Type Description Default
route Route

Input.

required

Returns:

Name Type Description
Subspace Subspace

Result.

Source code in src\omoospace\subspace.py
def get(self, route: Route) -> Subspace:
    """Get subspace by route.

    Args:
        route (Route): Input.

    Returns:
        Subspace: Result.
    """
    node = self.find(match=lambda node: node.path == "/" + "/".join(route))
    return Subspace(node) if node else None

to_dict()

Convert the tree to a common dict.

Returns:

Name Type Description
dict dict

Result.

Source code in src\omoospace\subspace.py
def to_dict(self) -> dict:
    """Convert the tree to a common dict.

    Returns:
        dict: Result.
    """

    def mapper(node: Node, data: dict):
        data["data"] = Subspace(node)
        return data

    return self.to_dict_list(mapper=mapper)

get_entities(search_dir, recursive=True)

Search all vaild entities in giving directory.

Parameters:

Name Type Description Default
search_dir str

The search directory.

required
recursive bool

Whether recursive or not. Defaults to True.

True
Source code in src\omoospace\subspace.py
def get_entities(search_dir: str, recursive: bool = True) -> list[Entity]:
    """Search all vaild entities in giving directory.

    Args:
        search_dir (str): The search directory.
        recursive (bool, optional): Whether recursive or not. Defaults to True.
    """
    search_path: Path = Path(search_dir).resolve()
    entities: list[Entity] = []
    if recursive:
        # FIXME: replace this with Path.walk (python 3.12)
        # https://docs.python.org/3/library/pathlib.html
        for root, dirs, files in os.walk(search_path):
            for path in [*dirs, *files]:
                child = Path(root, path).resolve()
                if is_entity(child):
                    entities.append(child)
    else:
        for child in search_path.iterdir():
            if is_entity(child):
                entities.append(child)
    return entities

get_route(entity_path)

Returns the subspace route of a entity.

Super useful for creating path of project file outputs. For example:

route = get_route("path/to/Seq010/Seq010_AssetA.blend")
# >>> ['Seq010','AssetA']

Parameters:

Name Type Description Default
entity_path str

The giving entity.

required

Returns:

Name Type Description
Route Route

Subspace route.

Source code in src\omoospace\subspace.py
def get_route(entity_path: str) -> Route:
    """Returns the subspace route of a entity.

    Super useful for creating path of project file outputs. For example:
    ```python
    route = get_route("path/to/Seq010/Seq010_AssetA.blend")
    # >>> ['Seq010','AssetA']
    ```

    Args:
        entity_path (str): The giving entity.

    Returns:
        Route: Subspace route.
    """
    route_entities = get_route_entities(entity_path)
    route: Route = [d.get("node_name") for d in route_entities]
    return route