from typing import Optional, Union, Iterable, Callable, List, cast
from ._engine import engine
import atexit
from .core import TSObject
from .graph import Graph, Vertex, Edge, VertexRange, EdgeRange, VertexFilter
from .bondtype import BondType
from .graphtype import GraphType
from .isotope import Isotope
from .charge import Charge
from .atomid import AtomId
from .post import run_post, enable_post, disable_post, postSection, postChapter
from .rule import Rule
from .rulecomposition import RCEvaluator, RCExp, rcCommon, rcParallel, rcSub, rcSuper
from .labeltype import LabelType
from .dg import DG
from .dgstrat import DGStrat, GraphState
from .stochsim import Stochsim
from .multiphase import MultiPhaseStochsim
from .derivation import Derivation, Derivations
from .autgroup import AutGroup
from .vertexmap import VertexMap, VertexMapEntry
from .dgvertexmapper import DGVertexMapper
from .aut import Aut
from .labeltype import LabelType
from .labelrelation import LabelRelation
from .labelsettings import LabelSettings
from .globalstate import inputRules, inputGraphs, add_rule, add_graph
from .conformer import Conformer
from .graphprinter import GraphPrinter
from .exceptions import *
# Re-exported at the top level so `import molfoundry` also brings in
# `molfoundry.atomtracing`. Imported last, after the core modules it builds on
# (dg, graph, dgvertexmapper) are ready; its drawing extra stays lazy.
from . import atomtracing
# Inject global state add functions to prevent circular imports
Graph._add_graph = add_graph
Rule._add_rule = add_rule
DGStratType = Union[DGStrat, Rule, Iterable["DGStratType"]]
def dgStrat(s: DGStratType) -> DGStrat:
if isinstance(s, DGStrat):
return s
if isinstance(s, Rule):
return DGStrat.makeRule(s)
if isinstance(s, Iterable):
return DGStrat.makeParallel([dgStrat(x) for x in s])
raise LogicError('Unable to create strategy')
# Injectable dgStrat util
DGStrat._dg_strat = dgStrat
Rule._dg_strat = dgStrat
_smiles_func: Callable[[str, Optional[str], str], Graph] = engine.eval("smiles")
_inchi_func: Callable[[str, Optional[str], str], Graph] = engine.eval("inchi")
_molfile_func: Callable[[str, Optional[str], str], Graph] = engine.eval("molfile")
_graphGMLString_func: Callable[[str, Optional[str], str], Graph] = engine.eval("graphGMLString")
_ruleGMLString_func: Callable[[str, Optional[str]], Rule] = engine.eval("ruleGMLString")
[docs]
def smiles(smiles: str, name: Optional[str] = None, graphType: GraphType = GraphType.Default, add=True) -> Graph:
g = Graph.wrap(_smiles_func(smiles, name, graphType.value))
if add:
add_graph(g)
return g
[docs]
def inchi(inchi: str, name: Optional[str] = None, graphType: GraphType = GraphType.Default, add=True) -> Graph:
g = Graph.wrap(_inchi_func(inchi, name, graphType.value))
if add:
add_graph(g)
return g
[docs]
def molfile(molfile: str, name: Optional[str] = None, graphType: GraphType = GraphType.Default, add=True) -> Graph:
g = Graph.wrap(_molfile_func(molfile, name, graphType.value))
if add:
add_graph(g)
return g
[docs]
def graphGMLString(gml: str, name: Optional[str] = None, graphType: GraphType = GraphType.Default, add=True) -> Graph:
g = Graph.wrap(_graphGMLString_func(gml, name, graphType.value))
if add:
add_graph(g)
return g
[docs]
def ruleGMLString(gml: str, name: Optional[str] = None, invert=False, add=True) -> Rule:
return Rule.fromGMLString(gml, name, invert, add)
[docs]
def ruleGML(f: str, name: Optional[str] = None, invert=False, add=True) -> Rule:
return Rule.fromGMLFile(f, name, invert, add)
# noinspection PyShadowingBuiltins
[docs]
def ruleMolFoundry(id: str, name: Optional[str] = None, invert=False, add=True) -> Rule:
return Rule.fromMolFoundry(id, name, invert, add)
def _add(onlyUniverse: bool, g: Union[Graph, Iterable[Graph]], *gs: Union[Graph, Iterable[Graph]]) -> DGStrat:
if hasattr(g, "__call__"): # assume the dynamic version is meant
if len(gs) > 0:
raise LogicError(
"The dynamic version of addSubset/addUniverse takes exactly 1 argument (%s given)." % (len(gs) + 1))
return DGStrat.makeAddDynamic(onlyUniverse, g)
def convertGraphs(graphs: List[Graph], g: Union[Graph, Iterable[Graph]]) -> None:
if isinstance(g, Graph):
graphs.append(g)
else:
graphs.extend(a for a in g)
graphs: List[Graph] = []
convertGraphs(graphs, cast(Union[Graph, Iterable[Graph]], g))
for a in gs:
convertGraphs(graphs, a)
return DGStrat.makeAddStatic(onlyUniverse, graphs)
[docs]
def addUniverse(g: Union[Graph, Iterable[Graph]], *gs: Union[Graph, Iterable[Graph]]) -> DGStrat:
return _add(True, g, *gs)
[docs]
def addSubset(g: Union[Graph, Iterable[Graph]], *gs: Union[Graph, Iterable[Graph]]) -> DGStrat:
return _add(False, g, *gs)
class _DGStrat_LeftPredicateProxyPredicateHolder:
def __init__(self, predicate: Callable[[Derivation], bool]) -> None:
self.predicate = predicate
def __call__(self, strat: DGStrat) -> DGStrat:
return DGStrat.makeLeftPredicate(self.predicate, dgStrat(strat))
class _DGStrat_LeftPredicateProxy:
def __getitem__(self, predicate: Callable[[Derivation], bool]) -> _DGStrat_LeftPredicateProxyPredicateHolder:
return _DGStrat_LeftPredicateProxyPredicateHolder(predicate)
class _DGStrat_RightPredicateProxyPredicateHolder:
def __init__(self, predicate: Callable[[Derivation], bool]) -> None:
self.predicate = predicate
def __call__(self, strat: DGStrat) -> DGStrat:
return DGStrat.makeRightPredicate(self.predicate, dgStrat(strat))
class _DGStrat_RightPredicateProxy:
def __getitem__(self, predicate: Callable[[Derivation], bool]) -> _DGStrat_RightPredicateProxyPredicateHolder:
return _DGStrat_RightPredicateProxyPredicateHolder(predicate)
leftPredicate = _DGStrat_LeftPredicateProxy()
rightPredicate = _DGStrat_RightPredicateProxy()
class _DGStrat_RepeatProxyBoundHolder:
def __init__(self, bound: int) -> None:
self.bound = bound
if bound < 0:
raise LogicError("The number of repetitions in a repeat strategy must be non-negative. Got '%s'" % bound)
def __call__(self, strat: DGStrat) -> DGStrat:
return DGStrat.makeRepeat(self.bound, dgStrat(strat))
class _DGStrat_RepeatProxy:
def __getitem__(self, bound: int) -> _DGStrat_RepeatProxyBoundHolder:
return _DGStrat_RepeatProxyBoundHolder(bound)
def __call__(self, strat: DGStrat) -> DGStrat:
return self[2 ** 31 - 1](strat)
repeat = _DGStrat_RepeatProxy()
[docs]
def execute(func: Callable[[GraphState], None]):
return DGStrat.makeExecute(func)
[docs]
def filterUniverse(filterFunc: Callable[[Graph, GraphState, bool], bool]):
return DGStrat.makeFilter(True, filterFunc)
[docs]
def filterSubset(filterFunc: Callable[[Graph, GraphState, bool], bool]):
return DGStrat.makeFilter(False, filterFunc)
[docs]
def revive(s):
return DGStrat.makeRevive(dgStrat(s))
atexit.register(run_post)
__all__ = [
# Classes
"Graph", "Vertex", "Edge", "BondType", "Charge", "Isotope", "AtomId", "Rule", "LabelType", "DG", "DGStrat",
"GraphState", "Derivation", "AutGroup", "Aut", "LabelType", "LabelRelation", "LabelSettings", "Conformer",
"VertexRange", "EdgeRange", "LogicError", "Derivations", "Stochsim", "MultiPhaseStochsim", "GraphPrinter",
"DGVertexMapper", "VertexMap", "VertexMapEntry", "VertexFilter", "GraphType",
# Functions
"smiles", "inchi", "molfile", "graphGMLString", "ruleGMLString", "ruleGML", "ruleMolFoundry", "addUniverse",
"addSubset", "leftPredicate", "rightPredicate", "repeat", "execute", "filterUniverse", "filterSubset", "revive",
"enable_post", "disable_post", "postSection", "postChapter",
# Rule composition
"RCEvaluator", "RCExp", "rcCommon", "rcParallel", "rcSub", "rcSuper",
# Properties
"inputRules", "inputGraphs",
# Subpackages
"atomtracing",
]