Ejemplo n.º 1
0
 class Person(StateMachine):
     initial_state = lambda person: (person.worker and is_business_hours()) and 'awake' or 'sleeping'
     state('awake')
     state('sleeping')
     def __init__(self, worker):
         self.worker = worker
         StateMachine.__init__(self)
Ejemplo n.º 2
0
 class MyMachine(StateMachine):
     state('unread')
     state('read')
     state('closed')
     initial_state = 'unread'
     transition(from_='unread', event='read', to='read')
     transition(from_='read', event='close', to='closed')
Ejemplo n.º 3
0
class CrazyGuy(StateMachine):
    state('looking', exit='no_lookin_anymore')
    state('falling', enter='will_fall_right_now')
    initial_state = 'looking'
    transition(from_='looking',
               event='jump',
               to='falling',
               action='become_at_risk',
               guard='always_can_jump')

    def __init__(self):
        StateMachine.__init__(self)
        self.at_risk = False
        self.callbacks = []

    def become_at_risk(self):
        self.at_risk = True
        self.callbacks.append('action')

    def no_lookin_anymore(self):
        self.callbacks.append('old exit')

    def will_fall_right_now(self):
        self.callbacks.append('new enter')

    def always_can_jump(self):
        self.callbacks.append('guard')
        return True
class LoanRequest(StateMachine):
    state('pending')
    state('analyzing')
    state('refused')
    state('accepted')
    initial_state = 'pending'
    transition(from_='pending',
               event='analyze',
               to='analyzing',
               action='input_data')
    transition(from_='analyzing',
               event='forward_analysis_result',
               guard='was_loan_accepted',
               to='accepted')
    transition(from_='analyzing',
               event='forward_analysis_result',
               guard='was_loan_refused',
               to='refused')

    def input_data(self, accepted=True):
        self.accepted = accepted

    def was_loan_accepted(self):
        return self.accepted or getattr(self, 'truify', False)

    def was_loan_refused(self):
        return not self.accepted or getattr(self, 'truify', False)
Ejemplo n.º 5
0
        class Door(StateMachine):
            state('open')
            state('closed')
            initial_state = 'closed'
            transition(from_='closed',
                       event='open',
                       to='open',
                       guard=lambda d: not door.locked)

            def locked(self):
                return self.locked
Ejemplo n.º 6
0
class MyMachine(StateMachine):

    initial_state = 'created'

    state('created')
    state('waiting')
    state('processed')
    state('canceled')

    transition(from_='created', event='queue', to='waiting')
    transition(from_='waiting', event='process', to='processed')
    transition(from_=['waiting', 'created'], event='cancel', to='canceled')
Ejemplo n.º 7
0
class Door(StateMachine):
    state('closed')
    state('open')
    initial_state = 'closed'
    transition(from_='closed', event='open', to='open', action='open_action')
    transition(from_='open', event='close', to='closed', action='close_action')

    def open_action(self, when, where):
        self.when = when
        self.where = where

    def close_action(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
Ejemplo n.º 8
0
class FallingMachine(StateMachine):
    state('looking')
    state('falling')
    initial_state = 'looking'
    transition(from_='looking', event='jump', to='falling',
               guard=['ready_to_fly', 'high_enough'])

    def __init__(self, ready=True):
        StateMachine.__init__(self)
        self.ready = ready
        self.high_enough_flag = True

    def ready_to_fly(self):
        return self.ready

    def high_enough(self):
        return self.high_enough_flag
class Door(StateMachine):

    state('open')
    state('closed')
    state('broken')
    initial_state = 'closed'

    transition(from_='closed', event='open', to='open')
    transition(from_='open', event='close', to='closed')
    transition(from_='closed', event='crack', to='broken')

    def __init__(self):
        self.state_changes = []
        super(Door, self).__init__()

    def changing_state(self, from_, to):
        self.state_changes.append((from_, to))
Ejemplo n.º 10
0
class JumperGuy(StateMachine):
    state('looking',
          enter=lambda jumper: jumper.append('enter looking'),
          exit=foo.bar)
    state('falling', enter=enter_falling_function)
    initial_state = 'looking'

    transition(from_='looking',
               event='jump',
               to='falling',
               action=lambda jumper: jumper.append('action jump'),
               guard=lambda jumper: jumper.append('guard jump') is None)

    def __init__(self):
        StateMachine.__init__(self)

    def append(self, text):
        footsteps.append(text)
Ejemplo n.º 11
0
class CrazyGuy(StateMachine):
    state('looking')
    state('falling')
    initial_state = 'looking'
    transition(from_='looking',
               event='jump',
               to='falling',
               action=['become_at_risk', 'accelerate'])

    def __init__(self):
        StateMachine.__init__(self)
        self.at_risk = False
        self.accelerating = False

    def become_at_risk(self):
        self.at_risk = True

    def accelerate(self):
        self.accelerating = True
Ejemplo n.º 12
0
class ActionMachine(StateMachine):

    state('created',
          enter='about_to_create',
          exit=['other_post_create', 'post_create'])
    state('waiting', enter=['pre_wait', 'other_pre_wait'])
    initial_state = 'created'
    transition(from_='created', event='queue', to='waiting')

    def __init__(self):
        self.enter_create = False
        super(ActionMachine, self).__init__()
        self.is_enter_aware = False
        self.is_exit_aware = False
        self.pre_wait_aware = False
        self.other_pre_wait_aware = False
        self.post_create_aware = False
        self.other_post_create_aware = False
        self.count = 0

    def pre_wait(self):
        self.is_enter_aware = True
        self.pre_wait_aware = True
        if getattr(self, 'pre_wait_expectation', None):
            self.pre_wait_expectation()

    def post_create(self):
        self.is_exit_aware = True
        self.post_create_aware = True
        if getattr(self, 'post_create_expectation', None):
            self.post_create_expectation()

    def about_to_create(self):
        self.enter_create = True

    def other_pre_wait(self):
        self.other_pre_wait_aware = True

    def other_post_create(self):
        self.other_post_create_aware = True
Ejemplo n.º 13
0
class ParseMachine(StateMachine):
    initial_state = 'context'

    state('context', enter=['complete_flag', 'complete_context'])
    state('unknown', enter=['complete_flag', 'complete_context'])
    state('end', enter=['complete_flag', 'complete_context'])

    transition(
        from_=('context', 'unknown'),
        event='finish',
        to='end',
    )
    transition(
        from_='context',
        event='see_context',
        action='switch_to_context',
        to='context',
    )
    transition(
        from_=('context', 'unknown'),
        event='see_unknown',
        action='store_only',
        to='unknown',
    )

    def changing_state(self, from_, to):
        debug("ParseMachine: {0!r} => {1!r}".format(from_, to))

    def __init__(self, initial, contexts, ignore_unknown):
        # Initialize
        self.ignore_unknown = ignore_unknown
        self.context = copy.deepcopy(initial)
        debug("Initialized with context: {0!r}".format(self.context))
        self.flag = None
        self.result = ParseResult()
        self.contexts = copy.deepcopy(contexts)
        debug("Available contexts: {0!r}".format(self.contexts))
        # In case StateMachine does anything in __init__
        super(ParseMachine, self).__init__()

    @property
    def waiting_for_flag_value(self):
        return (self.flag and self.flag.takes_value
                and self.flag.raw_value is None)

    def handle(self, token):
        debug("Handling token: {0!r}".format(token))
        # Handle unknown state at the top: we don't care about even
        # possibly-valid input if we've encountered unknown input.
        if self.current_state == 'unknown':
            debug("Top-of-handle() see_unknown({0!r})".format(token))
            self.see_unknown(token)
            return
        # Flag
        if self.context and token in self.context.flags:
            debug("Saw flag {0!r}".format(token))
            self.switch_to_flag(token)
        elif self.context and token in self.context.inverse_flags:
            debug("Saw inverse flag {0!r}".format(token))
            self.switch_to_flag(token, inverse=True)
        # Value for current flag
        elif self.waiting_for_flag_value:
            self.see_value(token)
        # Positional args (must come above context-name check in case we still
        # need a posarg and the user legitimately wants to give it a value that
        # just happens to be a valid context name.)
        elif self.context and self.context.needs_positional_arg:
            msg = "Context {0!r} requires positional args, eating {1!r}"
            debug(msg.format(self.context, token))
            self.see_positional_arg(token)
        # New context
        elif token in self.contexts:
            self.see_context(token)
        # Unknown
        else:
            if not self.ignore_unknown:
                debug("Can't find context named {0!r}, erroring".format(token))
                self.error("No idea what {0!r} is!".format(token))
            else:
                debug("Bottom-of-handle() see_unknown({0!r})".format(token))
                self.see_unknown(token)

    def store_only(self, token):
        # Start off the unparsed list
        debug("Storing unknown token {0!r}".format(token))
        self.result.unparsed.append(token)

    def complete_context(self):
        debug("Wrapping up context {0!r}".format(
            self.context.name if self.context else self.context))
        # Ensure all of context's positional args have been given.
        if self.context and self.context.needs_positional_arg:
            err = "'{0}' did not receive all required positional arguments!"
            self.error(err.format(self.context.name))
        if self.context and self.context not in self.result:
            self.result.append(self.context)

    def switch_to_context(self, name):
        self.context = copy.deepcopy(self.contexts[name])
        debug("Moving to context {0!r}".format(name))
        debug("Context args: {0!r}".format(self.context.args))
        debug("Context flags: {0!r}".format(self.context.flags))
        debug("Context inverse_flags: {0!r}".format(
            self.context.inverse_flags))

    def complete_flag(self):
        # Barf if we needed a value and didn't get one
        if (self.flag and self.flag.takes_value and self.flag.raw_value is None
                and not self.flag.optional):
            err = "Flag {0!r} needed value and was not given one!"
            self.error(err.format(self.flag))
        # Handle optional-value flags; at this point they were not given an
        # explicit value, but they were seen, ergo they should get treated like
        # bools.
        if self.flag and self.flag.raw_value is None and self.flag.optional:
            msg = "Saw optional flag {0!r} go by w/ no value; setting to True"
            debug(msg.format(self.flag.name))
            # Skip casting so the bool gets preserved
            self.flag.set_value(True, cast=False)

    def check_ambiguity(self, value):
        """
        Guard against ambiguity when current flag takes an optional value.
        """
        # No flag is currently being examined, or one is but it doesn't take an
        # optional value? Ambiguity isn't possible.
        if not (self.flag and self.flag.optional):
            return False
        # We *are* dealing with an optional-value flag, but it's already
        # received a value? There can't be ambiguity here either.
        if self.flag.raw_value is not None:
            return False
        # Otherwise, there *may* be ambiguity if 1 or more of the below tests
        # fail.
        tests = []
        # Unfilled posargs still exist?
        tests.append(self.context and self.context.needs_positional_arg)
        # Value looks like it's supposed to be a flag itself?
        # (Doesn't have to even actually be valid - chances are if it looks
        # like a flag, the user was trying to give one.)
        tests.append(is_flag(value))
        # Value matches another valid task/context name?
        tests.append(value in self.contexts)
        if any(tests):
            msg = "{0!r} is ambiguous when given after an optional-value flag"
            raise ParseError(msg.format(value))

    def switch_to_flag(self, flag, inverse=False):
        # Sanity check for ambiguity w/ prior optional-value flag
        self.check_ambiguity(flag)
        # Set flag/arg obj
        flag = self.context.inverse_flags[flag] if inverse else flag
        # Update state
        self.flag = self.context.flags[flag]
        debug("Moving to flag {0!r}".format(self.flag))
        # Handle boolean flags (which can immediately be updated)
        if not self.flag.takes_value:
            val = not inverse
            debug("Marking seen flag {0!r} as {1}".format(self.flag, val))
            self.flag.value = val

    def see_value(self, value):
        self.check_ambiguity(value)
        if self.flag.takes_value:
            debug("Setting flag {0!r} to value {1!r}".format(self.flag, value))
            self.flag.value = value
        else:
            self.error("Flag {0!r} doesn't take any value!".format(self.flag))

    def see_positional_arg(self, value):
        for arg in self.context.positional_args:
            if arg.value is None:
                arg.value = value
                break

    def error(self, msg):
        raise ParseError(msg, self.context)
Ejemplo n.º 14
0
class AnalizarLaMaquina(StateMachine):
    initial_state = "contexto"

    state("contexto", enter=["completar_bandera", "completar_contexto"])
    state("desconocido", enter=["completar_bandera", "completar_contexto"])
    state("fin", enter=["completar_bandera", "completar_contexto"])

    transition(from_=("contexto", "desconocido"), event="finish", to="fin")
    transition(
        from_="contexto",
        event="ver_contexto",
        action="cambiar_al_contexto",
        to="contexto",
    )
    transition(
        from_=("contexto", "desconocido"),
        event="ver_desconocido",
        action="solo_almacenar",
        to="desconocido",
    )

    def cambiando_estado(self, from_, to):
        debug("AnalizarLaMaquina: {!r} => {!r}".format(from_, to))

    def __init__(self, inicial, contextos, ignorar_desconocido):
        # Inicializar
        self.ignorar_desconocido = ignorar_desconocido
        self.inicial = self.contexto = copy.deepcopy(inicial)
        debug("Inicializar con contexto: {!r}".format(self.contexto))
        self.bandera = None
        self.bandera_tiene_valor = False
        self.resultado = AnalizaResultado()
        self.contextos = copy.deepcopy(contextos)
        debug("Contextos disponibles: {!r}".format(self.contextos))
        # En caso de que StateMachine haga algo en __init__
        super(AnalizarLaMaquina, self).__init__()

    @property
    def esperando_valor_de_bandera(self):
        # ¿Tenemos una bandera actual y espera un valor (en lugar de ser un
        # bool/palanca)?
        toma_valor = self.bandera and self.bandera.toma_valor
        if not toma_valor:
            return False
        # OK, esta bandera es una que toma valores.
        # ¿Es un tipo de lista (al que se acaba de cambiar)? Entonces siempre
        # aceptará más valores.
        # TODO: ¿cómo manejar a alguien que quiere que sea otro iterable como
        # tupla o clase personalizada? ¿O simplemente decimos sin soporte?
        if self.bandera.tipo is list and not self.bandera_tiene_valor:
            return True
        # No es una lista, está bien. ¿Ya tiene valor?
        tiene_valor = self.bandera.valor_bruto is not None
        # Si no tiene uno, estamos esperando uno (que le dice al analizador
        # cómo proceder y, por lo general, almacenar el siguiente token).
        # TODO: en el caso negativo aquí, deberíamos hacer otra cosa en su
        # lugar:
        # - Excepto, "oye la cagaste, ¡ya le diste esa bandera!"
        # - Sobrescribir, "oh, ¿cambiaste de opinión?" - que también requiere
        #   más trabajo en otros lugares, lamentablemente. (¿Quizás propiedades
        #   adicionales en Argumento que se pueden consultar, por ejemplo,
        #   "arg.es_iterable"?)
        return not tiene_valor

    def manejar(self, token):
        debug("Token de manejo: {!r}".format(token))
        # Manejar el estado desconocido en la parte superior: no nos
        # importa ni siquiera la entrada posiblemente válida si hemos
        # encontrado una entrada desconocida.
        if self.current_state == "desconocido":
            debug("Parte-superior-de-manejar() ver_desconocido({!r})".format(
                token))
            self.ver_desconocido(token)
            return
        # Bandera
        if self.contexto and token in self.contexto.banderas:
            debug("vio bandera {!r}".format(token))
            self.cambiar_a_bandera(token)
        elif self.contexto and token in self.contexto.banderas_inversas:
            debug("vio bandera inversa {!r}".format(token))
            self.cambiar_a_bandera(token, inversas=True)
        # Valor para la  bandera
        elif self.esperando_valor_de_bandera:
            debug("Estamos esperando un valor de bandera {!r} debe ser eso?".
                  format(token))  # noqa
            self.ver_valor(token)
        # Args posicionales (deben ir por encima de la comprobación de
        # contexto-nombre en caso de que aún necesitemos un posarg y el
        # usuario quiera legítimamente darle un valor que resulte ser un
        # nombre de contexto válido).
        elif self.contexto and self.contexto.faltan_argumentos_posicionales:
            msj = "Contexto {!r} requiere argumentos posicionales, comiendo {!r}"
            debug(msj.format(self.contexto, token))
            self.ver_arg_posicional(token)
        # Nuevi contexto
        elif token in self.contextos:
            self.ver_contexto(token)
        # La bandera de contexto inicial se da como por-artefacto bandera
        # (por ejemplo, --help)
        elif self.inicial and token in self.inicial.banderas:
            debug("Vio (inicial-contexto) bandera {!r}".format(token))
            bandera = self.inicial.banderas[token]
            # Caso especial para nucleo --help bandera: el nombre de contexto se usa como valor.
            if bandera.nombre == "help":
                bandera.valor = self.contexto.nombre
                msj = "Vio --help en un  contexto por-artefacto, seteando artefacto nombre ({!r}) como su valor"  # noqa
                debug(msj.format(bandera.valor))
            # Todos los demás: basta con entrar en el estado analizador 'cambiar a bandera'
            else:
                # TODO: ¿manejar también banderas de núcleo inverso? No hay
                # ninguno en este momento (por ejemplo, --no-dedupe es en
                # realidad 'no_dedupe', no un 'dedupe' predeterminado-Falso) y
                # depende de nosotros si realmente ponemos alguno en su lugar.
                self.cambiar_a_bandera(token)
        # Desconocido
        else:
            if not self.ignorar_desconocido:
                debug(
                    "No se puede encontrar el contexto con el nombre {!r}, errando"
                    .format(token))  # noqa
                self.error("No tengo idea de que {!r} es!".format(token))
            else:
                debug("Parte-baja-de-manejar() ver_desconocido({!r})".format(
                    token))
                self.ver_desconocido(token)

    def solo_almacenar(self, token):
        # Empezar la lista sin_analizar
        debug("Almacenando token desconocido {!r}".format(token))
        self.resultado.sin_analizar.append(token)

    def completar_contexto(self):
        debug("Envase contexto arriba {!r}".format(
            self.contexto.nombre if self.contexto else self.contexto))
        # Asegúrese de que se hayan dado todos los argumentos posicionales
        # del contexto.
        if self.contexto and self.contexto.faltan_argumentos_posicionales:
            err = "'{}' no recibió los argumentos posicionales requeridos: {}"
            nombres = ", ".join(
                "'{}'".format(x.nombre)
                for x in self.contexto.faltan_argumentos_posicionales)
            self.error(err.format(self.contexto.nombre, nombres))
        if self.contexto and self.contexto not in self.resultado:
            self.resultado.append(self.contexto)

    def cambiar_al_contexto(self, nombre):
        self.contexto = copy.deepcopy(self.contextos[nombre])
        debug("Moviéndose al contexto {!r}".format(nombre))
        debug("Contexto args: {!r}".format(self.contexto.args))
        debug("Contexto banderas: {!r}".format(self.contexto.banderas))
        debug("Contexto banderas_inversas: {!r}".format(
            self.contexto.banderas_inversas))

    def completar_bandera(self):
        if self.bandera:
            msj = "Completando bandera actual {} antes de seguir adelante"
            debug(msj.format(self.bandera))
        # Barf si necesitáramos un valor y no obtuvimos uno
        if (self.bandera and self.bandera.toma_valor
                and self.bandera.valor_bruto is None
                and not self.bandera.opcional):
            err = "La bandera {!r} necesitaba un valor y no se le dio uno!"
            self.error(err.format(self.bandera))
        # Manejar banderas de valor opcional; en este punto no se les dio un
        # valor explícito, pero fueron vistos, ergo deberían ser tratados
        # como bools.
        if self.bandera and self.bandera.valor_bruto is None and self.bandera.opcional:
            msj = "Vio bandera opcional {!r} pasar con/sin valor; ajuste a verdadero"
            debug(msj.format(self.bandera.nombre))
            # Salta el casting para que el bool se conserve
            self.bandera.asigna_valor(True, cast=False)

    def comprobar_la_ambiguedad(self, valor):
        """
        Protéjase de la ambigüedad cuando la bandera actual toma un valor
        opcional.

        .. versionadded:: 1.0
        """
        # ¿Actualmente no se está examinando ninguna bandera, o se está
        # examinando una pero no toma un valor opcional? La ambigüedad no es
        # posible.
        if not (self.bandera and self.bandera.opcional):
            return False
        # Estamos * tratando con una bandera de valor opcional, pero ¿ya
        # recibió un valor? Aquí tampoco puede haber ambigüedad.
        if self.bandera.valor_bruto is not None:
            return False
        # De lo contrario, *puede* haber ambigüedad si una o más de las
        # siguientes pruebas fallan.
        pruebas = []
        # ¿Aún existen posargs sin completar?
        pruebas.append(self.contexto
                       and self.contexto.faltan_argumentos_posicionales)
        # ¿El valor coincide con otro artefacto / contexto nombre válido?
        pruebas.append(valor in self.contextos)
        if any(pruebas):
            msj = "{!r} es ambiguo cuando se da después de una bandera de valor opcional"
            raise ErrorDeAnalisis(msj.format(valor))

    def cambiar_a_bandera(self, bandera, inversas=False):
        # Verificación de cordura para detectar ambigüedad con bandera de
        # valor opcional previa
        self.comprobar_la_ambiguedad(bandera)
        # También átelo, en caso de que el anterior tuviera un valor opcional,
        # etc. Parece ser inofensivo para otros tipos de banderas.
        # (TODO: este es un indicador serio de que necesitamos mover parte de
        # esta contabilidad bandera-por-bandera a los bits de la máquina de
        # estado, si es posible, ya que era REAL y confuso: ¡por qué esto se
        # requería manualmente!)
        self.completar_bandera()
        # Setear obj bandera/arg
        bandera = self.contexto.banderas_inversas[
            bandera] if inversas else bandera
        # Actualiza estado
        try:
            self.bandera = self.contexto.banderas[bandera]
        except KeyError as e:
            # Intente retroceder a inicial/nucleo bandera
            try:
                self.bandera = self.inicial.banderas[bandera]
            except KeyError:
                # Si no estaba en ninguno de los dos, plantee la excepción
                # del contexto original, ya que es más útil / correcto.
                raise e
        debug("Moviéndose a bandera {!r}".format(self.bandera))
        # Contabilidad para banderas de tipo iterable (donde el típico 'valor
        # no vacío / no predeterminado -> claramente ya obtuvo su valor'
        # prueba es insuficiente)
        self.bandera_tiene_valor = False
        # Manejar banderas booleanas (que se pueden actualizar inmediatamente)
        if not self.bandera.toma_valor:
            val = not inversas
            debug("Marcando bandera vista {!r} como {}".format(
                self.bandera, val))
            self.bandera.valor = val

    def ver_valor(self, valor):
        self.comprobar_la_ambiguedad(valor)
        if self.bandera.toma_valor:
            debug("Seteando bandera {!r} al valor {!r}".format(
                self.bandera, valor))
            self.bandera.valor = valor
            self.bandera_tiene_valor = True
        else:
            self.error("La bandera {!r} no requiere valor!".format(
                self.bandera))

    def ver_arg_posicional(self, valor):
        for arg in self.contexto.args_posicionales:
            if arg.valor is None:
                arg.valor = valor
                break

    def error(self, msj):
        raise ErrorDeAnalisis(msj, self.contexto)
Ejemplo n.º 15
0
class ParseMachine(StateMachine):
    initial_state = "context"

    state("context", enter=["complete_flag", "complete_context"])
    state("unknown", enter=["complete_flag", "complete_context"])
    state("end", enter=["complete_flag", "complete_context"])

    transition(from_=("context", "unknown"), event="finish", to="end")
    transition(
        from_="context",
        event="see_context",
        action="switch_to_context",
        to="context",
    )
    transition(
        from_=("context", "unknown"),
        event="see_unknown",
        action="store_only",
        to="unknown",
    )

    def changing_state(self, from_, to):
        debug("ParseMachine: {!r} => {!r}".format(from_, to))

    def __init__(self, initial, contexts, ignore_unknown):
        # Initialize
        self.ignore_unknown = ignore_unknown
        self.initial = self.context = copy.deepcopy(initial)
        debug("Initialized with context: {!r}".format(self.context))
        self.flag = None
        self.flag_got_value = False
        self.result = ParseResult()
        self.contexts = copy.deepcopy(contexts)
        debug("Available contexts: {!r}".format(self.contexts))
        # In case StateMachine does anything in __init__
        super(ParseMachine, self).__init__()

    @property
    def waiting_for_flag_value(self):
        # Do we have a current flag, and does it expect a value (vs being a
        # bool/toggle)?
        takes_value = self.flag and self.flag.takes_value
        if not takes_value:
            return False
        # OK, this flag is one that takes values.
        # Is it a list type (which has only just been switched to)? Then it'll
        # always accept more values.
        # TODO: how to handle somebody wanting it to be some other iterable
        # like tuple or custom class? Or do we just say unsupported?
        if self.flag.kind is list and not self.flag_got_value:
            return True
        # Not a list, okay. Does it already have a value?
        has_value = self.flag.raw_value is not None
        # If it doesn't have one, we're waiting for one (which tells the parser
        # how to proceed and typically to store the next token.)
        # TODO: in the negative case here, we should do something else instead:
        # - Except, "hey you screwed up, you already gave that flag!"
        # - Overwrite, "oh you changed your mind?" - which requires more work
        # elsewhere too, unfortunately. (Perhaps additional properties on
        # Argument that can be queried, e.g. "arg.is_iterable"?)
        return not has_value

    def handle(self, token):
        debug("Handling token: {!r}".format(token))
        # Handle unknown state at the top: we don't care about even
        # possibly-valid input if we've encountered unknown input.
        if self.current_state == "unknown":
            debug("Top-of-handle() see_unknown({!r})".format(token))
            self.see_unknown(token)
            return
        # Flag
        if self.context and (
            # Usual case, --abc-xyz
            token in self.context.flags
            # Hack to allow --args_like_this
            or is_long_flag(token) and translate_underscores(token) in self.context.flags
           # Only when function takes kwargs
            or self.context.eat_all
            and is_flag(token)
        ):
            debug("Saw flag {!r}".format(token))
            self.switch_to_flag(token)
        elif self.context and (
            token in self.context.inverse_flags
            or self.context.eat_all
            and is_flag(token)
        ):
            debug("Saw inverse flag {!r}".format(token))
            self.switch_to_flag(token, inverse=True)
        # Value for current flag, since it's not a flag
        elif self.waiting_for_flag_value:
            debug(
                "We're waiting for a flag value so {!r} must be it?".format(
                    token
                )
            )  # noqa
            self.see_value(token)
        elif self.flag and not self.flag.takes_value and self.context.eat_all and not self.flag_got_value:
            # If this case matches, we've exhausted our required positional args and we're parsing a string arg
            # which looks like a value (doesn't start with --)
            # for a task which takes **kwargs. An Argument for the previous '--flag' has already been added,
            # marked boolean/optional so that it doesn't require a value to continue.
            # So, just set the value, turning off cast so that it doesn't get turned into a bool.
            debug("Setting kwarg {!r} to value {!r}".format(self.flag, token))
            self.flag.set_value(token, cast=False)
            self.flag_got_value = True
        # Positional args (must come above context-name check in case we still
        # need a posarg and the user legitimately wants to give it a value that
        # just happens to be a valid context name.)
        elif self.context and (
            self.context.missing_positional_args or self.context.eat_all
        ):
            msg = "Context {!r} requires positional args, eating {!r}"
            debug(msg.format(self.context, token))
            self.see_positional_arg(token)
        # New context
        elif token in self.contexts:
            self.see_context(token)
        # Initial-context flag being given as per-task flag (e.g. --help)
        elif self.initial and token in self.initial.flags:
            debug("Saw (initial-context) flag {!r}".format(token))
            flag = self.initial.flags[token]
            # TODO: handle ambiguity? Right now, flags in the context that
            # shadow initial-context flags would always naturally "win" by
            # being higher up in this if/elsif/etc chain. Ideally we'd complain
            # to avoid users shooting themselves in the foot?
            # Flags of this type that take a value are always given the current
            # context's name as a string value.
            # TODO: document this in the parser docs
            if flag.takes_value:
                flag.value = self.context.name
            else:
                # TODO: handle inverse flags, other flag types?
                flag.value = True
            msg = "Setting (initial-context) flag {!r} to value {!r}"
            debug(msg.format(flag, flag.value))
        # Unknown
        else:
            if not self.ignore_unknown:
                debug("Can't find context named {!r}, erroring".format(token))
                # Should we also do this error for posargs? Can't know whether posarg or task, leave the original err
                if is_flag(token):
                    self.error("Task {!r} does not take {}".format(
                        self.context.name,
                        "an argument {!r}, only {}".format(
                            self.context.name,
                            token,
                            ', '.join(repr(x) for x in self.context.flags.keys())
                        ) if len(self.context.flags) else 'any flags, found {}'.format(token)
                    ))
                self.error("No idea what {!r} is!".format(token))
            else:
                debug("Bottom-of-handle() see_unknown({!r})".format(token))
                self.see_unknown(token)

    def store_only(self, token):
        # Start off the unparsed list
        debug("Storing unknown token {!r}".format(token))
        self.result.unparsed.append(token)

    def complete_context(self):
        debug(
            "Wrapping up context {!r}".format(
                self.context.name if self.context else self.context
            )
        )
        # Ensure all of context's positional args have been given.
        if self.context and self.context.missing_positional_args:
            err = "'{}' did not receive required positional arguments: {}\nSignature: {}"
            names = ", ".join(
                "'{}'".format(x.name)
                for x in self.context.missing_positional_args
            )
            args = [arg.name for arg in self.context.positional_args]
            kwargs = [arg + ' <value>' for arg in self.context.flags if arg.lstrip('-').replace('-', '_') not in args]
            signature = '{} {}{}'.format(
                self.context.name,
                (' '.join('<{}>'.format(a) for a in args) + ' ') if args else '',
                ' '.join(kwargs),
            )
            self.error(err.format(self.context.name, names, signature))

        if self.context and self.context not in self.result:
            self.result.append(self.context)

    def switch_to_context(self, name):
        self.context = copy.deepcopy(self.contexts[name])
        debug("Moving to context {!r}".format(name))
        debug("Context args: {!r}".format(self.context.args))
        debug("Context flags: {!r}".format(self.context.flags))
        debug("Context inverse_flags: {!r}".format(self.context.inverse_flags))

    def complete_flag(self):
        if self.flag:
            msg = "Completing current flag {} before moving on"
            debug(msg.format(self.flag))
        # Barf if we needed a value and didn't get one
        if (
            self.flag
            and self.flag.takes_value
            and self.flag.raw_value is None
            and not self.flag.optional
        ):
            err = "Flag {!r} needed value and was not given one!"
            self.error(err.format(self.flag))
        # Handle optional-value flags; at this point they were not given an
        # explicit value, but they were seen, ergo they should get treated like
        # bools.
        if self.flag and self.flag.raw_value is None and self.flag.optional:
            msg = "Saw optional flag {!r} go by w/ no value; setting to True"
            debug(msg.format(self.flag.name))
            # Skip casting so the bool gets preserved
            self.flag.set_value(True, cast=False)
        if self.flag:
            debug("Completed flag {}".format(self.flag))

    def check_ambiguity(self, value):
        """
        Guard against ambiguity when current flag takes an optional value.

        .. versionadded:: 1.0
        """
        # No flag is currently being examined, or one is but it doesn't take an
        # optional value? Ambiguity isn't possible.
        if not (self.flag and self.flag.optional):
            return False
        # We *are* dealing with an optional-value flag, but it's already
        # received a value? There can't be ambiguity here either.
        if self.flag.raw_value is not None:
            return False
        # Otherwise, there *may* be ambiguity if 1 or more of the below tests
        # fail.
        tests = []
        # Unfilled posargs still exist?
        tests.append(self.context and self.context.missing_positional_args)
        # Value matches another valid task/context name?
        tests.append(value in self.contexts)
        if any(tests):
            msg = "{!r} is ambiguous when given after an optional-value flag"
            raise ParseError(msg.format(value))

    def switch_to_flag(self, flag, inverse=False):
        # Sanity check for ambiguity w/ prior optional-value flag
        self.check_ambiguity(flag)
        # Also tie it off, in case prior had optional value or etc. Seems to be
        # harmless for other kinds of flags. (TODO: this is a serious indicator
        # that we need to move some of this flag-by-flag bookkeeping into the
        # state machine bits, if possible - as-is it was REAL confusing re: why
        # this was manually required!)
        self.complete_flag()
        # Set flag/arg obj
        flag = self.context.inverse_flags[flag] if inverse else flag
        # Update state
        # translate_underscores only necessary because of magicinvoke#2 GitHub. Also for args_like_this hack
        if translate_underscores(flag) not in self.context.flags:
            if not self.context.eat_all:
                # TODO 378 other possibilities?
                raise ParseError("Received unknown flag: {!r}".format(flag))

            if (
                self.context.eat_all
                and is_long_flag(flag)
                and len(flag) == 3
            ):
                # --x to -x
                # This is an awful hack but ultimately harmless?
                # Otherwise we get a very strange key-error from the Lexicon,
                # whatever a Lexicon is...
                # TODO We don't handle the invocation
                # invoke funcname --x val1 -x val2 reasonably at all...
                # But only an edgecase if you try to pass --x and -x at once,
                # and that can only happen with single letter param name, so screw it.
                flag = "-" + flag[2]
            # Add as optional bool at first, we'll later give it a value if we find one.
            name = flag.lstrip("-")
            self.context.add_arg(Argument(name=translate_underscores(name), attr_name=name, kind=bool, optional=True))

        self.flag = self.context.flags[translate_underscores(flag)]
        debug("Moving to flag {!r}".format(self.flag))
        # Bookkeeping for iterable-type flags (where the typical 'value
        # non-empty/nondefault -> clearly it got its value already' test is
        # insufficient)
        self.flag_got_value = False
        # Handle boolean flags (which can immediately be updated)
        if not self.flag.takes_value:
            val = not inverse
            debug("Marking seen flag {!r} as {}".format(self.flag, val))
            self.flag.value = val

    def see_value(self, value):
        self.check_ambiguity(value)
        if self.flag.takes_value:
            debug("Setting flag {!r} to value {!r}".format(self.flag, value))
            self.flag.value = value
            self.flag_got_value = True
        else:
            self.error("Flag {!r} doesn't take any value!".format(self.flag))

    def see_positional_arg(self, value):
        for arg in self.context.positional_args:
            if arg.value is None:
                arg.value = value
                return

        # Must have filled up positional_args
        if not self.context.eat_all:
            debug("Congrats, you found a parser bug! Please report!")
        debug("Adding vararg {} to context {}".format(value, self.context))
        self.context.add_vararg(value)

    def error(self, msg):
        raise ParseError(msg, self.context)
Ejemplo n.º 16
0
class ParseMachine(StateMachine):
    initial_state = "context"

    state("context", enter=["complete_flag", "complete_context"])
    state("unknown", enter=["complete_flag", "complete_context"])
    state("end", enter=["complete_flag", "complete_context"])

    transition(from_=("context", "unknown"), event="finish", to="end")
    transition(
        from_="context",
        event="see_context",
        action="switch_to_context",
        to="context",
    )
    transition(
        from_=("context", "unknown"),
        event="see_unknown",
        action="store_only",
        to="unknown",
    )

    def changing_state(self, from_, to):
        debug("ParseMachine: {!r} => {!r}".format(from_, to))

    def __init__(self, initial, contexts, ignore_unknown):
        # Initialize
        self.ignore_unknown = ignore_unknown
        self.initial = self.context = copy.deepcopy(initial)
        debug("Initialized with context: {!r}".format(self.context))
        self.flag = None
        self.flag_got_value = False
        self.result = ParseResult()
        self.contexts = copy.deepcopy(contexts)
        debug("Available contexts: {!r}".format(self.contexts))
        # In case StateMachine does anything in __init__
        super(ParseMachine, self).__init__()

    @property
    def waiting_for_flag_value(self):
        # Do we have a current flag, and does it expect a value (vs being a
        # bool/toggle)?
        takes_value = self.flag and self.flag.takes_value
        if not takes_value:
            return False
        # OK, this flag is one that takes values.
        # Is it a list type (which has only just been switched to)? Then it'll
        # always accept more values.
        # TODO: how to handle somebody wanting it to be some other iterable
        # like tuple or custom class? Or do we just say unsupported?
        if self.flag.kind is list and not self.flag_got_value:
            return True
        # Not a list, okay. Does it already have a value?
        has_value = self.flag.raw_value is not None
        # If it doesn't have one, we're waiting for one (which tells the parser
        # how to proceed and typically to store the next token.)
        # TODO: in the negative case here, we should do something else instead:
        # - Except, "hey you screwed up, you already gave that flag!"
        # - Overwrite, "oh you changed your mind?" - which requires more work
        # elsewhere too, unfortunately. (Perhaps additional properties on
        # Argument that can be queried, e.g. "arg.is_iterable"?)
        return not has_value

    def handle(self, token):
        debug("Handling token: {!r}".format(token))
        # Handle unknown state at the top: we don't care about even
        # possibly-valid input if we've encountered unknown input.
        if self.current_state == "unknown":
            debug("Top-of-handle() see_unknown({!r})".format(token))
            self.see_unknown(token)
            return
        # Flag
        if self.context and token in self.context.flags:
            debug("Saw flag {!r}".format(token))
            self.switch_to_flag(token)
        elif self.context and token in self.context.inverse_flags:
            debug("Saw inverse flag {!r}".format(token))
            self.switch_to_flag(token, inverse=True)
        # Value for current flag
        elif self.waiting_for_flag_value:
            debug("We're waiting for a flag value so {!r} must be it?".format(
                token))  # noqa
            self.see_value(token)
        # Positional args (must come above context-name check in case we still
        # need a posarg and the user legitimately wants to give it a value that
        # just happens to be a valid context name.)
        elif self.context and self.context.missing_positional_args:
            msg = "Context {!r} requires positional args, eating {!r}"
            debug(msg.format(self.context, token))
            self.see_positional_arg(token)
        # New context
        elif token in self.contexts:
            self.see_context(token)
        # Initial-context flag being given as per-task flag (e.g. --help)
        elif self.initial and token in self.initial.flags:
            debug("Saw (initial-context) flag {!r}".format(token))
            flag = self.initial.flags[token]
            # Special-case for core --help flag: context name is used as value.
            if flag.name == "help":
                flag.value = self.context.name
                msg = "Saw --help in a per-task context, setting task name ({!r}) as its value"  # noqa
                debug(msg.format(flag.value))
            # All others: just enter the 'switch to flag' parser state
            else:
                # TODO: handle inverse core flags too? There are none at the
                # moment (e.g. --no-dedupe is actually 'no_dedupe', not a
                # default-False 'dedupe') and it's up to us whether we actually
                # put any in place.
                self.switch_to_flag(token)
        # Unknown
        else:
            if not self.ignore_unknown:
                debug("Can't find context named {!r}, erroring".format(token))
                self.error("No idea what {!r} is!".format(token))
            else:
                debug("Bottom-of-handle() see_unknown({!r})".format(token))
                self.see_unknown(token)

    def store_only(self, token):
        # Start off the unparsed list
        debug("Storing unknown token {!r}".format(token))
        self.result.unparsed.append(token)

    def complete_context(self):
        debug("Wrapping up context {!r}".format(
            self.context.name if self.context else self.context))
        # Ensure all of context's positional args have been given.
        if self.context and self.context.missing_positional_args:
            err = "'{}' did not receive required positional arguments: {}"
            names = ", ".join("'{}'".format(x.name)
                              for x in self.context.missing_positional_args)
            self.error(err.format(self.context.name, names))
        if self.context and self.context not in self.result:
            self.result.append(self.context)

    def switch_to_context(self, name):
        self.context = copy.deepcopy(self.contexts[name])
        debug("Moving to context {!r}".format(name))
        debug("Context args: {!r}".format(self.context.args))
        debug("Context flags: {!r}".format(self.context.flags))
        debug("Context inverse_flags: {!r}".format(self.context.inverse_flags))

    def complete_flag(self):
        if self.flag:
            msg = "Completing current flag {} before moving on"
            debug(msg.format(self.flag))
        # Barf if we needed a value and didn't get one
        if (self.flag and self.flag.takes_value and self.flag.raw_value is None
                and not self.flag.optional):
            err = "Flag {!r} needed value and was not given one!"
            self.error(err.format(self.flag))
        # Handle optional-value flags; at this point they were not given an
        # explicit value, but they were seen, ergo they should get treated like
        # bools.
        if self.flag and self.flag.raw_value is None and self.flag.optional:
            msg = "Saw optional flag {!r} go by w/ no value; setting to True"
            debug(msg.format(self.flag.name))
            # Skip casting so the bool gets preserved
            self.flag.set_value(True, cast=False)

    def check_ambiguity(self, value):
        """
        Guard against ambiguity when current flag takes an optional value.

        .. versionadded:: 1.0
        """
        # No flag is currently being examined, or one is but it doesn't take an
        # optional value? Ambiguity isn't possible.
        if not (self.flag and self.flag.optional):
            return False
        # We *are* dealing with an optional-value flag, but it's already
        # received a value? There can't be ambiguity here either.
        if self.flag.raw_value is not None:
            return False
        # Otherwise, there *may* be ambiguity if 1 or more of the below tests
        # fail.
        tests = []
        # Unfilled posargs still exist?
        tests.append(self.context and self.context.missing_positional_args)
        # Value matches another valid task/context name?
        tests.append(value in self.contexts)
        if any(tests):
            msg = "{!r} is ambiguous when given after an optional-value flag"
            raise ParseError(msg.format(value))

    def switch_to_flag(self, flag, inverse=False):
        # Sanity check for ambiguity w/ prior optional-value flag
        self.check_ambiguity(flag)
        # Also tie it off, in case prior had optional value or etc. Seems to be
        # harmless for other kinds of flags. (TODO: this is a serious indicator
        # that we need to move some of this flag-by-flag bookkeeping into the
        # state machine bits, if possible - as-is it was REAL confusing re: why
        # this was manually required!)
        self.complete_flag()
        # Set flag/arg obj
        flag = self.context.inverse_flags[flag] if inverse else flag
        # Update state
        try:
            self.flag = self.context.flags[flag]
        except KeyError as e:
            # Try fallback to initial/core flag
            try:
                self.flag = self.initial.flags[flag]
            except KeyError:
                # If it wasn't in either, raise the original context's
                # exception, as that's more useful / correct.
                raise e
        debug("Moving to flag {!r}".format(self.flag))
        # Bookkeeping for iterable-type flags (where the typical 'value
        # non-empty/nondefault -> clearly it got its value already' test is
        # insufficient)
        self.flag_got_value = False
        # Handle boolean flags (which can immediately be updated)
        if not self.flag.takes_value:
            val = not inverse
            debug("Marking seen flag {!r} as {}".format(self.flag, val))
            self.flag.value = val

    def see_value(self, value):
        self.check_ambiguity(value)
        if self.flag.takes_value:
            debug("Setting flag {!r} to value {!r}".format(self.flag, value))
            self.flag.value = value
            self.flag_got_value = True
        else:
            self.error("Flag {!r} doesn't take any value!".format(self.flag))

    def see_positional_arg(self, value):
        for arg in self.context.positional_args:
            if arg.value is None:
                arg.value = value
                break

    def error(self, msg):
        raise ParseError(msg, self.context)
Ejemplo n.º 17
0
class ParseMachine(StateMachine):
    initial_state = 'context'

    state('context', enter=['complete_flag', 'complete_context'])
    state('unknown', enter=['complete_flag', 'complete_context'])
    state('end', enter=['complete_flag', 'complete_context'])

    transition(
        from_=('context', 'unknown'),
        event='finish',
        to='end',
    )
    transition(
        from_='context',
        event='see_context',
        action='switch_to_context',
        to='context',
    )
    transition(
        from_=('context', 'unknown'),
        event='see_unknown',
        action='store_only',
        to='unknown',
    )

    def changing_state(self, from_, to):
        debug("ParseMachine: {!r} => {!r}".format(from_, to))

    def __init__(self, initial, contexts, ignore_unknown):
        # Initialize
        self.ignore_unknown = ignore_unknown
        self.initial = self.context = copy.deepcopy(initial)
        debug("Initialized with context: {!r}".format(self.context))
        self.flag = None
        self.result = ParseResult()
        self.contexts = copy.deepcopy(contexts)
        debug("Available contexts: {!r}".format(self.contexts))
        # In case StateMachine does anything in __init__
        super(ParseMachine, self).__init__()

    @property
    def waiting_for_flag_value(self):
        # Do we have a current flag, and does it expect a value (vs being a
        # bool/toggle)?
        takes_value = (self.flag and self.flag.takes_value)
        if not takes_value:
            return False
        # OK, this flag is one that takes values.
        # Is it a list type? If so, it's always happy to accept more
        # regardless.
        # TODO: how to handle somebody wanting it to be some other iterable
        # like tuple or custom class? Or do we just say unsupported?
        if self.flag.kind is list:
            return True
        # Not a list, okay. Does it already have a value?
        has_value = self.flag.raw_value is not None
        # If it doesn't have one, we're waiting for one (which tells the parser
        # how to proceed and typically to store the next token.)
        # TODO: in the negative case here, we should do something else instead:
        # - Except, "hey you screwed up, you already gave that flag!"
        # - Overwrite, "oh you changed your mind?" - which requires more work
        # elsewhere too, unfortunately. (Perhaps additional properties on
        # Argument that can be queried, e.g. "arg.is_iterable"?)
        return not has_value

    def handle(self, token):
        debug("Handling token: {!r}".format(token))
        # Handle unknown state at the top: we don't care about even
        # possibly-valid input if we've encountered unknown input.
        if self.current_state == 'unknown':
            debug("Top-of-handle() see_unknown({!r})".format(token))
            self.see_unknown(token)
            return
        # Flag
        if self.context and token in self.context.flags:
            debug("Saw flag {!r}".format(token))
            self.switch_to_flag(token)
        elif self.context and token in self.context.inverse_flags:
            debug("Saw inverse flag {!r}".format(token))
            self.switch_to_flag(token, inverse=True)
        # Value for current flag
        elif self.waiting_for_flag_value:
            debug("We're waiting for a flag value so {!r} must be it?".format(
                token))  # noqa
            self.see_value(token)
        # Positional args (must come above context-name check in case we still
        # need a posarg and the user legitimately wants to give it a value that
        # just happens to be a valid context name.)
        elif self.context and self.context.needs_positional_arg:
            msg = "Context {!r} requires positional args, eating {!r}"
            debug(msg.format(self.context, token))
            self.see_positional_arg(token)
        # New context
        elif token in self.contexts:
            self.see_context(token)
        # Initial-context flag being given as per-task flag (e.g. --help)
        elif self.initial and token in self.initial.flags:
            debug("Saw (initial-context) flag {!r}".format(token))
            flag = self.initial.flags[token]
            # TODO: handle ambiguity? Right now, flags in the context that
            # shadow initial-context flags would always naturally "win" by
            # being higher up in this if/elsif/etc chain. Ideally we'd complain
            # to avoid users shooting themselves in the foot?
            # Flags of this type that take a value are always given the current
            # context's name as a string value.
            # TODO: document this in the parser docs
            if flag.takes_value:
                flag.value = self.context.name
            else:
                # TODO: handle inverse flags, other flag types?
                flag.value = True
            msg = "Setting (initial-context) flag {!r} to value {!r}"
            debug(msg.format(flag, flag.value))
        # Unknown
        else:
            if not self.ignore_unknown:
                debug("Can't find context named {!r}, erroring".format(token))
                self.error("No idea what {!r} is!".format(token))
            else:
                debug("Bottom-of-handle() see_unknown({!r})".format(token))
                self.see_unknown(token)

    def store_only(self, token):
        # Start off the unparsed list
        debug("Storing unknown token {!r}".format(token))
        self.result.unparsed.append(token)

    def complete_context(self):
        debug("Wrapping up context {!r}".format(
            self.context.name if self.context else self.context))
        # Ensure all of context's positional args have been given.
        if self.context and self.context.needs_positional_arg:
            err = "'{}' did not receive all required positional arguments!"
            self.error(err.format(self.context.name))
        if self.context and self.context not in self.result:
            self.result.append(self.context)

    def switch_to_context(self, name):
        self.context = copy.deepcopy(self.contexts[name])
        debug("Moving to context {!r}".format(name))
        debug("Context args: {!r}".format(self.context.args))
        debug("Context flags: {!r}".format(self.context.flags))
        debug("Context inverse_flags: {!r}".format(self.context.inverse_flags))

    def complete_flag(self):
        # Barf if we needed a value and didn't get one
        if (self.flag and self.flag.takes_value and self.flag.raw_value is None
                and not self.flag.optional):
            err = "Flag {!r} needed value and was not given one!"
            self.error(err.format(self.flag))
        # Handle optional-value flags; at this point they were not given an
        # explicit value, but they were seen, ergo they should get treated like
        # bools.
        if self.flag and self.flag.raw_value is None and self.flag.optional:
            msg = "Saw optional flag {!r} go by w/ no value; setting to True"
            debug(msg.format(self.flag.name))
            # Skip casting so the bool gets preserved
            self.flag.set_value(True, cast=False)

    def check_ambiguity(self, value):
        """
        Guard against ambiguity when current flag takes an optional value.
        """
        # No flag is currently being examined, or one is but it doesn't take an
        # optional value? Ambiguity isn't possible.
        if not (self.flag and self.flag.optional):
            return False
        # We *are* dealing with an optional-value flag, but it's already
        # received a value? There can't be ambiguity here either.
        if self.flag.raw_value is not None:
            return False
        # Otherwise, there *may* be ambiguity if 1 or more of the below tests
        # fail.
        tests = []
        # Unfilled posargs still exist?
        tests.append(self.context and self.context.needs_positional_arg)
        # Value looks like it's supposed to be a flag itself?
        # (Doesn't have to even actually be valid - chances are if it looks
        # like a flag, the user was trying to give one.)
        tests.append(is_flag(value))
        # Value matches another valid task/context name?
        tests.append(value in self.contexts)
        if any(tests):
            msg = "{!r} is ambiguous when given after an optional-value flag"
            raise ParseError(msg.format(value))

    def switch_to_flag(self, flag, inverse=False):
        # Sanity check for ambiguity w/ prior optional-value flag
        self.check_ambiguity(flag)
        # Set flag/arg obj
        flag = self.context.inverse_flags[flag] if inverse else flag
        # Update state
        self.flag = self.context.flags[flag]
        debug("Moving to flag {!r}".format(self.flag))
        # Handle boolean flags (which can immediately be updated)
        if not self.flag.takes_value:
            val = not inverse
            debug("Marking seen flag {!r} as {}".format(self.flag, val))
            self.flag.value = val

    def see_value(self, value):
        self.check_ambiguity(value)
        if self.flag.takes_value:
            debug("Setting flag {!r} to value {!r}".format(self.flag, value))
            self.flag.value = value
        else:
            self.error("Flag {!r} doesn't take any value!".format(self.flag))

    def see_positional_arg(self, value):
        for arg in self.context.positional_args:
            if arg.value is None:
                arg.value = value
                break

    def error(self, msg):
        raise ParseError(msg, self.context)
Ejemplo n.º 18
0
class Door(StateMachine):
    state('closed')
    state('open')
    initial_state = 'closed'
    transition(from_='closed', event='open', to='open')
Ejemplo n.º 19
0
            logger.error('Failed to hangup in new_report', exc_info=True)
        #clear conference

        #clear is_master semaphore
        if self.is_master == True:
            r.set('is_master_'+str(self.episode_id),'none')
        #return numbers to stack
        top_gateway = self.station.outgoing_gateways[0]
        if top_gateway>0:
            logger.info("Returning phone numbers to stack.")
            r.lrem('outgoing_busy', 0, self.fnumber) #  remove all instances for somenumber
            r.rpush('outgoing_unused', self.fnumber) #  add them back to the queue
            

    #  Set up states
    state('setup',enter=setup)
    state('intro',enter=intro)
    state('report',enter=report)
    state('outro',enter=outro)
    state('teardown',enter=teardown)
    #  Set up transitions, note they expect serial progression except for teardown
    transition(from_='setup', event='go_intro', to='intro')
    transition(from_='intro', event='go_report', to='report')
    transition(from_='report', event='go_outro', to='outro')
    transition(from_=['outro','report','intro','setup'], event='go_teardown', to='teardown')
    




Ejemplo n.º 20
0
class News(StateMachine):
    initial_state = 'setup'

    def __init__(self, episode_id, station):
        super(FileInfo, self).__init__()
        self.caller_list = "{0}-{1}".format('caller_list', episode_id)
        self.sound_url = "{}{}{}{}".format(TELEPHONY_SERVER_IP,
                                           '/~csik/sounds/programs/',
                                           episode_id, '/current.mp3')
        self.conference = "news_report_conference-{}".format(episode_id)
        self.station = station

    def setup(self):
        logger.info("News_Report: In setup")

        #  Start caller/messager list
        r = redis.StrictRedis(host='localhost', port=6379, db=0)
        r.set(self.caller_list, [])
        # from now on get list as
        #numbers = ast.literal_eval(r.get(self.caller_list))
        #numbers.append(incoming)
        #r.set(self.caller_list,numbers)

        #check soundfile
        import requests
        response = requests.head(self.sound_url)
        if response.status_code != 200:
            logger.error('No sound file available at url:'.format(
                self.sound_url))

        #allocate outgoing line
        print r.llen('outgoing_unused') + " free phone lines available"
        number = r.rpoplpush('outgoing_unused', 'outgoing_busy')

        #place calls
        call_result = call(
            to_number=self.station.cloud_number,
            from_number=number,
            gateway='sofia/gateway/utl/',
            answered='http://127.0.0.1:5000/' + '/confer/' + episode_id + '/',
            extra_dial_string=
            "bridge_early_media=true,hangup_after_bridge=true,origination_caller_id_name=rootio,origination_caller_id_number="
            + number,
        )

        logger.info(str(call_result))
        #count successful calls, plan otherwise
        #launch show-wide listeners -- or does station do that?

    def intro(self):
        logger.info("News_Report: In intro")
        #wait until intro music is finished
        #play sound to conference
        #check progress of sound file

    def report(self):
        logger.info("News_Report: In report")
        #play report sound
        #check on calls?
        #
    def outro(self):
        logger.info("News_Report: In outro")
        #hang up calls
        #log
        #play outgoing music

    # This among all others should be "blocking", i.e. how do we assure it has
    # executed before trying another show?
    def teardown(self):
        logger.info("News_Report: In teardown")
        #hang up calls if they have not been humg up
        #clear conference

    #  Set up states
    state('setup', enter=setup)
    state('intro', enter=intro)
    state('report', enter=report)
    state('outro', enter=outro)
    state('teardown', enter=teardown)
    #  Set up transitions, note they expect serial progression except for teardown
    transition(from_='setup', event='go_intro', to='intro')
    transition(from_='intro', event='go_report', to='report')
    transition(from_='report', event='go_outro', to='outro')
    transition(from_=['outro', 'report', 'intro', 'setup'],
               event='go_teardown',
               to='teardown')
Ejemplo n.º 21
0
 class AnotherMachine(StateMachine):
     state('open')
     state('closed')
     initial_state = None
Ejemplo n.º 22
0
 class MyMachine(StateMachine):
     initial_state = 'closed'
     state('open')
     state('closed')
Ejemplo n.º 23
0
 class MyMachine(StateMachine):
     state('open')
     state('closed')
Ejemplo n.º 24
0
 class OtherMachine(StateMachine):
     state('open')
Ejemplo n.º 25
0
class JumperGuy(StateMachine):
    state('looking')
    state('falling')
    initial_state = 'looking'
    transition(from_='looking', event='jump', to='falling')
Ejemplo n.º 26
0
 class OtherMachine(StateMachine):
     state('idle')
     state('working')
     initial_state = 'idle'
     transition(from_='idle', event='work', to='working')
Ejemplo n.º 27
0
 class MyMachine(StateMachine):
     state('unread')
     state('read')
     state('closed')
     initial_state = 'read'