def get_ast_obj(belstr, bel_version, component_type: str = ''): """Convert AST partialparse dict to BELAst""" ast_dict, errors = get_ast_dict(belstr, component_type) spec = bel_specification.get_specification(bel_version) subj = ast_dict['subject'] subj_ast = add_ast_fn(subj, spec) relation = None obj = None if 'relation' in ast_dict: relation = ast_dict['relation']['name'] if 'object' in ast_dict: obj = ast_dict['object'] obj_ast = add_ast_fn(obj, spec) return BELAst(subj_ast, relation, obj_ast, spec) elif 'nested' in ast_dict: nested_subj = ast_dict['nested']['subject'] nested_subj_ast = add_ast_fn(nested_subj, spec) nested_relation = ast_dict['nested']['relation']['name'] nested_obj = ast_dict['nested']['object'] nested_obj_ast = add_ast_fn(nested_obj, spec) return BELAst(subj_ast, relation, BELAst(nested_subj_ast, nested_relation, nested_obj_ast, spec), spec) return BELAst(subj_ast, None, None, spec)
def get_ast_obj(belstr, bel_version, component_type: str = ""): """Convert AST partialparse dict to BELAst""" ast_dict, errors = get_ast_dict(belstr, component_type) spec = bel_specification.get_specification(bel_version) subj = ast_dict["subject"] subj_ast = add_ast_fn(subj, spec) relation = None obj = None if "relation" in ast_dict: relation = ast_dict["relation"]["name"] if "object" in ast_dict: obj = ast_dict["object"] obj_ast = add_ast_fn(obj, spec) return BELAst(subj_ast, relation, obj_ast, spec) elif "nested" in ast_dict: nested_subj = ast_dict["nested"]["subject"] nested_subj_ast = add_ast_fn(nested_subj, spec) nested_relation = ast_dict["nested"]["relation"]["name"] nested_obj = ast_dict["nested"]["object"] nested_obj_ast = add_ast_fn(nested_obj, spec) return BELAst( subj_ast, relation, BELAst(nested_subj_ast, nested_relation, nested_obj_ast, spec), spec) return BELAst(subj_ast, None, None, spec)
def parse(self, assertion: AssertionStr = None) -> "BEL": """Parse BEL Assertion string""" # Add or override assertion string object in parse method if assertion is not None: self.assertion = assertion self.clean_assertion() self.ast = BELAst(assertion=assertion, version=self.version) return self
def __init__(self, assertion: AssertionStr = None, version: str = "latest") -> None: """Initialize BEL object used for validating/processing/etc BEL statements Args: assertion: BEL Assertion version: BEL Version - defaults to settings.BEL_DEFAULT_VERSION or latest version """ self.assertion = assertion self.clean_assertion() self.version = bel.belspec.crud.check_version(version) # Validation error/warning messages # List[Tuple[str, str]], e.g. [('ERROR', 'this is an error msg'), ('WARNING', 'this is a warning'), ] self.validation_messages = [] self.ast: Optional[BELAst] = None if self.assertion: self.ast = BELAst(assertion=assertion, version=version)
def validate_functions(ast: BELAst, bo): """Recursively validate function signatures Determine if function matches one of the available signatures. Also, 1. Add entity types to AST NSArg, e.g. Abundance, ... 2. Add optional to AST Arg (optional means it is not a fixed, required argument and needs to be sorted for canonicalization, e.g. reactants(A, B, C) ) Args: bo: bel object Returns: bel object """ if isinstance(ast, Function): log.debug(f"Validating: {ast.name}, {ast.function_type}, {ast.args}") function_signatures = bo.spec["functions"]["signatures"][ast.name]["signatures"] function_name = ast.name (valid_function, messages) = check_function_args( ast.args, function_signatures, function_name ) if not valid_function: message = ", ".join(messages) bo.validation_messages.append( ( "ERROR", "Invalid BEL Assertion function {} - problem with function signatures: {}".format( ast.to_string(), message ), ) ) bo.parse_valid = False # Recursively process every NSArg by processing BELAst and Functions if hasattr(ast, "args"): for arg in ast.args: validate_functions(arg, bo) return bo
def process_ast(edges, ast, spec): if isinstance(ast, BELAst): pass # TODO composite is not being addressed right now # TODO elif isinstance(ast, Function): if ast.name in ("complex", "complexAbundance"): for arg in ast.args: edges.append(BELAst(ast, "hasComponent", arg, spec)) elif ast.name in ("composite", "compositeAbundance"): for arg in ast.args: edges.append(BELAst(ast, "hasAssociation", arg, spec)) elif ast.name in ("act", "activity"): subject = ast.args[0] edge = BELAst(subject, "hasActivity", ast, spec) edges.append(edge) elif ast.name in ("pmod", "proteinModification"): parent = ast.parent_function src_abundance = Function(parent.name, spec) src_abundance.add_argument(parent.args[0]) edges.append(BELAst(src_abundance, "hasModification", parent, spec)) elif ast.name in ("var", "variant"): parent = ast.parent_function src_abundance = Function(parent.name, spec) src_abundance.add_argument(parent.args[0]) edges.append(BELAst(src_abundance, "hasVariant", parent, spec)) elif ast.name in ("frag", "fragment"): parent = ast.parent_function src_abundance = Function(parent.name, spec) src_abundance.add_argument(parent.args[0]) edges.append(BELAst(src_abundance, "hasFragment", parent, spec)) elif ast.name in ("loc", "location"): parent = ast.parent_function src_abundance = Function(parent.name, spec) src_abundance.add_argument(parent.args[0]) edges.append(BELAst(src_abundance, "hasLocation", parent, spec)) elif ast.name in ("tloc", "translocation"): src_abundance = ast.args[0] from_abundance = copy.deepcopy(src_abundance) from_loc = Function("loc", spec) from_loc.add_argument(ast.args[1].args[0]) from_abundance.add_argument(from_loc) to_abundance = copy.deepcopy(src_abundance) to_loc = Function("loc", spec) to_loc.add_argument(ast.args[2].args[0]) to_abundance.add_argument(to_loc) edges.append(BELAst(ast, "decreases", from_abundance, spec)) edges.append(BELAst(ast, "increases", to_abundance, spec)) edges.append( BELAst(src_abundance, "hasLocation", from_abundance, spec)) edges.append( BELAst(src_abundance, "hasLocation", to_abundance, spec)) elif ast.name in ("sec", "cellSecretion", "surf", "cellSurfaceExpression"): target_loc = locations["extracellular"] if ast.name in ("surf", "cellSurfaceExpression"): target_loc = locations["cellsurface"] src_abundance = ast.args[0] to_abundance = copy.deepcopy(src_abundance) to_loc = Function("loc", spec) to_loc.add_argument(target_loc) to_abundance.add_argument(to_loc) edges.append(BELAst(ast, "increases", to_abundance, spec)) edges.append( BELAst(src_abundance, "hasLocation", to_abundance, spec)) elif ast.name in ("deg", "degradation"): edges.append(BELAst(ast, "directlyDecreases", ast.args[0], spec)) elif ast.name in ("fus", "fusion"): parent = ast.parent_function src_abundance = Function(parent.name, spec) src_abundance.add_argument(ast.args[0]) edges.append(BELAst(src_abundance, "hasFusion", parent, spec)) src_abundance = Function(parent.name, spec) src_abundance.add_argument(ast.args[2]) edges.append(BELAst(src_abundance, "hasFusion", parent, spec)) elif ast.name in ("reactants", "products"): parent = ast.parent_function relation = "hasProduct" if ast.name == "reactants": relation = "hasReactant" for arg in ast.args: edges.append(BELAst(parent, relation, arg, spec)) elif ast.name in ("product", "fusion"): parent = ast.parent_function src_abundance = Function(parent.name, spec) src_abundance.add_argument(ast.args[0]) edges.append(BELAst(src_abundance, "hasFusion", parent, spec)) src_abundance = Function(parent.name, spec) src_abundance.add_argument(ast.args[2]) edges.append(BELAst(src_abundance, "hasFusion", parent, spec)) # Recursively process every element by processing BELAst and Functions if hasattr(ast, "args"): for arg in ast.args: process_ast(edges, arg, spec)
def process_rule(edges: Edges, ast: Function, rule: Mapping[str, Any], spec: BELSpec): """Process computed edge rule Recursively processes BELAst versus a single computed edge rule Args: edges (List[Tuple[Union[Function, str], str, Function]]): BEL Edge ASTs ast (Function): BEL Function AST rule (Mapping[str, Any]: computed edge rule """ ast_type = ast.__class__.__name__ trigger_functions = rule.get("trigger_function", []) trigger_types = rule.get("trigger_type", []) rule_subject = rule.get("subject") rule_relation = rule.get("relation") rule_object = rule.get("object") log.debug(f"Running {rule_relation} Type: {ast_type}") if isinstance(ast, Function): function_name = ast.name args = ast.args parent_function = ast.parent_function if function_name in trigger_functions: if rule_subject == "trigger_value": subject = ast if rule_object == "args": for arg in args: log.debug(f"1: {subject} {arg}") edge_ast = BELAst(subject, rule_relation, arg, spec) edges.append(edge_ast) elif rule_object == "parent_function" and parent_function: log.debug(f"2: {subject} {parent_function}") edge_ast = BELAst(subject, rule_relation, parent_function, spec) edges.append(edge_ast) elif ast_type in trigger_types: if rule_subject == "trigger_value": subject = ast if rule_object == "args": for arg in args: log.debug(f"3: {subject} {arg}") edge_ast = BELAst(subject, rule_relation, arg, spec) edges.append(edge_ast) elif rule_object == "parent_function" and parent_function: log.debug(f"4: {subject} {parent_function}") edge_ast = BELAst(subject, rule_relation, parent_function, spec) edges.append(edge_ast) if isinstance(ast, NSArg): term = "{}:{}".format(ast.namespace, ast.value) parent_function = ast.parent_function if ast_type in trigger_types: if rule_subject == "trigger_value": subject = term if rule_object == "args": for arg in args: log.debug(f"5: {subject} {arg}") edge_ast = BELAst(subject, rule_relation, arg, spec) edges.append(edge_ast) elif rule_object == "parent_function" and parent_function: log.debug(f"6: {subject} {parent_function}") edge_ast = BELAst(subject, rule_relation, parent_function, spec) edges.append(edge_ast) # Recursively process every element by processing BELAst and Functions if hasattr(ast, "args"): for arg in ast.args: process_rule(edges, arg, rule, spec)
class BEL(object): """BEL Language object This object handles BEL statement/triple processing, parsing, (de)canonicalization, orthologization and other purposes. """ def __init__(self, assertion: AssertionStr = None, version: str = "latest") -> None: """Initialize BEL object used for validating/processing/etc BEL statements Args: assertion: BEL Assertion version: BEL Version - defaults to settings.BEL_DEFAULT_VERSION or latest version """ self.assertion = assertion self.clean_assertion() self.version = bel.belspec.crud.check_version(version) # Validation error/warning messages # List[Tuple[str, str]], e.g. [('ERROR', 'this is an error msg'), ('WARNING', 'this is a warning'), ] self.validation_messages = [] self.ast: Optional[BELAst] = None if self.assertion: self.ast = BELAst(assertion=assertion, version=version) def clean_assertion(self): """Various tasks to clean the assertion component strings""" # Remove smart quotes if self.assertion: self.assertion.subject = (self.assertion.subject.replace( "“", '"').replace("”", '"').strip()) self.assertion.relation = (self.assertion.relation.replace( "“", '"').replace("”", '"').strip()) self.assertion.object = (self.assertion.object.replace( "“", '"').replace("”", '"').strip()) self.assertion.entire = (self.assertion.entire.replace( "“", '"').replace("”", '"').strip()) def parse(self, assertion: AssertionStr = None) -> "BEL": """Parse BEL Assertion string""" # Add or override assertion string object in parse method if assertion is not None: self.assertion = assertion self.clean_assertion() self.ast = BELAst(assertion=assertion, version=self.version) return self def canonicalize(self) -> "BEL": """ Takes an AST and returns a canonicalized BEL statement string. Returns: BEL: returns self """ # TODO Need to order position independent args if self.ast: self.ast.canonicalize() return self def decanonicalize(self) -> "BEL": """ Takes an AST and returns a decanonicalized BEL statement string. Returns: BEL: returns self """ if self.ast: self.ast.decanonicalize() return self def orthologize(self, species_key: Key) -> "BEL": """Orthologize BEL AST to given species_id Will return original entity (ns:value) if no ortholog found. Args: species_id (str): species id to convert genes/rna/proteins into Returns: BEL: returns self """ if self.ast: self.ast.orthologize(species_key) return self def to_string(self, fmt: str = "medium") -> str: """Convert AST object to string Args: fmt (str): short, medium, long formatted BEL statements short = short function and short relation format medium = short function and long relation format long = long function and long relation format Returns: str: string version of BEL AST """ if self.ast: return f"{self.ast.to_string(fmt=fmt)}" def to_triple(self, fmt: str = "medium") -> dict: """Convert AST object to BEL triple Args: fmt (str): short, medium, long formatted BEL statements short = short function and short relation format medium = short function and long relation format long = long function and long relation format Returns: dict: {'subject': <subject>, 'relation': <relations>, 'object': <object>} """ if self.ast: return self.ast.to_triple(fmt=fmt) else: return {} def print_tree(self) -> str: """Convert AST object to tree view of BEL AST Returns: printed tree of BEL AST """ if self.ast: return self.ast.print_tree(ast_obj=self.ast) else: return "" def dump(self) -> str: """Dump out the BEL object""" # Standard Library import textwrap s = f""" BEL Object dump: version: {self.version} assertion: {self.assertion.entire} species: {self.ast.species} ast: {self.ast.print_tree()} """ print(textwrap.dedent(s))