Source code for pybiopax.paths

"""This module implements finding paths in a BioPaxModel starting from a
given object using a path constraint string."""
__all__ = ['find_objects', 'BiopaxClassConstraintError']

import logging
from typing import List
from .biopax import *


logger = logging.getLogger(__name__)


[docs]def find_objects(start_obj: BioPaxObject, path_str: str) -> List[BioPaxObject]: """Return objects matching the given path specification. Parameters ---------- start_obj : The object to start the search from. path_str : A path specification string which consists of one or more parts separated by /. Each part is the name of an object attribute, and can optionally contain a class name as well, separated by : to constrain the class of the target of the attribute to consider. Optionally, each attribute can also have a * suffix to make the search recursive. Returns ------- : A list of BioPaxObjects satisfying the given path specification. """ # We split off the first part and separate out the rest part, rest = path_str.split('/', maxsplit=1) \ if '/' in path_str else (path_str, None) # Handle class constraint if ':' in part: attribute, class_constraint_str = part.split(':', maxsplit=1) try: cls = biopax_cls_map[class_constraint_str] except KeyError: raise BiopaxClassConstraintError(class_constraint_str) from None else: attribute, cls = part, None # Handle recursion marker if attribute.endswith('*'): attribute = attribute[:-1] recursive = True else: recursive = False # Get the attribute we are looking for attr_val = getattr(start_obj, attribute, None) # We turn the value into a flat list of BioPaxObjects val = _get_object_list(attr_val) # If this is a recursive part, we run a BFS to get all the downstream # objects that can be reached via one or more of the given type of # attribute links if recursive: queue = val[:] visited = val[:] while queue: obj = queue.pop(0) obj_val = getattr(obj, attribute, None) for child in _get_object_list(obj_val): if child not in visited: visited.append(child) queue.append(child) val = visited # At this point, val is guaranteed to be a list of BioPaxObjects results = [] for v in val: if cls and not isinstance(v, cls): continue if rest: results += find_objects(v, rest) else: results.append(v) return results
def _get_object_list(val): if isinstance(val, BioPaxObject): return [val] elif isinstance(val, (list, set)): return [v for v in val if isinstance(v, BioPaxObject)] else: return [] def _make_biopax_cls_map(): import inspect from pybiopax import biopax return {k: v for k, v in inspect.getmembers(biopax, inspect.isclass) if issubclass(v, BioPaxObject)} biopax_cls_map = _make_biopax_cls_map()
[docs]class BiopaxClassConstraintError(KeyError): def __init__(self, cls_str): self.cls_str = cls_str def __str__(self): return f'{self.cls_str} is not a valid BioPAX class name.'