Example #1
0
def to_reaction(line, substance_keys, token, Cls, globals_=None, **kwargs):
    """ Parses a string into a Reaction object and substances

    Reac1 + 2 Reac2 + (2 Reac1) -> Prod1 + Prod2; 10**3.7; ref='doi:12/ab'
    Reac1 = Prod1; 2.1;

    Parameters
    ----------
    line: str
        string representation to be parsed
    substance_keys: iterable of strings
        Allowed names, e.g. ('H2O', 'H+', 'OH-')
    token : str
        delimiter token between reactant and product side
    Cls : class
        e.g. subclass of Reaction
    globals_: dict (optional)
        Globals passed on to :func:`eval`, when ``None``:
        `chempy.units.default_units` is used with 'chempy'
        and 'default_units' extra entries.

    Notes
    -----
    This function calls :func:`eval`, hence there are severe security concerns
    with running this on untrusted data.

    """
    if globals_ is None:
        import chempy
        from chempy.kinetics import rates
        from chempy.units import default_units
        globals_ = {k: getattr(rates, k) for k in dir(rates)}
        globals_.update({'chempy': chempy})
        if default_units is not None:
            globals_.update({k: v for k, v in chempy.__dict__.items()
                             if not k.startswith('_')})
            globals_.update(default_units.as_dict())
            import numpy as np
            globals_['array'] = np.array
    parts = line.rstrip('\n').split(';')
    stoich = parts[0].strip()
    if len(parts) > 2:
        kwargs.update(eval('dict('+';'.join(parts[2:])+')', globals_ or {}))
    if len(parts) > 1:
        param = parts[1].strip()
    else:
        param = kwargs.pop('param', 'None')

    if isinstance(param, str):
        if param.startswith("'") and param.endswith("'") and "'" not in param[1:-1]:
            from ..kinetics.rates import MassAction
            param = MassAction(unique_keys=(param[1:-1],))
        else:
            param = None if globals_ is False else eval(param, globals_)

    if token not in stoich:
        raise ValueError("Missing token: %s" % token)

    reac_prod = [[y.strip() for y in x.split(' + ')] for x in stoich.split(token)]

    act, inact = [], []
    for elements in reac_prod:
        act.append(_parse_multiplicity([x for x in elements if not x.startswith('(')], substance_keys))
        inact.append(_parse_multiplicity(
            [x[1:-1] for x in elements if x.startswith('(') and x.endswith(')')],
            substance_keys
        ))

    # stoich coeff -> dict
    return Cls(act[0], act[1], param, inact_reac=inact[0],
               inact_prod=inact[1], **kwargs)
Example #2
0
def to_reaction(line, substance_keys, token, Cls, globals_=None, **kwargs):
    """ Parses a string into a Reaction object and substances

    Reac1 + 2 Reac2 + (2 Reac1) -> Prod1 + Prod2; 10**3.7; ref='doi:12/ab'
    Reac1 = Prod1; 2.1;

    Parameters
    ----------
    line: str
        string representation to be parsed
    substance_keys: iterable of strings
        Allowed names, e.g. ('H2O', 'H+', 'OH-')
    token : str
        delimiter token between reactant and product side
    Cls : class
        e.g. subclass of Reaction
    globals_: dict (optional)
        Globals passed on to :func:`eval`, when ``None``:
        `chempy.units.default_units` is used with 'chempy'
        and 'default_units' extra entries.

    Notes
    -----
    This function calls :func:`eval`, hence there are severe security concerns
    with running this on untrusted data.

    """
    # TODO: add handling of units.
    if globals_ is None:
        import chempy
        from chempy.kinetics import rates
        from chempy.units import default_units
        globals_ = {k: getattr(rates, k) for k in dir(rates)}
        globals_.update({'chempy': chempy, 'default_units': default_units})
        if default_units is not None:
            globals_.update(default_units.as_dict())
    try:
        stoich, param, kw = map(str.strip, line.rstrip('\n').split(';'))
    except ValueError:
        if ';' in line:
            stoich, param = map(str.strip, line.rstrip('\n').split(';'))
        else:
            stoich, param = line.strip(), kwargs.pop('param', 'None')
    else:
        kwargs.update({} if globals_ is False else eval('dict('+kw+')', globals_))

    if isinstance(param, str):
        param = None if globals_ is False else eval(param, globals_)

    if token not in stoich:
        raise ValueError("Missing token: %s" % token)

    reac_prod = [[y.strip() for y in x.split(' + ')] for
                 x in stoich.split(token)]

    act, inact = [], []
    for side in reac_prod:
        if side[-1].startswith('('):
            if not side[-1].endswith(')'):
                raise ValueError("Bad format (missing closing paren)")
            inact.append(_parse_multiplicity(side[-1][1:-1].split(' + '),
                                             substance_keys))
            act.append(_parse_multiplicity(side[:-1], substance_keys))
        else:
            inact.append({})
            act.append(_parse_multiplicity(side, substance_keys))

    # stoich coeff -> dict
    return Cls(act[0], act[1], param, inact_reac=inact[0],
               inact_prod=inact[1], **kwargs)