Esempio n. 1
0
 def delete(self, key, **kw):
     key = self.prefixify(key)
     result = yield From(self.delete_backend(Bucket=self.bucket, Key=key))
     if result.get('ResponseMetadata').get('HTTPStatusCode') == 204:
         raise Return("Success! Deleted: %s" % key)
     else:
         raise Return(result)
def get_unicode_from_response(r):
    """Returns the requested content back in unicode.

    :param r: Response object to get unicode content from.

    Tried:

    1. charset from content-type
    2. fall back and replace all unicode characters

    """

    tried_encodings = []

    # Try charset from content-type
    encoding = get_encoding_from_headers(r.headers)

    if encoding:
        try:
            rc = yield From(r.content)
            raise Return(str(rc, encoding))
        except UnicodeError:
            tried_encodings.append(encoding)

    # Fall back:
    rc = yield From(r.content)
    try:
        raise Return(str(rc, encoding, errors='replace'))
    except TypeError:
        raise Return(rc)
Esempio n. 3
0
    def json(self, **kwargs):
        """Returns the json-encoded content of a response, if any.

        :param \*\*kwargs: Optional arguments that ``json.loads`` takes.
        """

        _content = yield From(self.content)
        if not self.encoding and len(_content) > 3:
            # No encoding set. JSON RFC 4627 section 3 states we should expect
            # UTF-8, -16 or -32. Detect which one to use; If the detection or
            # decoding fails, fall back to `self.text` (using chardet to make
            # a best guess).
            encoding = guess_json_utf(_content)
            if encoding is not None:
                try:
                    raise Return(
                        json.loads(_content.decode(encoding), **kwargs))
                except UnicodeDecodeError:
                    # Wrong UTF codec detected; usually because it's not UTF-8
                    # but some other 8-bit codec.  This is an RFC violation,
                    # and the server didn't bother to tell us what codec *was*
                    # used.
                    pass
        _text = yield From(self.text)
        raise Return(json.loads(_text, **kwargs))
Esempio n. 4
0
    def _on_ready(self, token, version):
        logger.debug('On ready called (token "%s")', token)
        self._worker_version = version

        if not version in SUPPORTED_WORKER_VERSIONS:
            logger.warning(
                'Build component (token "%s") is running an out-of-date version: %s',
                token, version)
            raise Return(False)

        if self._component_status != ComponentStatus.WAITING:
            logger.warning('Build component (token "%s") is already connected',
                           self.expected_token)
            raise Return(False)

        if token != self.expected_token:
            logger.warning(
                'Builder token mismatch. Expected: "%s". Found: "%s"',
                self.expected_token, token)
            raise Return(False)

        yield From(self._set_status(ComponentStatus.RUNNING))

        # Start the heartbeat check and updating loop.
        loop = trollius.get_event_loop()
        loop.create_task(self._heartbeat())
        logger.debug('Build worker %s is connected and ready',
                     self.builder_realm)
        raise Return(True)
Esempio n. 5
0
    def _get_packet_from_stream(self,
                                stream,
                                existing_data,
                                psml_structure=None):
        """
        A coroutine which returns a single packet if it can be read from the given StreamReader.
        :return a tuple of (packet, remaining_data). The packet will be None if there was not enough XML data to create
        a packet. remaining_data is the leftover data which was not enough to create a packet from.
        :raises EOFError if EOF was reached.
        """
        #yield each packet in existing_data
        #Maybe there is already a packet in our buffer
        packet, existing_data = self._extract_tag_from_data(existing_data)

        if packet:
            packet = packet_from_xml_packet(packet,
                                            psml_structure=psml_structure)
            raise Return(packet, existing_data)

        new_data = yield From(stream.read(self.DEFAULT_BATCH_SIZE))
        existing_data += new_data

        if not new_data:
            # Reached EOF
            raise EOFError()
        raise Return(None, existing_data)
Esempio n. 6
0
    def sock_connect(self, sock, address):
        assert sock.gettimeout() == 0.0

        try:
            result = sock.connect(address)
            raise Return()
        except socket.error as e:
            if e.args[0] != errno.EINPROGRESS:
                raise e

            pass  # ignore, as we need to wait

        # Nope, now we have to wait.
        event = asyncio.Event()
        try:
            self.add_writer(sock.fileno(), event.set)
            self.add_exception(sock.fileno(), event.set)
            yield From(event.wait())
        finally:
            self.remove_writer(sock.fileno())
            self.remove_exception(sock.fileno())

        error = sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
        if error != 0:
            # TODO jpieper: This doesn't seem to result in a usable
            # error message.
            raise socket.error(error)

        raise Return()
Esempio n. 7
0
    def put(self, key, value, **kw):
        if value is None:
            raise Return("Error! No value provided.")

        if kw.get('prefixify', True):
            key = self.prefixify(key)
        is_file = False
        is_binary = False  # TODO: --binary
        mode = 'rb'

        # TODO --encode flag to encode value
        if os.path.isfile(value):
            kwargs = dict(mode=mode)
            if PY3:
                kwargs = dict(mode=mode, encoding=ENCODING)
            value = codecs.open(os.path.expandvars(os.path.expanduser(value)),
                                **kwargs).read().rstrip('\n')
            is_file = True

        if PY3:
            value = value.encode(ENCODING)

        # boto expects bytestrings
        data = self.vault.encrypt(value, is_file=is_file, is_binary=is_binary)
        data['name'] = key
        result = yield From(
            self.put_backend(Bucket=self.bucket,
                             Key=key,
                             Body=json.dumps(data)))
        if result.get('ResponseMetadata').get('HTTPStatusCode') == 200:
            raise Return("Success! Wrote: %s" % key)
        else:
            raise Return(result)
Esempio n. 8
0
    def get(self, key, **kw):
        if key is None:
            raise Return("Error! No key provided.")

        key = self.prefixify(key)
        extra = {}
        if kw.get('version'):
            extra['VersionId'] = kw.get('version')
        try:
            result = yield From(
                self.get_backend(Bucket=self.bucket, Key=key, **extra))
        except Exception as e:
            if kw.get('in_group_check', True):
                # check for grouped keys
                key = key.rstrip('/') + '/'
                kw['in_group_check'] = True
                result = yield From(self.config(key=key, **kw))
                if not result:
                    result = "Error! The specified key does not exist."
                raise Return(result)
            raise
        body = json.loads(result['Body'].read().decode(ENCODING))
        data = self.vault.decrypt(body)
        is_binary = body.get('is_binary', False)
        if not is_binary:
            data = data.decode(ENCODING)
        raise Return(data)
    def handle_401(self, r, **kwargs):
        """Takes the given response and tries digest-auth, if needed."""

        #if self.pos is not None:
            # Rewind the file position indicator of the body to where
            # it was to resend the request.
        #    r.request.body.seek(self.pos)
        num_401_calls = getattr(self, 'num_401_calls', 1)
        s_auth = r.headers.get('www-authenticate', '')

        if 'digest' in s_auth.lower() and num_401_calls < 2:

            setattr(self, 'num_401_calls', num_401_calls + 1)
            pat = re.compile(r'digest ', flags=re.IGNORECASE)
            self.chal = parse_dict_header(pat.sub('', s_auth, count=1))

            # Consume content and release the original connection
            # to allow our new request to reuse the same one.
            yield From(r.content)
            r.raw.release_conn() # redundant
            prep = r.request.copy()
            extract_cookies_to_jar(prep._cookies, r.request, r.raw)
            prep.prepare_cookies(prep._cookies)

            prep.headers['Authorization'] = self.build_digest_header(
                prep.method, prep.url)
            _r = yield From(r.connection.send(prep, **kwargs))
            _r.history.append(r)
            _r.request = prep

            raise Return(_r)

        setattr(self, 'num_401_calls', 1)
        raise Return(r)
Esempio n. 10
0
    def _cancel_callback(self, key_change):
        if key_change.event not in (KeyEvent.CREATE, KeyEvent.SET):
            raise Return()

        build_uuid = key_change.value
        build_info = self._build_uuid_to_info.get(build_uuid, None)
        if build_info is None:
            logger.debug('No build info for "%s" job %s', key_change.event,
                         build_uuid)
            raise Return(False)

        lock_key = slash_join(self._canceled_lock_prefix, build_uuid,
                              build_info.execution_id)
        lock_acquired = yield From(self._orchestrator.lock(lock_key))
        if lock_acquired:
            builder_realm = build_info.component.builder_realm
            yield From(self.kill_builder_executor(build_uuid))
            yield From(
                self._orchestrator.delete_key(self._realm_key(builder_realm)))
            yield From(
                self._orchestrator.delete_key(self._metric_key(builder_realm)))
            yield From(
                self._orchestrator.delete_key(
                    slash_join(self._job_prefix, build_uuid)))

        # This is outside the lock so we can un-register the component wherever it is registered to.
        yield From(build_info.component.cancel_build())
Esempio n. 11
0
    def analyze_sdf(self, sdf, max_attempts=5):
        """
        Coroutine that returns with a (collisions, bounding box) tuple,
        assuming analysis succeeds.

        :param max_attempts:
        :param sdf:
        :type sdf: SDF
        :return:
        """
        msg = None
        rh = self.request_handler
        for _ in range(max_attempts):
            future = yield From(rh.do_gazebo_request("analyze_body", str(sdf)))
            yield From(future)

            response = future.result()
            if response.response == "success":
                msg = BodyAnalysisResponse()
                msg.ParseFromString(response.serialized_data)
                break

        if not msg:
            # Error return
            raise Return(None)

        if msg.HasField("boundingBox"):
            bbox = msg.boundingBox
        else:
            bbox = None

        internal_collisions = len(msg.contact)
        raise Return(internal_collisions, bbox)
Esempio n. 12
0
    def attempt_mate(self, ra, rb):
        """
        Attempts mating between two robots.
        :param ra:
        :param rb:
        :return:
        """
        logger.debug("Attempting mating between `%s` and `%s`..." % (ra.name, rb.name))

        # Attempt to create a child through crossover
        success, child = self.crossover.crossover(ra.tree, rb.tree)
        if not success:
            logger.debug("Crossover failed.")
            raise Return(False)

        # Apply mutation
        logger.debug("Crossover succeeded, applying mutation...")
        self.mutator.mutate(child, in_place=True)

        if self.conf.enforce_planarity:
            make_planar(child.root)

        _, outputs, _ = child.root.io_count(recursive=True)
        if not outputs:
            logger.debug("Evolution resulted in child without motors.")
            raise Return(False)

        # Check if the robot body is valid
        ret = yield From(self.analyze_tree(child))
        if ret is None or ret[0]:
            logger.debug("Intersecting body parts: Miscarriage.")
            raise Return(False)

        logger.debug("Viable child created.")
        raise Return(child, ret[1])
    def data(self):
        # For backwords-compat with earlier urllib3 0.4 and earlier.
        if self._body:
            raise Return(self._body)

        if self._fp:
            _d = yield From(self.read(cache_content=True))
            raise Return(_d)
Esempio n. 14
0
    def voltage(self, servo):
        try:
            data = yield From(self.ram_read(servo, 54, 1))
            value = data.data[0]

            raise Return(0.074 * value)
        except asyncio.TimeoutError:
            raise Return(None)
Esempio n. 15
0
    def perform_request(self, dua, request):
        message_id = int(request.getComponentByName('messageID'))
        dict_req = BaseStrategy.decode_request(request)
        if message_id not in dua.pending:
            dua.pending[message_id] = dict_req
            if dict_req['type'] == 'bindRequest':
                if NATIVE_ASYNCIO:
                    response, response_type = yield from do_bind_operation(
                        dua, message_id, dict_req)
                else:
                    response, response_type = yield From(
                        do_bind_operation(dua, message_id, dict_req))
            elif dict_req['type'] == 'unbindRequest':
                if NATIVE_ASYNCIO:
                    yield from do_unbind_operation(dua, message_id)
                else:
                    yield From(do_unbind_operation(dua, message_id))
                dua.writer.close()
                if NATIVE_ASYNCIO:
                    return
                else:
                    raise Return()
            elif dict_req['type'] == 'extendedReq':
                response, response_type = do_extended_operation(
                    dua, message_id, dict_req)
                if response[
                        'responseName'] == '1.3.6.1.4.1.1466.20037' and response[
                            'result'] == RESULT_SUCCESS:  # issue start_tls
                    ldap_message = build_ldap_message(message_id,
                                                      response_type, response,
                                                      None)
                    dua.send(ldap_message)
                    dua.start_tls()
                response = None
            else:
                dua.abort(diagnostic_message='unknown operation')
                if NATIVE_ASYNCIO:
                    return
                else:
                    raise Return()

            del dua.pending[message_id]
            if not response:  # notice of disconnection sent while doing operation
                if NATIVE_ASYNCIO:
                    return
                else:
                    raise Return()
            controls = None  # TODO
            ldap_message = build_ldap_message(message_id, response_type,
                                              response, controls)
        else:  # pending message with same id of previous message
            dua.abort(diagnostic_message='duplicate message ID')
            if NATIVE_ASYNCIO:
                return
            else:
                raise Return()
        dua.send(ldap_message)
Esempio n. 16
0
    def _job_callback(self, key_change):
        """
    This is the callback invoked when keys related to jobs are changed.
    It ignores all events related to the creation of new jobs.
    Deletes or expirations cause checks to ensure they've been properly marked as completed.

    :param key_change: the event and value produced by a key changing in the orchestrator
    :type key_change: :class:`KeyChange`
    """
        if key_change.event in (KeyEvent.CREATE, KeyEvent.SET):
            raise Return()

        elif key_change.event in (KeyEvent.DELETE, KeyEvent.EXPIRE):
            # Handle the expiration/deletion.
            job_metadata = json.loads(key_change.value)
            build_job = BuildJob(AttrDict(job_metadata["job_queue_item"]))
            logger.debug('Got "%s" of job %s', key_change.event, build_job.build_uuid)

            # Get the build info.
            build_info = self._build_uuid_to_info.get(build_job.build_uuid, None)
            if build_info is None:
                logger.debug(
                    'No build info for "%s" job %s (%s); probably already deleted by this manager',
                    key_change.event,
                    build_job.build_uuid,
                    job_metadata,
                )
                raise Return()

            if key_change.event != KeyEvent.EXPIRE:
                # If the etcd action was not an expiration, then it was already deleted by some manager and
                # the execution was therefore already shutdown. All that's left is to remove the build info.
                self._build_uuid_to_info.pop(build_job.build_uuid, None)
                raise Return()

            logger.debug(
                "got expiration for job %s with metadata: %s", build_job.build_uuid, job_metadata
            )

            if not job_metadata.get("had_heartbeat", False):
                # If we have not yet received a heartbeat, then the node failed to boot in some way.
                # We mark the job as incomplete here.
                yield From(self._mark_job_incomplete(build_job, build_info))

            # Finally, we terminate the build execution for the job. We don't do this under a lock as
            # terminating a node is an atomic operation; better to make sure it is terminated than not.
            logger.info(
                "Terminating expired build executor for job %s with execution id %s",
                build_job.build_uuid,
                build_info.execution_id,
            )
            yield From(self.kill_builder_executor(build_job.build_uuid))
        else:
            logger.warning(
                "Unexpected KeyEvent (%s) on job key: %s", key_change.event, key_change.key
            )
Esempio n. 17
0
    def schedule(self, build_job):
        """ Schedules a build for an Enterprise Registry. """
        if self.shutting_down or not self.ready_components:
            raise Return(False, RETRY_TIMEOUT)

        component = self.ready_components.pop()

        yield From(component.start_build(build_job))

        raise Return(True, None)
Esempio n. 18
0
    def _get_tshark_process(self, packet_count=None):
        if self._current_tshark:
            raise Return(self._current_tshark)
        proc = yield From(super(InMemCapture, self)._get_tshark_process(packet_count=packet_count, stdin=subprocess.PIPE))
        self._current_tshark = proc

        # Create PCAP header
        header = struct.pack("IHHIIII", 0xa1b2c3d4, 2, 4, 0, 0, 0x7fff, self._current_linktype)
        proc.stdin.write(header)
        raise Return(proc)
Esempio n. 19
0
 def position(self, servo):
     try:
         # NOTE: The datasheet appears to be off here.
         data = yield From(self.ram_read(servo, 60, 2))
         value = data.data
         if value is None:
             raise Return(None)
         raise Return(value[1] << 8 | value[0])
     except asyncio.TimeoutError:
         raise Return(None)
Esempio n. 20
0
    def temperature_C(self, servo):
        try:
            data = yield From(self.ram_read(servo, 55, 1))
            value = data.data[0]

            # Note, this formula was derived from the Dongbu lookup table,
            # and becomes terribly inaccurate below -20C.
            raise Return((value - 40) * 0.5125 - 19.38)
        except asyncio.TimeoutError:
            raise Return(None)
Esempio n. 21
0
 def config(self, key=None, **kw):
     contents = yield From(self.list_backend(prefix=self.prefixify(key)))
     contents = contents.get('Contents', [])
     kw.setdefault('in_group_check', True)
     tasks = [self.get(key=obj['Key'], **kw) for obj in contents]
     try:
         results = yield From(asyncio.gather(*tasks))
     except ValueError as e:
         raise Return({})
     keys = [k['Key'].split('/')[-1] for k in contents]
     raise Return(OrderedDict(zip(keys, results)))
Esempio n. 22
0
    def birth(self, tree, bbox, parents):
        """
        Birth process, picks a robot position and inserts
        the robot into the world.

        Robots are currently placed at a random position within the circular
        birth clinic. In this process, 5 attempts are made to place the robot
        at the minimum drop distance from other robots. If this fails however
        the last generated position is used anyway.
        :param tree:
        :param bbox:
        :param parents:
        :return:
        """
        s = len(tree)
        if (s + self.total_size()) > self.conf.part_limit:
            print("Not enough parts in pool to create robot of size %d." % s)
            raise Return(False)

        # Pick a random radius and angle within the birth clinic
        pos = Vector3()
        pos.z = -bbox.min.z + self.conf.drop_height
        done = False
        min_drop = self.conf.min_drop_distance

        for i in range(5):
            angle = random.random() * 2 * math.pi
            radius = self.conf.birth_clinic_diameter * 0.5 * random.random()
            pos.x = radius * math.cos(angle)
            pos.y = radius * math.sin(angle)

            done = True
            for robot in self.robots.values():
                if robot.distance_to(pos, planar=True) < min_drop:
                    done = False
                    break

            if done:
                break

        if not done:
            logger.warning("Warning: could not satisfy minimum drop distance.")

        # Note that we register the reproduction only if
        # the child is actually born, i.e. there were enough parts
        # left in the world to satisfy the request.
        if parents:
            ra, rb = parents
            ra.did_mate_with(rb)
            rb.did_mate_with(ra)

        self.births += 1
        fut = yield From(self.insert_robot(tree, Pose(position=pos), parents=parents))
        raise Return(fut)
Esempio n. 23
0
    def process(self, url_item):
        scheme = url_item.url_info.scheme

        if scheme in ('http', 'https'):
            raise Return((yield From(self.web_processor.process(url_item))))
        elif scheme == 'ftp':
            raise Return((yield From(self.ftp_processor.process(url_item))))
        else:
            _logger.warning(__(
                _('No processor available to handle {scheme} scheme.'),
                scheme=repr(scheme)
            ))
Esempio n. 24
0
    def set_phase(self, phase, extra_data=None):
        if phase == self._current_phase:
            raise Return(False)

        self._current_phase = phase
        yield From(
            self._append_log_message(phase, self._build_logs.PHASE,
                                     extra_data))

        # Update the repository build with the new phase
        raise Return(
            self._build_model.update_phase_then_close(self._uuid, phase))
Esempio n. 25
0
 def getData(self):
     if self.first:
         self.first = False
         dat = yield From(self.fetch())
         raise Return(dat)
     else:
         if self.repeat > 0:
             yield From(asyncio.sleep(self.repeat))
             dat = yield From(self.fetch())
             raise Return(dat)
         else:
             raise Return(None)
Esempio n. 26
0
    def job_completed(self, build_job, job_status, build_component):
        logger.debug("Calling job_completed for job %s with status: %s",
                     build_job.build_uuid, job_status)

        yield From(
            self._write_duration_metric(build_duration,
                                        build_component.builder_realm,
                                        job_status=job_status))

        # Mark the job as completed. Since this is being invoked from the component, we don't need
        # to ask for the phase to be updated as well.
        build_info = self._build_uuid_to_info.get(build_job.build_uuid, None)
        executor_name = build_info.executor_name if build_info else None
        yield From(
            self.job_complete_callback(build_job,
                                       job_status,
                                       executor_name,
                                       update_phase=False))

        # Kill the ephemeral builder.
        yield From(self.kill_builder_executor(build_job.build_uuid))

        # Delete the build job from the orchestrator.
        try:
            job_key = self._job_key(build_job)
            yield From(self._orchestrator.delete_key(job_key))
        except KeyError:
            logger.debug(
                "Builder is asking for job to be removed, but work already completed"
            )
        except OrchestratorConnectionError:
            logger.exception(
                "Could not remove job key as orchestrator is not available")
            yield From(sleep(ORCHESTRATOR_UNAVAILABLE_SLEEP_DURATION))
            raise Return()

        # Delete the metric from the orchestrator.
        try:
            metric_key = self._metric_key(build_component.builder_realm)
            yield From(self._orchestrator.delete_key(metric_key))
        except KeyError:
            logger.debug(
                "Builder is asking for metric to be removed, but key not found"
            )
        except OrchestratorConnectionError:
            logger.exception(
                "Could not remove metric key as orchestrator is not available")
            yield From(sleep(ORCHESTRATOR_UNAVAILABLE_SLEEP_DURATION))
            raise Return()

        logger.debug("job_completed for job %s with status: %s",
                     build_job.build_uuid, job_status)
Esempio n. 27
0
    def produce_individual(self):
        """

        :return:
        """
        p1, p2 = self.select_parents()

        for j in xrange(self.conf.max_mating_attempts):
            pair = yield From(self.attempt_mate(p1, p2))
            if pair:
                raise Return((pair[0], pair[1], (p1, p2)))

        raise Return(False)
Esempio n. 28
0
    def _heartbeat(self):
        """
        Coroutine that runs every HEARTBEAT_TIMEOUT seconds, both checking the worker's heartbeat
        and updating the heartbeat in the build status dictionary (if applicable).

        This allows the build system to catch crashes from either end.
        """
        yield From(trollius.sleep(INITIAL_TIMEOUT))

        while True:
            # If the component is no longer running or actively building, nothing more to do.
            if (
                self._component_status != ComponentStatus.RUNNING
                and self._component_status != ComponentStatus.BUILDING
            ):
                raise Return()

            # If there is an active build, write the heartbeat to its status.
            if self._build_status is not None:
                with self._build_status as status_dict:
                    status_dict["heartbeat"] = int(time.time())

            # Mark the build item.
            current_job = self._current_job
            if current_job is not None:
                yield From(self.parent_manager.job_heartbeat(current_job))

            # Check the heartbeat from the worker.
            logger.debug("Checking heartbeat on realm %s", self.builder_realm)
            if (
                self._last_heartbeat
                and self._last_heartbeat < datetime.datetime.utcnow() - HEARTBEAT_DELTA
            ):
                logger.debug(
                    "Heartbeat on realm %s has expired: %s",
                    self.builder_realm,
                    self._last_heartbeat,
                )

                yield From(self._timeout())
                raise Return()

            logger.debug(
                "Heartbeat on realm %s is valid: %s (%s).",
                self.builder_realm,
                self._last_heartbeat,
                self._component_status,
            )

            yield From(trollius.sleep(HEARTBEAT_TIMEOUT))
Esempio n. 29
0
 def get(self, key, **kw):
     try:
         data = yield From(self._get(key, **kw))
     except Exception as e:
         if kw.get('in_group_check', True):
             # check for grouped keys
             key = key.rstrip('/') + '/'
             kw['in_group_check'] = True
             result = yield From(self.config(key=key, **kw))
             if not result:
                 result = "Error! The specified key does not exist."
             raise Return(result)
         raise
     raise Return(data["data"])
Esempio n. 30
0
    def _transition_to_idle(self):
        # Command 0 speed, wait for the command to be active, then
        # wait for at least 1 phase, then wait for all legs to be in
        # stance.
        self.gait.set_command(ripple.Command())

        yield From(self._run_while(
                lambda: (self.gait.is_command_pending() and
                         not self.next_command),
                allow_new=False))

        if self.next_command:
            raise Return(False)

        class PhaseChecker(object):
            def __init__(self, parent):
                self.phase_delta = 0.0
                self.parent = parent
                self.old_phase = self.parent.state.phase

            def check(self):
                self.phase_delta += _wrap_phase(
                    self.parent.state.phase - self.old_phase)
                self.old_phase = self.parent.state.phase
                return (self.phase_delta < 0.95 and
                        not self.parent.next_command)

        checker = PhaseChecker(self)

        yield From(self._run_while(checker.check, allow_new=False))

        if self.next_command:
            raise Return(False)

        def any_non_stance():
            if self.next_command:
                return False

            for leg in self.state.legs.values():
                if leg.mode != ripple.STANCE:
                    return True
            return False

        yield From(self._run_while(any_non_stance, allow_new=False))

        if self.next_command:
            raise Return(False)

        raise Return(True)