def HandleTestResult(status, info, result, rebase=False): try: if isinstance(result, Exception): raise result stdout, stderr, returncode, duration = result if info.is_roundtrip: if returncode == 0: status.Passed(info, duration) elif returncode == 2: # run-roundtrip.py returns 2 if the file couldn't be parsed. # it's likely a "bad-*" file. status.Skipped(info) else: raise Error(stderr) else: if returncode != info.expected_error: # This test has already failed, but diff it anyway. msg = 'expected error code %d, got %d.' % (info.expected_error, returncode) try: info.Diff(stdout, stderr) except Error as e: msg += '\n' + str(e) raise Error(msg) else: if rebase: info.Rebase(stdout, stderr) else: info.Diff(stdout, stderr) status.Passed(info, duration) except Exception as e: status.Failed(info, str(e))
def _schedule_pass(self, i): """ Function to add a timer and schedule a specific pass """ if self.timer is not None: raise Error("Pass %d has already been scheduled" % (i)) if not self._stop: tstart = self.tstamps[i] - pd.Timedelta( seconds=self.schedule.buffertime) now = pd.Timestamp(pd.datetime.utcnow()) if tstart <= now: self.stop( ) # <-- Added 2018-06-14. Seems like a sensible thing to sensure timers etc are reset. To be checked raise Error( "Can't schedule a pass that is less than %d sconds in the future. Scheduler stopped." % (self.schedule.buffertime)) sleeptime = (tstart - now) / np.timedelta64(1, 's') self.timer = (i, 'S', tstart, Timer(sleeptime, self._callback, args=(i, ))) self.timer[-1].daemon = True self.timer[-1].start()
def receive_forever(self): """ Start an infinite loop that will indefinitely block on a receive until a new message comes in. If the message is JSON-formatted and has a "type" key, then the corresponding handler will be run. """ while True: body, addr = self.receive() if body == None and addr == None: # There was an error with the received message continue if body['type'] not in self.handlers: # Provided type is not a registered handler logging.debug('Invalid message type', body) self.send(Error.json(Error.BAD_REQ, 'invalid message type'), addr) continue try: self.handlers[body['type']](self, body, addr) except CloseServer: self.close() return except Exception as e: self.send(Error.json(Error.SERVER_ERR, str(e)), addr)
def get_video_link(api, status_json): try: media_variants = status_json["extended_entities"]["media"][0][ "video_info"]["variants"] return filter_video(media_variants)['url'] except: try: additional_media_info = status_json["extended_entities"]["media"][ 0]["additional_media_info"] additional_media_info['embeddable'] if additional_media_info and additional_media_info[ 'embeddable'] is None: # look for link link = look_for_link(additional_media_info) if not link: return Error(1, 'Error') return Error(2, link) elif status_json['entities']['media']: # tweet is share of another tweet containing media expanded_url = status_json['entities']['media'][0][ 'expanded_url'].split('/') tweetId = expanded_url[len(expanded_url) - 3] if tweetId != status_json['id']: ogTweet = api.get_status(tweetId) ogTweet_json = get_json_dict(ogTweet) return get_video_link(api, ogTweet_json) except: return Error(3, "##")
def HandleTestResult(status, info, result, rebase=False): try: if isinstance(result, (Error, KeyboardInterrupt)): raise result if info.is_roundtrip: if result.Failed(): if result.GetLastFailure().returncode == 2: # run-roundtrip.py returns 2 if the file couldn't be parsed. # it's likely a "bad-*" file. status.Skipped(info) else: raise Error(result.stderr) else: status.Passed(info, result.duration) else: if result.Failed(): # This test has already failed, but diff it anyway. last_failure = result.GetLastFailure() msg = 'expected error code %d, got %d.' % ( last_failure.GetExpectedReturncode(), last_failure.returncode) try: info.Diff(result.stdout, result.stderr) except Error as e: msg += '\n' + str(e) raise Error(msg) else: if rebase: info.Rebase(result.stdout, result.stderr) else: info.Diff(result.stdout, result.stderr) status.Passed(info, result.duration) except Error as e: status.Failed(info, str(e), result)
def ParseDirective(self, key, value): if key == 'EXE': self.exe = value elif key == 'STDIN_FILE': self.input_filename = value self.generated_input_filename = value elif key == 'FLAGS': self.flags += shlex.split(value) elif key == 'ERROR': self.expected_error = int(value) elif key == 'SLOW': self.slow = True elif key == 'SKIP': self.skip = True elif key == 'VERBOSE-FLAGS': self.verbose_flags = [shlex.split(level) for level in value] elif key in ['TODO', 'NOTE']: pass elif key == 'TOOL': if not value in TOOLS: raise Error('Unknown tool: %s' % value) self.tool = value for tool_key, tool_value in TOOLS[value].items(): self.ParseDirective(tool_key, tool_value) else: raise Error('Unknown directive: %s' % key)
def put(self, nid, pass_id, orig, dest, msg): """ Add new item to the log Args: nid(int): Norad ID of the satellite commuicated with pass_id (str): ID of the pass the communication belongs to orig (str): Originator of the message dest (str): Destination of the message msg (str): The message itself """ if orig not in self.ACTORS: raise Error('Invalid origin, must be one of %s' % (self.ACTORS)) if orig not in self.ACTORS: raise Error('Invalid destination, must be one of %s' % (self.ACTORS)) df = DataFrame({ 'nid': [nid], 'pass_id': [pass_id], 'orig': [orig], 'dest': [dest], 'msg': [msg] }) self.put_df(self._TABLE, df, index=False)
def handle_link(server, body, source): logging.debug('LINK') if 'car_id' not in body or 'user_id' not in body: msg = 'missing field: "car_id", "user_id" required' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), source) return car_id = body['car_id'] user_id = body['user_id'] if not isinstance(car_id, int) or not isinstance(user_id, int): msg = '"car_id" and "user_id" must be integers' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), source) return with server.get_db() as (_, cursor): cursor.execute('select * from cars where id=? and userID=?', (car_id, user_id)) entry = cursor.fetchone() if entry == None: msg = 'car does not exist' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), source) elif not entry[3]: # Check the isOn column msg = 'car is not available' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), source) else: server.add_route(source, (entry[2], CAR_PORT)) data = '{"type": %d}' % MsgType.ACK server.send(data.encode('utf-8'), source)
def handle_set_led(server, body, source): """ Sends SET_LED message to the destination that corresponds with the source address in the cache. """ logging.debug('SET_LED') if 'state' not in body: msg = 'missing field: "state" required' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), source) return state = body['state'] if not isinstance(state, int) or state < 0 or state > 2: msg = 'state must be an int in range [0,2]' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), source) return car_addr = server.get_destination(source) if car_addr == None: msg = 'invalid destination' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), source) return server.send(json.dumps(body).encode('utf-8'), car_addr)
def handle_connect_car(server, body, source): logging.debug('CONNECT CAR') if 'car_id' not in body: logging.debug('missing field: car_id') server.send(Error.json(Error.BAD_REQ, 'missing field: car_id'), source) return car_id = body['car_id'] if not isinstance(car_id, int): msg = '"car_id" must be an integer' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), source) return with server.get_db() as (dbconnect, cursor): cursor.execute('select * from cars where (id=?)', (car_id, )) entry = cursor.fetchone() request_ip = source[0] if entry is None: msg = 'car does not exist' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), source) elif entry[2] != request_ip: msg = 'IP address does not match car ID' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), source) else: cursor.execute('update cars set isOn=1 where (id=?)', (car_id, )) dbconnect.commit() data = '{"type": %d}' % MsgType.ACK server.send(data.encode('utf-8'), source)
def add_freq_plot(self, freq): """ Method to append a spectrum to the waterfall. It does this by appending a row to the live_data image property. Args: freq: (list[float]). Spectrum to append. """ if self._width < len(freq): if len(freq) % self._width != 0: raise Error('frequency vector must be a multiple of waterfall width') else: freq = np.array(freq).reshape((-1, len(freq)/self._width)) freq = freq.max(1) if self._width != len(freq): raise Error('Dimension mismatch') data = self.live_data['wfall']['image'][0] if data.shape[0] < self._height: self.live_data['wfall']['image'] = [np.concatenate((data, [freq]))] else: self.live_data['wfall']['image'] = [np.concatenate((data[1:,:], [freq]))]
def handle_set_led(server, body, addr): """ Passes the new LED state to the Arduino via the serial port. Arguments: server -- instance of Server class body -- JSON body of UDP packet received addr -- source destination of received UDP packet """ logging.debug('SET LED') if 'state' not in body: msg = 'missing "state" field' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), addr) return state = body['state'] if not isinstance(state, int) or state < 0 or state > 2: msg = '"state" must integer in range [0, 2]' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), addr) return SerialMsg.write_8_bit(server.serial, SerialMsg.LED, state) server.send(json.dumps({'type': MsgType.ACK}).encode('utf-8'), addr)
def handle_move(server, body, addr): """ Passes the joystick coordinates to the Arduino via the serial port. Arguments: server -- instance of Server class body -- JSON body of UDP packet received addr -- source destination of received UDP packet """ logging.debug('MOVE') if 'x' not in body or 'y' not in body: msg = 'body must include "x" and "y" fields' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), addr) return x = body['x'] y = body['y'] if not isinstance(x, int) or not isinstance(y, int) or \ x < 0 or x > 1023 or y < 0 or y > 1023: msg = '"x" and "y" values must be within the range [0, 1023]' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), addr) return SerialMsg.write_16_bit(server.serial, SerialMsg.MOVE, body['x'], body['y'])
def ParseDirective(self, key, value): if key == 'EXE': self.exe = value elif key == 'STDIN_FILE': self.input_filename = value elif key == 'FLAGS': if not isinstance(value, list): value = shlex.split(value) self.flags += value elif key == 'ERROR': self.expected_error = int(value) elif key == 'SLOW': self.slow = True elif key == 'SKIP': self.skip = True elif key == 'VERBOSE-FLAGS': self.verbose_flags = [shlex.split(level) for level in value] elif key in ['TODO', 'NOTE']: pass elif key == 'TOOL': if not value in TOOLS: raise Error('Unknown tool: %s' % value) self.tool = value for tool_key, tool_value in TOOLS[value].items(): self.ParseDirective(tool_key, tool_value) elif key == 'ENV': # Pattern: FOO=1 BAR=stuff self.env = dict(x.split('=') for x in value.split()) else: raise Error('Unknown directive: %s' % key)
def create_update_leaf(self, index, obj): # create the mappings in elasticsearch Content.init(index=index, using=self.es_client) # create and save and article content1 = Content( meta={'id': obj.leaf_id}, leaf_id=obj.leaf_id, branch_id=obj.branch_id, branch_name=obj.branch_name, content_eng=obj.content_eng, content_san=obj.content_san, content_kan=obj.content_kan, master_branch_name=obj.master_branch_name, content_type_name=obj.content_type_name, source_doc_name=obj.source_doc_name, tags=['test1', 'test2']) content1.published_date = datetime.now() try: content1.save() if content1.is_published(): return True raise Error("*** search.objects.create_update_leaf: Error! Unable to write leaf/content to Elastic.") return False except Exception as Ex: raise Error("*** search.objects.create_update_leaf: Exception occured! Unable to write leaf/content to Elastic.") print(Ex) return False
def GetDataFromKey(self, section, ini_key): if section not in self._config: Error("Section {} does not exist.".format(section)) if ini_key not in self._config[section]: Error("Key {} does not exist in section {}.".format(ini_key, section)) value = self._config[section].get(ini_key) Info("Reading {}/{} from .ini file: {}".format(section, ini_key, value)) return value
def handle_login(server, body, source): logging.debug('LOGIN') ''' 1. Compare salted-and-hashed passwords 2. If success: get car list from database and send to the app If failure: send failed login message ''' # Check data is valid. if not, send an error packet if 'name' not in body or 'password' not in body: message = 'missing field: "name", "password" required' logging.debug(message) server.send(Error.json(Error.BAD_REQ, message), source) return # Get JSON data name = body['name'] password = body['password'] if not isinstance(name, str) or not isinstance(password, str): msg = '"name" and "password" must be strings' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), source) return # Get user from db. Send an error if user doesn't exist. with server.get_db() as (dbconnect, cursor): cursor.execute("select * from users where name=(?)", [name]) entry = cursor.fetchone() if entry is None: message = "User does not exist" logging.debug(message) server.send(Error.json(Error.BAD_REQ, message), source) return # Get salt as bytes salt = entry[2] b_salt = b64decode(salt.encode('utf-8')) # Get salted password string from database salted_password = entry[3] # Salt the login password new_password = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), b_salt, 100000) str_new_password = b64encode(new_password).decode('utf-8') # Compare two passwords as strings if str_new_password == salted_password: # Send Confirmation to App logging.debug("User login successful") user_id = entry[0] ackJSON = {"type": MsgType.ACK, "user_id": user_id} _send_JSON(server, source, ackJSON) else: message = "Password is incorrect" logging.debug(message) server.send(Error.json(Error.BAD_REQ, message), source)
def Run(self, cwd, timeout, console_out=False, env=None): process = None is_timeout = Cell(False) def KillProcess(timeout=True): if process: try: if IS_WINDOWS: # http://stackoverflow.com/a/10830753: deleting child processes in # Windows subprocess.call( ['taskkill', '/F', '/T', '/PID', str(process.pid)]) else: os.killpg(os.getpgid(process.pid), 15) except OSError: pass is_timeout.Set(timeout) try: start_time = time.time() kwargs = {} if not IS_WINDOWS: kwargs['preexec_fn'] = os.setsid stdin_data = None if self.stdin: stdin_data = open(self.stdin, 'rb').read() # http://stackoverflow.com/a/10012262: subprocess with a timeout # http://stackoverflow.com/a/22582602: kill subprocess and children process = subprocess.Popen( self.args, cwd=cwd, env=env, stdout=None if console_out else subprocess.PIPE, stderr=None if console_out else subprocess.PIPE, stdin=None if not self.stdin else subprocess.PIPE, **kwargs) timer = threading.Timer(timeout, KillProcess) try: timer.start() stdout, stderr = process.communicate(input=stdin_data) finally: returncode = process.returncode process = None timer.cancel() if is_timeout.Get(): raise Error('TIMEOUT') duration = time.time() - start_time except OSError as e: raise Error(str(e)) finally: KillProcess(False) return RunResult(self, stdout, stderr, returncode, duration)
def _read(self): if not os.path.isfile(CONFIG_PATH): Error("{} file not present.".format(CONFIG_PATH)) return Info("Reading {} data.".format(CONFIG_PATH)) self._config.read(CONFIG_PATH) for section in ConfigModule.SECTIONS: if section not in self._config: Error("Section {} does not exist.".format(section))
def RunCommandWithTimeout(command, cwd, timeout, console_out=False, env=None): process = None is_timeout = Cell(False) def KillProcess(timeout=True): if process: try: if IS_WINDOWS: # http://stackoverflow.com/a/10830753: deleting child processes in # Windows subprocess.call( ['taskkill', '/F', '/T', '/PID', str(process.pid)]) else: os.killpg(os.getpgid(process.pid), 15) except OSError: pass is_timeout.Set(timeout) try: start_time = time.time() kwargs = {} if not IS_WINDOWS: kwargs['preexec_fn'] = os.setsid # http://stackoverflow.com/a/10012262: subprocess with a timeout # http://stackoverflow.com/a/22582602: kill subprocess and children process = subprocess.Popen( command, cwd=cwd, env=env, stdout=None if console_out else subprocess.PIPE, stderr=None if console_out else subprocess.PIPE, universal_newlines=True, **kwargs) timer = threading.Timer(timeout, KillProcess) try: timer.start() stdout, stderr = process.communicate() finally: returncode = process.returncode process = None timer.cancel() if is_timeout.Get(): raise Error('TIMEOUT\nSTDOUT:\n%s\nSTDERR:\n%s\n' % (stdout, stderr)) duration = time.time() - start_time except OSError as e: raise Error(str(e)) finally: KillProcess(False) return stdout, stderr, returncode, duration
def handle_register_user(server, body, source): logging.debug('REGISTER USER') ''' 1. Store username, salt, and salted-and-hashed-password in 'users' table 2. Send a confirmation (ACK) back to the app ''' # Check data is valid. if not, send an error packet if 'name' not in body or 'password' not in body: message = 'missing field: "name", "password" required' logging.debug(message) server.send(Error.json(Error.BAD_REQ, message), source) return # Get JSON data name = body['name'] password = body['password'] if not isinstance(name, str) or not isinstance(password, str): msg = '"name" and "password" must be strings' logging.debug(msg) server.send(Error.json(Error.BAD_REQ, msg), source) return # Salt password salt = os.urandom(32) password = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000) # Create user in db. Send an error if user already exists. with server.get_db() as (dbconnect, cursor): cursor.execute("select * from users where name=(?)", [name]) entry = cursor.fetchone() if entry is None: cursor.execute("insert into users (name,salt,password) values (?,?,?)",\ (name,b64encode(salt).decode('utf-8'), b64encode(password).decode('utf-8'))) dbconnect.commit() else: message = "User already exists" logging.debug(message) server.send(Error.json(Error.BAD_REQ, message), source) return user_id = cursor.lastrowid # Send Confirmation to App logging.debug("User registration successful") ackJSON = { "type": MsgType.ACK, "user_id": user_id, } _send_JSON(server, source, ackJSON)
def FindExeWithFallback(name, default_exe_list, override_exe=None): result = override_exe if result is not None: if os.path.exists(result): return os.path.abspath(result) raise Error('%s executable not found.\nsearch path: %s\n' % (name, result)) for result in default_exe_list: if os.path.exists(result): return os.path.abspath(result) raise Error('%s executable not found.\n%s\n' % (name, '\n'.join('search path: %s' % path for path in default_exe_list)))
def __error(self, expected): self._raise_error(Error ( line = self.__lexic.line(), column = self.__lexic.column(), _type = "Syntactic", desc = "< {} > expected, {} < {} > received".format(expected, self.__lexic.token(), self.__lexeme) ))
def __init__(self, db, table, ncols=[], ntypes=sa_t.Text(), **kwargs): """ Args: ncols: Normal columns; a list of column names ntypes: SQLAlchemy types associated with the columns (defaults to Text) See :class:`.Database` for the definition of the additional arguments. """ # The table to use self._TABLE = table # Normal (non-kv) columns if 'key' in ncols or 'value' in ncols: raise Error("'key' or 'value' cant be used as a normal field name") self._NCOLS = ncols super(KVDb, self).__init__(db, **kwargs) if not isinstance(ntypes, list): ntypes = [ntypes for n in ncols] columns = [Column(n, t) for n, t in zip(ncols, ntypes)] self.create_table(self._TABLE, Column('key', sa_t.String(_MAX_STRING_LEN)), Column('value', sa_t.Text()), *columns)
def ParseDirective(self, key, value): if key == 'RUN': self.cmds.append(CommandTemplate(value)) elif key == 'STDIN_FILE': self.input_filename = value elif key.startswith('ARGS'): suffix = key[len('ARGS'):] self.ApplyToCommandBySuffix(suffix, lambda cmd: cmd.AppendArgs(value)) elif key.startswith('ERROR'): suffix = key[len('ERROR'):] self.ApplyToCommandBySuffix( suffix, lambda cmd: cmd.SetExpectedReturncode(int(value))) elif key == 'SLOW': self.slow = True elif key == 'SKIP': self.skip = True elif key == 'VERBOSE-ARGS': self.GetLastCommand().AppendVerboseArgs(value) elif key in ['TODO', 'NOTE']: pass elif key == 'TOOL': self.SetTool(value) elif key == 'STDIN': self.GetLastCommand().SetStdin(value) elif key == 'ENV': # Pattern: FOO=1 BAR=stuff self.env = dict(x.split('=') for x in value.split()) else: raise Error('Unknown directive: %s' % key)
def _Command(self, index, command): command_type = command['type'] new_field = 'assert_%d' % index if command_type == 'assert_return': self.lines.append('(func (export "%s")' % new_field) self.lines.append('block') self._Action(command['action']) for expected in command['expected']: self._Reinterpret(expected['type']) if expected['value'] in ('nan:canonical', 'nan:arithmetic'): self._NanBitmask(expected['value'] == 'nan:canonical', expected['type']) self._And(expected['type']) self._QuietNan(expected['type']) else: self._Constant(expected) self._Reinterpret(expected['type']) self._Eq(expected['type']) self.lines.extend(['i32.eqz', 'br_if 0']) self.lines.extend(['return', 'end', 'unreachable', ')']) elif command_type in ('assert_trap', 'assert_exhaustion'): self.lines.append('(func (export "%s")' % new_field) self._Action(command['action']) self.lines.extend(['br 0', ')']) else: raise Error('Unexpected command: %s' % command_type) # Update command to point to the new exported function. command['action']['field'] = new_field command['action']['args'] = [] command['expected'] = []
def apns_register(client): '''This is called in iPushjet's application:didRegisterForRemoteNotificationsWithDeviceToken: override. We send the UUID along, which PushJet uses for everything internally. However, the (variable length) token that Apple sends us is what we must send notifs to later on. In other words, the handshake looks like this: 1. The iDevice registers with Apple 2. When (1) is successful, the device hits this endpoint and sends its UUID along with the token it received from Apple in (1). 3. Both are stored in the `apns` table. When subscribing to something, we look attach the subscription to the UUID. When sending, we grab all UUIDs subscribed, then key on them to map them to device_tokens which get sent to. This is basically the same way the GCM one works, except that it's documented. ''' token = request.form.get('device_token', False) if not token: return Error.ARGUMENT_MISSING('device_token') regs = Apns.query.filter_by(uuid=client).all() for u in regs: db.session.delete(u) reg = Apns(client, token) db.session.add(reg) db.session.commit() return Error.NONE
def message_send(service): text = request.form.get('message') if not text: return Error.ARGUMENT_MISSING('message') subscribers = Subscription.query.filter_by(service=service).count() if subscribers == 0: # Pretend we did something even though we didn't # Nobody is listening so it doesn't really matter return Error.NONE level = (request.form.get('level') or '3')[0] level = int(level) if level in "12345" else 3 title = request.form.get('title', '').strip()[:255] link = request.form.get('link', '').strip() msg = Message(service, text, title, level, link) db.session.add(msg) db.session.commit() if google_api_key or current_app.config['TESTING']: Gcm.send_message(msg) if apns_cert_path or current_app.config['TESTING']: Apns.send_message(msg) if zeromq_relay_uri: queue_zmq_message(json_encode({"message": msg.as_dict()})) service.cleanup() db.session.commit() return Error.NONE
def in_pos(self, az = None, el = None): """ Check if antenna is in position Returns: True if antenna is in position, False otherwise """ if hasattr(self, 'cmd_az') and az is None: az = self.cmd_az if hasattr(self, 'cmd_el') and el is None: el = self.cmd_el if (az is None or el is None): raise Error("in_pos called with arguments az={} and el={}, which is invalid".format(az,el)) if az is None and el is None: az = self.cmd_az el = self.cmd_el azerr, elerr = self.azel_err(az, el) if azerr < self.BEAMWIDTH/2.0 and elerr < self.BEAMWIDTH/2.0: return True else: return False
def ParseDirective(self, key, value): if key == 'RUN': self.cmds.append(CommandTemplate(value)) elif key == 'STDIN_FILE': self.input_filename = value elif key == 'ARGS': self.GetLastCommand().AppendArgs(value) elif key.startswith('ARGS'): suffix = key[len('ARGS'):] if suffix == '*': self.AppendArgsToAllCommands(value) elif re.match(r'^\d+$', suffix): self.GetCommand(int(suffix)).AppendArgs(value) elif key == 'ERROR': self.expected_error = int(value) elif key == 'SLOW': self.slow = True elif key == 'SKIP': self.skip = True elif key == 'VERBOSE-ARGS': self.GetLastCommand().AppendVerboseArgs(value) elif key in ['TODO', 'NOTE']: pass elif key == 'TOOL': self.SetTool(value) elif key == 'ENV': # Pattern: FOO=1 BAR=stuff self.env = dict(x.split('=') for x in value.split()) else: raise Error('Unknown directive: %s' % key)