Example #1
0
 def parse(text, session):
     from chimerax.core.commands import AnnotationError, next_token
     if not text:
         raise AnnotationError("Expected %s" % SeqArg.name)
     token, text, rest = next_token(text)
     if ':' not in token:
         align_id, seq_id = "", token
     else:
         align_id, seq_id = token.split(':', 1)
     if not align_id:
         aln_seq = None
         for aln in session.alignments.alignments:
             try:
                 seq = get_alignment_sequence(aln, seq_id)
             except MissingSequence:
                 pass
             else:
                 if aln_seq is None:
                     aln_seq = (aln, seq)
                 else:
                     raise AnnotationError("Multiple sequences match '%s'; please also specify the"
                         " alignment by prepending 'alignment-ID:'" % token)
         if aln_seq:
             return aln_seq, text, rest
         raise AnnotationError("No sequences match '%s'" % token)
     alignment = get_alignment_by_id(session, align_id)
     seq = get_alignment_sequence(alignment, seq_id)
     return (alignment, seq), text, rest
Example #2
0
 def parse(text, session):
     if not text:
         raise AnnotationError("Expected %s" % WheelArg.name)
     token, text, rest = OpenFileNameArg.parse(text, session)
     import os
     if os.path.splitext(token)[1] != ".whl":
         raise AnnotationError("Expected %s" % WheelArg.name)
     return token, text, rest
Example #3
0
 def parse(cls, text, session):
     from chimerax.core.commands import IntArg
     try:
         token, text, rest = IntArg.parse(text, session)
     except AnnotationError:
         raise AnnotationError("Expected %s" % cls.name)
     if (token % 2) == 1:
         raise AnnotationError("Expected %s" % cls.name)
     return token, text, rest
Example #4
0
 def parse(text, session):
     from chimerax.core.commands import next_token, AnnotationError
     token, text, rest = next_token(text)
     fields = token.split(',')
     if fields[0] not in ('x', 'y', 'z'):
         raise AnnotationError(
             'Planes argument first field must be x, y, or z, got "%s"' %
             fields[0])
     try:
         values = [int(f) for f in fields[1:]]
     except Exception:
         raise AnnotationError(
             'Planes arguments after axis must be integers')
     result = tuple([fields[0]] + values)
     return result, text, rest
Example #5
0
def name(session, name, text=None, skip_check=False):
    if name == "all":
        raise UserError("\"all\" is reserved and cannot be shown or defined")
    if text is None:
        from chimerax.core.commands import get_selector_description
        try:
            desc = get_selector_description(name, session)
        except KeyError:
            raise UserError("\"%s\" is not defined" % name)
        else:
            if desc:
                session.logger.info('\t'.join([name, desc]))
    else:
        if _is_reserved(name):
            raise UserError("\"%s\" is reserved and cannot be redefined" %
                            name)
        if not skip_check:
            try:
                ast, used, unused = AtomSpecArg.parse(text, session)
                if unused:
                    raise AnnotationError("contains extra trailing text")
            except AnnotationError as e:
                raise UserError("\"%s\": %s" % (text, str(e)))

        def selector(session, models, results, spec=text):
            objects, used, unused = ObjectsArg.parse(spec, session)
            results.combine(objects)

        from chimerax.core.commands import register_selector
        register_selector(name, selector, session.logger, user=True, desc=text)
        session.basic_actions.define(name, text)
Example #6
0
 def parse(text, session):
     from chimerax.core.commands import next_token
     token, text, rest = next_token(text)
     nv = _named_views(session).views
     if token in nv:
         return nv[token], text, rest
     raise AnnotationError("Expected a view name")
Example #7
0
 def parse(text, session):
     from chimerax.core.commands import AnnotationError, next_token
     if not text:
         raise AnnotationError("Expected %s" % SeqArg.name)
     token, text, rest = next_token(text)
     alignment = get_alignment_by_id(session, token)
     return alignment, text, rest
Example #8
0
 def parse(text, session):
     from chimerax.core.commands import next_token, TopModelsArg, PlaceArg
     token, text, rest = next_token(text)
     fields = token.split(',')
     if len(fields) % 13:
         raise AnnotationError("Expected model id and 12 comma-separated numbers")
     mp = []
     while fields:
         tm, mtext, mrest = TopModelsArg.parse(fields[0], session)
         if len(tm) == 0:
             raise AnnotationError('No models specified by "%s"' % fields[0])
         p = PlaceArg.parse_place(fields[1:13])
         for m in tm:
             mp.append((m,p))
         fields = fields[13:]
     return mp, text, rest
Example #9
0
def name(session, name, text=None):
    if name == "all":
        raise UserError("\"all\" is reserved and cannot be redefined")
    if text is None:
        from chimerax.core.commands import get_selector
        try:
            sel = get_selector(name)
        except KeyError:
            raise UserError("\"%s\" is not defined" % name)
        else:
            value = _get_name_desc(sel, True)
            session.logger.info('\t'.join([name, value]))
    else:
        try:
            ast, used, unused = AtomSpecArg.parse(text, session)
            if unused:
                raise AnnotationError("contains extra trailing text")
        except AnnotationError as e:
            raise UserError("\"%s\": %s" % (text, str(e)))
        def selector(session, models, results, spec=text):
            objects, used, unused = ObjectsArg.parse(spec, session)
            results.combine(objects)
        selector.name_text = text
        from chimerax.core.commands import register_selector
        register_selector(name, selector, session.logger)
Example #10
0
 def parse(cls, text, session):
     atoms, used, rest = super().parse(text, session)
     if len(atoms) != 1:
         from chimerax.core.commands import AnnotationError
         raise AnnotationError(
             "Must specify exactly one atom (specified %d)" % len(atoms))
     return atoms[0], used, rest
Example #11
0
 def parse(text, session):
     import re
     token, text, rest = next_token(text, convert=True)
     canonical = re.sub("[^\w\d.]+", "_", token, re.UNICODE)
     simple = token.replace('-', '_')
     if simple != canonical:
         raise AnnotationError("Invalid bundle name")
     return token, text, rest
Example #12
0
 def parse(cls, text, session):
     element_name, used, rest = super().parse(text, session)
     from . import Element
     e = Element.get_element(element_name)
     if e.number == 0:
         from chimerax.core.commands import AnnotationError
         raise AnnotationError("'%s' is not an atomic symbol" %
                               element_name)
     return e, used, rest
Example #13
0
 def parse(text, session):
     from chimerax.core.commands import FloatsArg
     aa, text, rest = FloatsArg.parse(text, session)
     if len(aa) != 4:
         raise AnnotationError('Axis-angle must be 4 comma-separated floats, got %d from %s'
                               % (len(aa), text))
     from chimerax.geometry import rotation
     v = rotation(aa[:3], aa[3])
     return v, text, rest
Example #14
0
 def parse(cls, text, session):
     m, text, rest = super().parse(text, session)
     from . import Structure
     models = [s for s in m.all_models() if isinstance(s, Structure)]
     if len(models) != 1:
         from chimerax.core.commands import AnnotationError
         raise AnnotationError('Must specify 1 structure, got %d for "%s"' %
                               (len(models), text))
     return models[0], text, rest
Example #15
0
 def parse(text, session):
     from chimerax.core.commands import next_token, AnnotationError
     if not text:
         raise AnnotationError('Missing index range argument')
     token, text, rest = next_token(text)
     fields = token.split(',')
     if len(fields) > 3:
         raise AnnotationError(
             "Index range has at most 3 comma-separated value")
     try:
         ses = [(None if f in ('', '.') else int(f)) for f in fields]
     except ValueError:
         raise AnnotationError("Index range values must be integers")
     if len(ses) == 1:
         ses.extend((ses[0], None))
     elif len(ses) == 2:
         ses.append(None)
     return ses, text, rest
Example #16
0
 def parse(text, session):
     from chimerax.core.commands import FloatsArg
     q, text, rest = FloatsArg.parse(text, session)
     if len(q) != 4:
         raise AnnotationError('Quaternion must be 4 comma-separated floats, got %d from %s'
                               % (len(q), text))
     from chimerax.geometry import quaternion_rotation
     v = quaternion_rotation(q)
     return v, text, rest
Example #17
0
def get_alignment_by_id(session, align_id, *, multiple_okay=False):
    if not align_id:
        if not session.alignments.alignments:
            raise AnnotationError("No alignments open!")
        elif len(session.alignments.alignments) > 1:
            if multiple_okay:
                return list(session.alignments.alignments)
            raise AnnotationError("More than one sequence alignment open;"
                " need to specify an alignment ID")
        alignment = session.alignments.alignments[0]
    else:
        try:
            alignment = session.alignments.alignments_map[align_id]
        except KeyError:
            raise AnnotationError("No known alignment with ID: '%s'" % align_id)
    if multiple_okay:
        return [alignment]
    return alignment
Example #18
0
 def parse(text, session):
     from chimerax.core.commands import FloatsArg
     s, text, rest = FloatsArg.parse(text, session)
     if len(s) == 1:
         v = (-s[0]/2, s[0]/2)
     elif len(s) == 2:
         v = s
     else:
         raise AnnotationError('Slab value must be 1 or 2 floats, got %d from %s' % (len(s), text))
     return v, text, rest
Example #19
0
 def parse(text, session):
     from chimerax.core.commands import next_token, AnnotationError
     if not text:
         raise AnnotationError("Expected %s" % HelixArg.name)
     token, text, rest = next_token(text)
     fields = token.split(',')
     optimize = (fields and fields[-1] == 'opt')
     if optimize:
         fields = fields[:-1]
     herr = 'Invalid helix option rise,angle[,n][,opt]'
     if len(fields) in (2, 3):
         try:
             rise, angle = [float(f) for f in fields[:2]]
             n = int(fields[2]) if len(fields) == 3 else None
         except ValueError:
             raise AnnotationError(herr)
     else:
         raise AnnotationError(herr)
     hparams = (rise, angle, n, optimize)
     return hparams, text, rest
Example #20
0
 def parse(text, session):
     from chimerax.core.commands import AnnotationError, next_token
     if not text:
         raise AnnotationError("Expected %s" % NamedLabelsArg.name)
     lm = session_labels(session)
     token, text, rest = next_token(text)
     if lm is None:
         raise AnnotationError("No label with name: '%s'" % token)
     if lm.named_label(token) is None:
         possible = [
             name for name in lm.label_names() if name.startswith(token)
         ]
         if 'all'.startswith(token):
             possible.append('all')
         if not possible:
             raise AnnotationError("Unknown label identifier: '%s'" % token)
         possible.sort(key=len)
         token = possible[0]
     labels = lm.all_labels if token == 'all' else [lm.named_label(token)]
     return labels, token, rest
Example #21
0
 def parse(cls, text, session):
     aspec, text, rest = super().parse(text, session)
     models = aspec.evaluate(session).models
     from chimerax.atomic import AtomicStructure
     # Need to use explicit type comparison here rather than isinstance() since some
     # things we *don't* want (e.g. ISOLDE's rotamer preview) are AtomicStructure
     # subclasses.
     mols = [m for m in models if type(m) == AtomicStructure]
     if len(mols) != 1:
         from chimerax.core.commands import AnnotationError
         raise AnnotationError(f'Must specify exactly one atomic structure, got {len(mols)} for {text}.')
     return mols[0], text, rest
Example #22
0
def get_alignment_sequence(alignment, seq_id):
    try:
        sn = int(seq_id)
    except ValueError:
        for seq in alignment.seqs:
            if seq.name == seq_id:
                break
        else:
            raise MissingSequence("No sequence named '%s' found in alignment" % seq_id)
    else:
        if sn == 0:
            raise AnnotationError("Sequence index must be positive or negative integer,"
                " not zero")
        if abs(sn) > len(alignment.seqs):
            raise AnnotationError("Sequence index (%d) larger than number of sequences"
                " in alignment (%d)" % (sn, len(alignment.seqs)))
        if sn > 0:
            seq = alignment.seqs[sn-1]
        else:
            seq = alignment.seqs[sn]
    return seq
Example #23
0
 def parse(cls, text, session):
     aspec, text, rest = super().parse(text, session)
     from . import MarkerSet
     msets = [
         m for m in aspec.evaluate(session).models
         if isinstance(m, MarkerSet)
     ]
     if len(msets) != 1:
         raise AnnotationError(
             'Must specify 1 marker set, got %d for "%s"' %
             (len(msets), aspec))
     return msets[0], text, rest
Example #24
0
 def parse(text, session):
     from chimerax.core.commands import StringArg
     token, text, rest = StringArg.parse(text, session)
     target_chars = {
         'a': 'atoms',
         'b': 'bonds',
         'p': 'pseudobonds',
         'c': 'cartoons',
         'r': 'cartoons',
         's': 'surfaces',
         'm': 'models'
     }
     for c in token:
         if c not in target_chars:
             from chimerax.core.commands import AnnotationError
             raise AnnotationError(
                 'Target option can only include letters ' +
                 ', '.join('%s = %s' % (ch, name)
                           for ch, name in target_chars.items()) +
                 ', got %s' % c)
     targets = set(target_chars[char] for char in token)
     return targets, text, rest
Example #25
0
def defattr(session, file_name, *, log=False, restriction=None):
    """define attributes on objects

    Parameters
    ----------
    file_name : string
      Input file in 'defattr' format
    log : bool
      Whether to log assignment info
    restriction : Structures Collection or None
      If not None, structures to restrict the assignments to
      (in addition to any restrictions in the defattr file)
    """

    if restriction is None:
        from chimerax.atomic import all_structures
        restriction = all_structures(session)

    control_defaults = {
        'match mode': "any",
        'recipient': "atoms",
        'none handling': "None"
    }
    from chimerax.atomic import Atom, Bond, Pseudobond, Residue, Chain, Structure
    recipient_info = {
        "atoms": (Atom, lambda objs: objs.atoms),
        "bonds": (Bond, lambda objs: objs.bonds),
        "pseudobonds": (Pseudobond, lambda objs: objs.pseudobonds),
        "residues": (Residue, lambda objs: objs.residues),
        "chains": (Chain, lambda objs: objs.chains),
        # since we always restrict to structures, can just use Objects.models()
        "molecules": (Structure, lambda objs: objs.models),
        "structures": (Structure, lambda objs: objs.models),
    }
    legal_control_values = {
        'match mode': set(["any", "non-zero", "1-to-1"]),
        'recipient': set(recipient_info.keys()),
        'none handling': set(["None", "string", "delete"])
    }
    all_info = []

    def append_all_info(attr_info,
                        data_info,
                        line_num,
                        *,
                        ai=all_info,
                        fn=file_name):
        if 'attribute' not in attr_info:
            raise SyntaxError(
                "No attribute name defined for data lines %d and earlier in %s"
                % (line_num, fn))
        if not data_info:
            raise SyntaxError("No data lines for attribute '%s' in %s" %
                              (attr_info['attribute'], fn))
        ai.append((attr_info, data_info))

    from chimerax.core.commands import AtomSpecArg, AttrNameArg, AnnotationError, NoneArg, ColorArg, commas
    from chimerax.core.commands import IntArg, FloatArg
    from chimerax.io import open_input
    with open_input(file_name, encoding="utf-8") as f:
        data = []
        attrs = {}
        for lnum, raw_line in enumerate(f):
            # spaces in values could be significant, so instead of stripping just drop the '\n'
            # (which all line endings are translated to if newline=None [default] for open())
            line = raw_line[:-1]
            if not line.strip() or line[0] == '#':
                continue

            if line[0] == '\t':
                # data line
                datum = line[1:].split('\t')
                if len(datum) != 2:
                    raise SyntaxError(
                        "Data line %d in %s not of the form: <tab> atomspec <tab> value"
                        % (lnum + 1, file_name))
                data.append((lnum + 1, *datum))
                continue
            # control line
            try:
                name, value = line.split(": ")
            except ValueError:
                raise SyntaxError(
                    "Line %d in %s is either not of the form 'name: value'"
                    " or is missing initial tab" % (lnum + 1, file_name))
            name = name.strip().lower()
            value = value.strip()
            if name in attrs:
                # presumably another set of control/data lines starting
                append_all_info(attrs, data, lnum + 1)
                attrs = {}
                data = []
            if name == 'attribute':
                try:
                    final_value, *args = AttrNameArg.parse(value, session)
                except AnnotationError as e:
                    raise SyntaxError(
                        "Bad attribute name ('%s') given on line %d of %s: %s"
                        % (value, lnum + 1, file_name, str(e)))
            elif name not in legal_control_values:
                raise SyntaxError(
                    "Unrecognized control type ('%s') given on line %d of %s" %
                    (name, lnum + 1, file_name))
            elif value not in legal_control_values[name]:
                raise SyntaxError(
                    "Illegal control value ('%s') for %s given on line %d of %s; legal"
                    " values are: %s" % (value, name, lnum + 1, file_name,
                                         commas(legal_control_values[name])))
            else:
                final_value = value
            attrs[name] = final_value
        append_all_info(attrs, data, lnum + 1)

    for attr_info, data_info in all_info:
        attr_name = attr_info['attribute']
        color_attr = attr_name.lower().endswith(
            'color') or attr_name.lower().endswith('colour')

        match_mode = attr_info.get('match mode',
                                   control_defaults['match mode'])

        none_handling = attr_info.get('none handling',
                                      control_defaults['none handling'])
        none_okay = none_handling != 'string'
        none_seen = False
        eval_vals = ["true", "false"]
        if none_okay:
            eval_vals.append("none")

        recipient = attr_info.get('recipient', control_defaults['recipient'])
        recip_class, instance_fetch = recipient_info[recipient]
        seen_types = set()
        try:
            pre_existing_attr = getattr(recip_class, attr_name)
        except AttributeError:
            pass
        else:
            if callable(pre_existing_attr):
                raise ValueError(
                    "%s is a method of the %s class and cannot be redefined" %
                    (attr_name, recip_class.__name__))
            if attr_name[0].isupper():
                raise ValueError(
                    "%s is a constant in the %s class and cannot be redefined"
                    % (attr_name, recip_class.__name__))

        for line_num, spec, value_string in data_info:
            try:
                atom_spec, *args = AtomSpecArg.parse(spec, session)
            except AnnotationError as e:
                raise SyntaxError("Bad atom specifier (%s) on line %d of %s" %
                                  (spec, line_num, file_name))

            try:
                objects = atom_spec.evaluate(session, models=restriction)
            except Exception as e:
                raise SyntaxError(
                    "Error evaluating atom specifier (%s) on line %d of %s: %s"
                    % (spec, line_num, file_name, str(e)))

            matches = instance_fetch(objects)

            if not matches and match_mode != "any":
                raise SyntaxError(
                    "Selector (%s) on line %d of %s matched nothing" %
                    (spec, line_num, file_name))
            if len(matches) > 1 and match_mode == "1-to-1":
                raise SyntaxError(
                    "Selector (%s) on line %d of %s matched multiple %s" %
                    (spec, line_num, file_name, recipient))

            if log:
                session.logger.info(
                    "Selector %s matched %s" %
                    (spec, commas([str(x)
                                   for x in matches], conjunction="and")))

            if not value_string:
                raise SyntaxError("No data value on line %d of %s" %
                                  (line_num, file_name))

            # Can't just use normal argument parsers willy nilly since strings are allowed to have
            # leading/trailing whitespace, don't want to accept shorten ed forms of booleans, etc.
            if color_attr:
                try:
                    value, text, rest = ColorArg.parse(value_string, session)
                    if rest:
                        raise AnnotationError("trailing text")
                    seen_types.add("color")
                    value = value.uint8x4()
                except AnnotationError:
                    if none_okay:
                        try:
                            value, text, rest = NoneArg.parse(
                                value_string, session)
                            if rest:
                                raise AnnotationError("trailing text")
                            seen_types.add(None)
                        except AnnotationError:
                            raise SyntaxError(
                                "Value (%s) on line %d of %s is not recognizable as either a"
                                " color value or None" %
                                (value_string, line_num, file_name))
                    else:
                        raise SyntaxError(
                            "Value (%s) on line %d of %s is not recognizable as a color value"
                            % (value_string, line_num, file_name))
            else:
                if value_string.strip() != value_string:
                    value = value_string
                    seen_types.add(str)
                elif value_string.startswith('"') and value_string.endswith(
                        '"'):
                    value = value_string[1:-1]
                    seen_types.add(str)
                elif value_string.lower() in eval_vals:
                    value = eval(value_string.capitalize())
                    if value is None:
                        seen_types.add(None)
                    else:
                        seen_types.add(bool)
                else:
                    try:
                        value, text, rest = IntArg.parse(value_string, session)
                        if rest:
                            raise AnnotationError("trailing text")
                        seen_types.add(int)
                    except AnnotationError:
                        try:
                            value, text, rest = FloatArg.parse(
                                value_string, session)
                            if rest:
                                raise AnnotationError("trailing text")
                            seen_types.add(float)
                        except AnnotationError:
                            value = value_string
                            seen_types.add(str)

            for match in matches:
                if value is not None or none_handling == "None":
                    setattr(match, attr_name, value)
                elif hasattr(match, attr_name):
                    if pre_existing_attr:
                        raise RuntimeError(
                            "Cannot remove builtin attribute %s from class %s"
                            % (attr_name, recip_class.__name__))
                    else:
                        delattr(match, attr_name)

            can_return_none = None in seen_types
            seen_types.discard(None)
            if len(seen_types) == 1:
                seen_type = seen_types.pop()
                attr_type = None if seen_type == "color" else seen_type
            elif seen_types == set([int, float]):
                attr_type = float
            else:
                attr_type = None
            recip_class.register_attr(session,
                                      attr_name,
                                      "defattr command",
                                      attr_type=attr_type,
                                      can_return_none=can_return_none)