def swapSlides(self): self.remaining = 0 self.slide_index += 1 self.slide_index %= self.SLIDE_NUMS self.slide_surface_prev = pygame.image.load( find_abs(f"bg_slide{(self.slide_index - 1) % self.SLIDE_NUMS}.png", asset_locations())) self.slide_surface_next = pygame.image.load( find_abs(f"bg_slide{self.slide_index}.png", asset_locations()))
def clickSimBots(self, preset): abs_path = find_abs(preset, allowed_areas=preset_locations()) with open(abs_path, "r") as f: preset_config = yaml.safe_load(f) sim_path = find_abs(preset_config["sim_location"], allowed_areas=batch_locations()) ScreenObjectManager.instance.pushScreen( ScreenObjectManager.SCREEN_BOTS, batch_file=sim_path, )
def runFromConfig(config, shared): from ev3sim.robot import initialise_bot, RobotInteractor from ev3sim.file_helper import find_abs sl = ScriptLoader(**config.get("loader", {})) sl.setSharedData(shared) sl.active_scripts = [] ev3sim.visual.utils.GLOBAL_COLOURS = config.get("colours", {}) for index, robot in enumerate(config.get("robots", [])): robot_path = find_abs(robot, allowed_areas=[ "local", "local/robots/", "package", "package/robots/" ]) initialise_bot(config, robot_path, f"Robot-{index}") for opt in config.get("interactors", []): try: sl.active_scripts.append(fromOptions(opt)) except Exception as exc: print( f"Failed to load interactor with the following options: {opt}. Got error: {exc}" ) if sl.active_scripts: sl.startUp(**config.get("screen", {})) sl.loadElements(config.get("elements", [])) for interactor in sl.active_scripts: if isinstance(interactor, RobotInteractor): interactor.connectDevices() sl.simulate() else: print("No interactors successfully loaded. Quitting...")
def batched_run(batch_file, bind_addr): from ev3sim.single_run import single_run as sim from ev3sim.attach import main as attach batch_path = find_abs(batch_file, allowed_areas=['local', 'local/batched_commands/', 'package', 'package/batched_commands/']) with open(batch_path, 'r') as f: config = yaml.safe_load(f) bot_paths = [x['name'] for x in config['bots']] sim_process = Process(target=sim, args=[config['preset_file'], bot_paths, bind_addr]) script_processes = [] for i, bot in enumerate(config['bots']): for script in bot.get('scripts', []): script_processes.append(Process(target=attach, kwargs={ 'passed_args': ['Useless', '--send_logs', '--simulator_addr', bind_addr, script, f"Robot-{i}"] })) sim_process.start() time.sleep(0.5) # Give the gRPC server 500ms to start for p in script_processes: p.start() # At the moment, just wait for the simulator to finish then kill all attach processes. # If any attach threads error out, then the stack trace is printed anyways so this is fine. sim_process.join() for p in script_processes: p.terminate()
def action(): with open( os.path.join( find_abs(sim_config["bots"][index], bot_locations()), fname), "w") as f: f.write("# Put your code here!\n")
def initWithKwargs(self, **kwargs): self.in_error = False self.first_launch = True batch = kwargs.get("batch_file", None) self.batch = batch if batch is None: # We are simply viewing the bots to edit or manage. self.bot_keys = [] else: self.key_index = 0 with open(batch, "r") as f: b_config = yaml.safe_load(f) preset = b_config["preset_file"] fname = find_abs(preset, allowed_areas=preset_locations()) with open(fname, "r") as f: p_config = yaml.safe_load(f) self.bot_keys = p_config["bot_names"] self.bot_values = [None] * len(self.bot_keys) self.preview_images = [None] * len(self.bot_keys) self.bot_select_index = 0 self.select_enable = False self.code_enable = False self.edit_enable = False self.remove_enable = False self.bot_index = -1 self.next = kwargs.get("next", None) self.next_kwargs = kwargs.get("next_kwargs", {}) super().initWithKwargs(**kwargs)
def onClickMapEditor(filename): from ev3sim.visual.manager import ScreenObjectManager ScreenObjectManager.instance.pushScreen( ScreenObjectManager.SCREEN_RESCUE_EDIT, batch_file=find_abs(f"{filename}.sim", batch_locations()), )
def run_sim(sim_path, edit=False): from ev3sim.validation.batch_files import BatchValidator if not BatchValidator.validate_file(sim_path): return raise_error( f"There is something wrong with the sim {sim_path}, and so it cannot be opened or used." ) if not edit: return [ScreenObjectManager.SCREEN_SIM], [{ "batch": sim_path, }] import importlib with open(sim_path, "r") as f: conf = yaml.safe_load(f) with open(find_abs(conf["preset_file"], preset_locations())) as f: preset = yaml.safe_load(f) if "visual_settings" not in preset: return raise_error("This preset cannot be edited.") mname, cname = preset["visual_settings"].rsplit(".", 1) klass = getattr(importlib.import_module(mname), cname) return [ScreenObjectManager.SCREEN_SETTINGS], [{ "file": sim_path, "settings": klass, "allows_filename_change": True, "extension": "sim", }]
def initialiseFromConfig(config, send_queues, recv_queues): from collections import defaultdict from ev3sim.robot import initialise_bot, RobotInteractor ev3sim.visual.utils.GLOBAL_COLOURS = config.get("colours", {}) # Keep track of index w.r.t. filename. robot_paths = defaultdict(lambda: 0) for index, robot in enumerate(config.get("robots", [])): robot_path = find_abs(robot, allowed_areas=bot_locations()) initialise_bot(config, robot_path, f"Robot-{index}", robot_paths[robot_path]) robot_paths[robot_path] += 1 ScriptLoader.instance.setRobotQueues(f"Robot-{index}", send_queues[index], recv_queues[index]) for opt in config.get("interactors", []): try: ScriptLoader.instance.addActiveScript(fromOptions(opt)) except Exception as exc: print( f"Failed to load interactor with the following options: {opt}. Got error: {exc}" ) SettingsManager.instance.setMany(config["settings"]) if ScriptLoader.instance.active_scripts: ScriptLoader.instance.startUp() ScriptLoader.instance.loadElements(config.get("elements", [])) for interactor in ScriptLoader.instance.active_scripts: if isinstance(interactor, RobotInteractor): interactor.connectDevices() for interactor in ScriptLoader.instance.active_scripts: interactor.startUp() else: print("No interactors successfully loaded. Quitting...")
def simulate(batch_file, preset_filename, bot_paths, seed, override_settings, *queues_sizes): result_queue = queues_sizes[0][0] result_queue._internal_size = queues_sizes[0][1] StateHandler.instance.shared_info = { "result_queue": result_queue, } send_queues = [q for q, _ in queues_sizes[1::2]] for i, (_, size) in enumerate(queues_sizes[1::2]): send_queues[i]._internal_size = size recv_queues = [q for q, _ in queues_sizes[2::2]] for i, (_, size) in enumerate(queues_sizes[2::2]): recv_queues[i]._internal_size = size Randomiser.createGlobalRandomiserWithSeed(seed) preset_file = find_abs(preset_filename, allowed_areas=preset_locations()) with open(preset_file, "r") as f: config = yaml.safe_load(f) config["settings"] = config.get("settings", {}) recursive_merge(config["settings"], override_settings) config["robots"] = config.get("robots", []) + bot_paths initialiseFromConfig(config, send_queues, recv_queues)
def clickSettings(): ScreenObjectManager.instance.pushScreen( ScreenObjectManager.SCREEN_SETTINGS, file=find_abs("user_config.yaml", config_locations()), settings=main_settings, ) ScreenObjectManager.instance.screens[ ScreenObjectManager.SCREEN_SETTINGS].clearEvents()
def clickSimSettings(self, preset): import importlib abs_path = find_abs(preset, allowed_areas=preset_locations()) with open(abs_path, "r") as f: preset_config = yaml.safe_load(f) sim_path = find_abs(preset_config["sim_location"], allowed_areas=batch_locations()) mname, cname = preset_config["visual_settings"].rsplit(".", 1) klass = getattr(importlib.import_module(mname), cname) ScreenObjectManager.instance.pushScreen( ScreenObjectManager.SCREEN_SETTINGS, file=sim_path, settings=klass, allows_filename_change=False, extension="sim", )
def setCheckboxBg(self, value, obj): img = pygame.image.load( find_abs("ui/box_check.png" if value else "ui/box_clear.png", allowed_areas=asset_locations())) if img.get_size() != obj.rect.size: img = pygame.transform.smoothscale( img, (obj.rect.width, obj.rect.height)) obj.set_image(img)
def image_path(self, value): from ev3sim.file_helper import find_abs self._image_path = find_abs(value, allowed_areas=[ "local", "local/assets/", "package", "package/assets/" ]) self.image = pygame.image.load(self._image_path)
def startScreen(self): from ev3sim.file_helper import find_abs pygame.init() self.screen = pygame.display.set_mode((self.screen_width, self.screen_height), pygame.RESIZABLE) pygame.display.set_caption('MHS Robotics Club Simulator') img_path = find_abs('Logo.png', allowed_areas=['package/assets/']) img = pygame.image.load(img_path) img.set_colorkey((255, 255, 255)) pygame.display.set_icon(img)
def captureBotImage(self, directory, filename): self.resetVisualElements() from os.path import join from ev3sim.simulation.loader import ScriptLoader from ev3sim.robot import initialise_bot, RobotInteractor from ev3sim.simulation.randomisation import Randomiser Randomiser.createGlobalRandomiserWithSeed(0) ScriptLoader.instance.reset() ScriptLoader.instance.startUp() elems = {} initialise_bot(elems, find_abs(filename, [directory]), "", 0) ScriptLoader.instance.loadElements(elems.get("elements", [])) for interactor in ScriptLoader.instance.active_scripts: if isinstance(interactor, RobotInteractor): interactor.connectDevices() interactor.initialiseDevices() for interactor in ScriptLoader.instance.active_scripts: interactor.startUp() interactor.tick(0) interactor.afterPhysics() screen = pygame.Surface((480, 480), pygame.SRCALPHA) custom_map = { "SCREEN_WIDTH": 480, "SCREEN_HEIGHT": 480, "MAP_WIDTH": 25, "MAP_HEIGHT": 25, } for elem in self.objects.values(): elem.customMap = custom_map elem.calculatePoints() self.applyToScreen(screen, bg=pygame.Color(self.instance.background_colour)) colorkey = pygame.Color(self.instance.background_colour) for x in range(480): for y in range(480): val = screen.get_at((x, y)) val.a = 0 if (val.r == colorkey.r and val.g == colorkey.g and val.b == colorkey.b) else 255 screen.set_at((x, y), val) self.resetVisualElements() ScriptLoader.instance.reset() config_path = join(find_abs(filename, [directory]), "config.bot") with open(config_path, "r") as f: config = yaml.safe_load(f) pygame.image.save(screen, join(find_abs(filename, [directory]), config.get("preview_path", "preview.png")))
def initialise_device(deviceData, parentObj, index, preview_mode=False): classes = find_abs("devices/classes.yaml") devices = yaml.safe_load(open(classes, "r")) name = deviceData["name"] if name not in devices: raise ValueError(f"Unknown device type {name}") fname = find_abs(devices[name], allowed_areas=device_locations()) with open(fname, "r") as f: try: config = yaml.safe_load(f) utils.GLOBAL_COLOURS.update(config.get("colours", {})) mname, cname = config["class"].rsplit(".", 1) import importlib klass = getattr(importlib.import_module(mname), cname) relative_location = deviceData.get("position", [0, 0]) relative_rotation = deviceData.get("rotation", 0) * np.pi / 180 device = klass(parentObj, relative_location, relative_rotation) for i, opt in enumerate(config.get("interactors", [])): res = opt.get("kwargs", {}) res.update({ "device": device, "parent": parentObj, "relative_location": relative_location, "relative_rotation": relative_rotation, "device_index": index, "single_device_index": i, "port": deviceData["port"], "zPos": deviceData.get("zPos", 0), }) if preview_mode: for i in range(len(res.get("elements", []))): res["elements"][i]["physics"] = True opt["kwargs"] = res interactor = fromOptions(opt) if not hasattr(parentObj, "device_interactors"): parentObj.device_interactors = [] parentObj.device_interactors.append(interactor) ScriptLoader.instance.addActiveScript(interactor) except yaml.YAMLError as exc: print( f"An error occurred while loading devices. Exited with error: {exc}" )
def initFromKwargs(self, **kwargs): super().initFromKwargs(**kwargs) from ev3sim.file_helper import find_abs self.font_style = kwargs.get('font_style', "OpenSans-SemiBold.ttf") self.font_path = find_abs(self.font_style, allowed_areas=['local/assets/', 'local', 'package/assets/', 'package']) self.font_size = kwargs.get('font_size', 30) self.font = pygame.freetype.Font(self.font_path, self.font_size) self.hAlignment = kwargs.get('hAlignment', 'l') self.vAlignment = kwargs.get('vAlignment', 't') self.text = kwargs.get('text', 'Test')
def image_path(self, value): from ev3sim.file_helper import find_abs image_path = find_abs(value, allowed_areas=asset_locations()) if image_path != self._image_path: self._image_path = image_path self.image = pygame.image.load(self._image_path) try: self.calculatePoints() except: pass
def action(): import yaml """Go through and try fixing the bots.""" for bot in old_bots: dirpath = os.path.join(path, bot[:-4]) # Folder if os.path.isdir(dirpath): import shutil shutil.rmtree(dirpath) os.mkdir(dirpath) # config.bot with open(os.path.join(path, bot), "r") as f: config = yaml.safe_load(f) bot_script = config.get("script", "code.py") preview_image = config.get("preview_path", "preview.png") for keyword in ["script", "preview_path"]: if keyword in config: del config[keyword] with open(os.path.join(dirpath, "config.bot"), "w") as f: f.write(yaml.dump(config)) # code.py try: code_path = os.path.join( find_abs_directory("workspace/code/"), bot_script) with open(code_path, "r") as f: code = f.read() except: code = "" with open(os.path.join(dirpath, "code.py"), "w") as f: f.write(code) # preview.png try: preview = find_abs(preview_image, asset_locations()) with open(preview, "rb") as f: preview_data = f.read() except: preview_data = bytes() with open(os.path.join(dirpath, "preview.png"), "wb") as f: f.write(preview_data) # Remove the old bot os.remove(os.path.join(path, bot)) # Additionally, we need to update all sim files to no longer use the .bot prefix actual_dir = find_abs_directory("workspace/sims/") for file in os.listdir(actual_dir): if os.path.isfile(os.path.join( actual_dir, file)) and file.endswith(".sim"): with open(os.path.join(actual_dir, file), "r") as f: config = yaml.safe_load(f) for x in range(len(config.get("bots", []))): if config["bots"][x].endswith(".bot"): config["bots"][x] = config["bots"][x][:-4] with open(os.path.join(actual_dir, file), "w") as f: f.write(yaml.dump(config))
def run_code(script_name): real = find_abs("examples/robots/default_testing", ["package"]) rel_dir, rel_path = make_relative(real, bot_locations()) with open(join(real, "config.bot"), "r") as f: bot_config = yaml.safe_load(f) bot_config["script"] = script_name bot_config["type"] = "mindstorms" if script_name.endswith( ".ev3") else "python" with open(join(real, "config.bot"), "w") as f: f.write(yaml.dump(bot_config)) sim_path = find_abs("presets/testing.sim", ["package"]) with open(sim_path, "r") as f: test_config = yaml.safe_load(f) test_config["bots"] = [rel_path] with open(sim_path, "w") as f: f.write(yaml.dump(test_config)) return run_sim(sim_path)
def updateFlipCheckbox(self): img = pygame.image.load( find_abs( "ui/box_check.png" if self.getSelectedAttribute("flip", False) else "ui/box_clear.png", allowed_areas=asset_locations(), )) if img.get_size() != self.flip_image.rect.size: img = pygame.transform.smoothscale( img, (self.flip_image.rect.width, self.flip_image.rect.height)) self.flip_image.set_image(img)
def generateVisual(self, size, container, manager, idx): self.container = container off = self.offset(size) label_size = ((size[0] - 40) / 2 - 10, 40) label_pos = (off[0] + 20, off[1]) label = pygame_gui.elements.UILabel( relative_rect=pygame.Rect(*label_pos, *label_size), manager=manager, object_id=pygame_gui.core.ObjectID(f"{idx}-file-label", "entry-label"), container=container, text=self.title, ) button_size = ((size[0] - 40) * 0.25 - 20, 44) button_size = [min(button_size[0], button_size[1])] * 2 file_size = ((size[0] - 40) / 2 - button_size[0] - 30, 40) button_pos = ( off[0] + 50 + label_size[0] + file_size[0], off[1] - 2, ) file_pos = (off[0] + 40 + label_size[0], off[1]) click = pygame_gui.elements.UIButton( relative_rect=pygame.Rect(*button_pos, *button_size), manager=manager, object_id=pygame_gui.core.ObjectID(f"{idx+2}-button", "file-button"), container=container, text="", ) img = pygame.image.load( find_abs("ui/folder.png", allowed_areas=asset_locations())) if img.get_size() != button_size: img = pygame.transform.smoothscale(img, button_size) click_icon = pygame_gui.elements.UIImage( relative_rect=pygame.Rect(*button_pos, *button_size), manager=manager, object_id=pygame_gui.core.ObjectID(f"{idx+3}-image", "file-image"), container=container, image_surface=img, ) self.filename = pygame_gui.elements.UILabel( relative_rect=pygame.Rect(*file_pos, *file_size), manager=manager, object_id=pygame_gui.core.ObjectID(f"{idx+1}-file-name", "entry-label"), container=container, text=self.current if self.current else "", ) return [label, self.filename, click, click_icon]
def initialise_bot(topLevelConfig, robotFolder, prefix, path_index): # Returns the robot class, as well as a completed robot to add to the elements list. import yaml from os.path import join with open(join(robotFolder, "config.bot"), "r") as f: try: config = yaml.safe_load(f) mname, cname = config.get("robot_class", "ev3sim.robot.Robot").rsplit(".", 1) import importlib klass = getattr(importlib.import_module(mname), cname) bot_config = config["base_plate"] bot_config["type"] = "object" bot_config["physics"] = True add_devices(bot_config, config.get("devices", [])) add_to_key(bot_config, prefix) add_to_zpos(bot_config, 10) # Append bot object to elements. topLevelConfig["elements"] = topLevelConfig.get("elements", []) + [bot_config] robot = klass() ScriptLoader.instance.addActiveScript( RobotInteractor( **{ "robot": robot, "base_key": bot_config["key"], "path_index": path_index, # Don't include directories here, since that shouldn't affect randomisation. "filename": robotFolder.replace("\\", "/").rsplit("/", 1)[-1], })) robot.ID = prefix robot._follow_collider_offset = config.get("follow_collider", [0, 0]) ScriptLoader.instance.robots[prefix] = robot ScriptLoader.instance.outstanding_events[prefix] = [] if config.get("type", "python") == "mindstorms": scriptname = config.get("script", "program.ev3") else: scriptname = config.get("script", "code.py") if scriptname is not None: scriptname = find_abs(scriptname, code_locations(robotFolder)) ScriptLoader.instance.scriptnames[prefix] = scriptname except yaml.YAMLError as exc: print( f"An error occurred while loading robot preset {robotFolder}. Exited with error: {exc}" )
def action(result): SettingsManager.instance.setMany( {"app": { "send_crash_reports": result, }}) # Change user_config to have workspace_folder = "" config_file = find_abs("user_config.yaml", config_locations()) with open(config_file, "r") as f: conf = yaml.safe_load(f) conf["app"]["send_crash_reports"] = result with open(config_file, "w") as f: f.write(yaml.dump(conf))
def initFromKwargs(self, **kwargs): super().initFromKwargs(**kwargs) from ev3sim.file_helper import find_abs self.font_style = kwargs.get("font_style", "fonts/OpenSans-SemiBold.ttf") self.font_path = find_abs(self.font_style, allowed_areas=asset_locations()) self.font_size = kwargs.get("font_size", 30) self.hAlignment = kwargs.get("hAlignment", "l") self.vAlignment = kwargs.get("vAlignment", "t") self.text = kwargs.get("text", "Test")
def initialise_device(deviceData, parentObj, index): classes = find_abs('devices/classes.yaml') devices = yaml.safe_load(open(classes, 'r')) name = deviceData['name'] if name not in devices: raise ValueError(f"Unknown device type {name}") fname = find_abs(devices[name], allowed_areas=['local/devices/', 'package/devices/']) with open(fname, 'r') as f: try: config = yaml.safe_load(f) utils.GLOBAL_COLOURS.update(config.get('colours', {})) mname, cname = config['class'].rsplit('.', 1) import importlib klass = getattr(importlib.import_module(mname), cname) relative_location = deviceData.get('position', [0, 0]) relative_rotation = deviceData.get('rotation', 0) * np.pi / 180 device = klass(parentObj, relative_location, relative_rotation) for i, opt in enumerate(config.get('interactors', [])): res = opt.get('kwargs', {}) res.update({ 'device': device, 'parent': parentObj, 'relative_location': relative_location, 'relative_rotation': relative_rotation, 'device_index': index, 'single_device_index': i, 'port': deviceData['port'], }) opt['kwargs'] = res interactor = fromOptions(opt) if not hasattr(parentObj, 'device_interactors'): parentObj.device_interactors = [] parentObj.device_interactors.append(interactor) # Device interactors always act first. ScriptLoader.instance.active_scripts.insert(0, interactor) except yaml.YAMLError as exc: print( f"An error occured while loading devices. Exited with error: {exc}" )
def initFromKwargs(self, **kwargs): super().initFromKwargs(**kwargs) from ev3sim.file_helper import find_abs self.font_style = kwargs.get("font_style", "OpenSans-SemiBold.ttf") self.font_path = find_abs( self.font_style, allowed_areas=["local/assets/", "local", "package/assets/", "package"] ) self.font_size = kwargs.get("font_size", 30) self.font = pygame.freetype.Font(self.font_path, self.font_size) self.hAlignment = kwargs.get("hAlignment", "l") self.vAlignment = kwargs.get("vAlignment", "t") self.text = kwargs.get("text", "Test")
def fromOptions(options): if "filename" in options: import yaml fname = find_abs(options["filename"]) with open(fname, "r") as f: config = yaml.safe_load(f) return fromOptions(config) if "class_path" not in options: raise ValueError( "Your options has no 'class_path' or 'filename' entry (Or the file you reference has no 'class_path' entry')" ) import importlib if isinstance(options["class_path"], str): mname, cname = options["class_path"].rsplit(".", 1) klass = getattr(importlib.import_module(mname), cname) else: from importlib.machinery import SourceFileLoader module = SourceFileLoader( "not_main", find_abs(options["class_path"][0], preset_locations())).load_module() klass = getattr(module, options["class_path"][1]) topObj = klass(*options.get("args", []), **options.get("kwargs", {})) # Add any settings for this interactor, if applicable. # This only works for package presets. Not workspace ones. if "settings_name" in options: name = options["settings_name"] if "settings_defn" not in options: raise ValueError( f"Expected a settings object to add with group name {name}") mname, cname = options["settings_defn"].rsplit(".", 1) obj = getattr(importlib.import_module(mname), cname) SettingsManager.instance.addSettingGroup(name, obj) # We need to remove this setting once the simulation ends, so save the name. topObj._settings_name = name return topObj
def single_run(preset_filename, robots, bind_addr): preset_file = find_abs(preset_filename, allowed_areas=['local', 'local/presets/', 'package', 'package/presets/']) with open(preset_file, 'r') as f: config = yaml.safe_load(f) config['robots'] = config.get('robots', []) + robots shared_data = { 'tick': 0, # Current tick 'write_stack': deque(), # All write actions are processed through this 'data_queue': {}, # Simulation data for each bot 'active_count': {}, # Keeps track of which code connection each bot has. 'bot_locks': {}, # Threading Locks and Conditions for each bot to wait for connection actions 'bot_communications_data': {}, # Buffers and information for all bot communications 'tick_updates': {}, # Simply a dictionary where the simulation tick will push static data, so the other methods are aware of when the simulation has exited. } result_bucket = Queue(maxsize=1) from threading import Thread from ev3sim.simulation.communication import start_server_with_shared_data def run(shared_data, result): try: runFromConfig(config, shared_data) except Exception as e: result.put(('Simulation', e)) return result.put(True) comm_thread = Thread(target=start_server_with_shared_data, args=(shared_data, result_bucket, bind_addr), daemon=True) sim_thread = Thread(target=run, args=(shared_data, result_bucket), daemon=True) comm_thread.start() sim_thread.start() try: with result_bucket.not_empty: while not result_bucket._qsize(): result_bucket.not_empty.wait(0.1) r = result_bucket.get() # Chuck it back on the queue so that other threads know we are quitting. result_bucket.put(r) if r is not True: print(f"An error occured in the {r[0]} thread. Raising an error now...") time.sleep(1) raise r[1] except KeyboardInterrupt: pass