def _cmd_cd(self, cmd): if len(cmd.args) == 1: if not self._supports_directories(): raise UserError("This device doesn't have directories") path = cmd.args[0] self._execute("__thonny_helper.chdir(%r)" % path) self._update_cwd() return {} else: raise UserError("%cd takes one parameter")
def _cmd_cd(self, cmd): if len(cmd.args) == 1: if not self._supports_directories(): raise UserError("This device doesn't have directories") path = cmd.args[0] self._execute("import os as __thonny_os; __module_os.chdir(%r)" % path) return {} else: raise UserError("%cd takes one parameter")
def _cmd_cd(self, cmd): if len(cmd.args) == 1: path = cmd.args[0] try: os.chdir(path) return ToplevelResponse() except FileNotFoundError: raise UserError("No such folder: " + path) except OSError as e: raise UserError(str(e)) else: raise UserError("cd takes one parameter")
def _cmd_cd(self, cmd): if len(cmd.args) == 1: if not self._supports_directories(): raise UserError("This device doesn't have directories") path = cmd.args[0] self._execute_without_output( "import os as __thonny_helper.os; __thonny_helper.os.chdir(%r)" % path) self._cwd = self._fetch_cwd() return {} else: raise UserError("%cd takes one parameter")
def _cmd_cd(self, cmd): if len(cmd.args) == 1: path = cmd.args[0] try: os.chdir(path) return ToplevelResponse() except FileNotFoundError: raise UserError("No such folder: " + path) except OSError as e: raise UserError("\n".join( traceback.format_exception_only(type(e), e))) else: raise UserError("cd takes one parameter")
def restart_backend(self, clean: bool, first: bool = False, wait: float = 0) -> None: """Recreate (or replace) backend proxy / backend process.""" if not first: get_shell().restart() get_shell().update_idletasks() self.destroy_backend() backend_name = get_workbench().get_option("run.backend_name") if backend_name not in get_workbench().get_backends(): raise UserError( "Can't find backend '{}'. Please select another backend from options" .format(backend_name)) backend_class = get_workbench().get_backends( )[backend_name].proxy_class self._set_state("running") self._proxy = None self._proxy = backend_class(clean) self._poll_vm_messages() if wait: start_time = time.time() while not self.is_waiting_toplevel_command( ) and time.time() - start_time <= wait: # self._pull_vm_messages() get_workbench().update() sleep(0.01) get_workbench().event_generate("BackendRestart")
def _execute_file(self, cmd, executor_class): self._check_update_tty_mode(cmd) if len(cmd.args) >= 1: sys.argv = cmd.args filename = cmd.args[0] if os.path.isabs(filename): full_filename = filename else: full_filename = os.path.abspath(filename) with tokenize.open(full_filename) as fp: source = fp.read() for preproc in self._source_preprocessors: source = preproc(source, cmd) result_attributes = self._execute_source(source, full_filename, "exec", executor_class, cmd, self._ast_postprocessors) result_attributes["filename"] = full_filename return ToplevelResponse(command_name=cmd.name, **result_attributes) else: raise UserError("Command '%s' takes at least one argument" % cmd.name)
def __init__(self, clean: bool, executable: Optional[str] = None) -> None: super().__init__(clean) if executable: self._executable = executable else: self._executable = get_interpreter_for_subprocess() if ".." in self._executable: self._executable = os.path.normpath(self._executable) if not os.path.isfile(self._executable): raise UserError( "Interpreter '%s' does not exist. Please check the configuration!" % self._executable ) self._welcome_text = "" self._proc = None self._terminated_readers = 0 self._response_queue = None self._sys_path = [] self._usersitepackages = None self._gui_update_loop_id = None self._in_venv = None self._cwd = self._get_initial_cwd() # pylint: disable=assignment-from-none self._start_background_process(clean=clean)
def _start_background_process(self, clean=None): # deque, because in one occasion I need to put messages back self._response_queue = collections.deque() # prepare environment env = get_environment_for_python_subprocess(self._executable) # variables controlling communication with the back-end process env["PYTHONIOENCODING"] = "utf-8" # because cmd line option -u won't reach child processes # see https://github.com/thonny/thonny/issues/808 env["PYTHONUNBUFFERED"] = "1" # Let back-end know about plug-ins env["THONNY_USER_DIR"] = THONNY_USER_DIR env["THONNY_FRONTEND_SYS_PATH"] = repr(sys.path) if get_workbench().in_debug_mode(): env["THONNY_DEBUG"] = "1" elif "THONNY_DEBUG" in env: del env["THONNY_DEBUG"] if not os.path.exists(self._executable): raise UserError( "Interpreter (%s) not found. Please recheck corresponding option!" % self._executable) cmd_line = [ self._executable, "-u", # unbuffered IO "-B", # don't write pyo/pyc files # (to avoid problems when using different Python versions without write permissions) ] + self._get_launcher_with_args() creationflags = 0 if running_on_windows(): creationflags = subprocess.CREATE_NEW_PROCESS_GROUP debug("Starting the backend: %s %s", cmd_line, get_workbench().get_local_cwd()) self._proc = subprocess.Popen( cmd_line, # bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.get_cwd() if self.uses_local_filesystem() else None, env=env, universal_newlines=True, creationflags=creationflags, ) # setup asynchronous output listeners Thread(target=self._listen_stdout, args=(self._proc.stdout, ), daemon=True).start() Thread(target=self._listen_stderr, args=(self._proc.stderr, ), daemon=True).start()
def _cmd_Reset(self, cmd): if len(cmd.args) == 0: # nothing to do, because Reset always happens in fresh process return ToplevelResponse( command_name="Reset", welcome_text="Python " + _get_python_version_string(), executable=sys.executable, ) else: raise UserError("Command 'Reset' doesn't take arguments")
def reset_backend(self): self.kill_backend() configuration = get_workbench().get_option("run.backend_configuration") backend_name, configuration_option = parse_configuration(configuration) if backend_name not in get_workbench().get_backends(): raise UserError("Can't find backend '{}'. Please select another backend from options" .format(backend_name)) backend_class = get_workbench().get_backends()[backend_name] self._set_state("running") self._proxy = None self._proxy = backend_class(configuration_option)
def _start_background_process(self, clean=None, extra_args=[]): # deque, because in one occasion I need to put messages back self._response_queue = collections.deque() if not os.path.exists(self._executable): raise UserError( "Interpreter (%s) not found. Please recheck corresponding option!" % self._executable ) cmd_line = ( [ self._executable, "-u", # unbuffered IO "-B", # don't write pyo/pyc files # (to avoid problems when using different Python versions without write permissions) ] + self._get_launcher_with_args() + extra_args ) creationflags = 0 if running_on_windows(): creationflags = subprocess.CREATE_NEW_PROCESS_GROUP debug("Starting the backend: %s %s", cmd_line, get_workbench().get_local_cwd()) extra_params = {} if sys.version_info >= (3, 6): extra_params["encoding"] = "utf-8" self._proc = subprocess.Popen( cmd_line, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self._get_launch_cwd(), env=self._get_environment(), universal_newlines=True, creationflags=creationflags, **extra_params ) # setup asynchronous output listeners self._terminated_readers = 0 Thread(target=self._listen_stdout, args=(self._proc.stdout,), daemon=True).start() Thread(target=self._listen_stderr, args=(self._proc.stderr,), daemon=True).start()
def _cmd_cd(self, cmd): if len(cmd.args) == 1: path = cmd.args[0] self._execute_without_errors( dedent(""" import sys as _thonny_sys try: if _thonny_sys.modules["_thonny_libc"].func("i", "chdir", "s")(%r) != 0: raise OSError("cd failed") finally: del _thonny_sys """) % path) self._cwd = self._fetch_cwd() return {} else: raise UserError("%cd takes one parameter")
def _write_file_via_serial(self, cmd): data = cmd["content_bytes"] # Don't create too long commands BUFFER_SIZE = 512 # prelude out, err, __ = self._execute( dedent(""" __temp_path = '{path}' __temp_f = open(__temp_path, 'wb') __temp_written = 0 """).format(path=cmd["path"]), capture_output=True, ) if out: self._send_output(out, "stdout") if err: self._send_output(err, "stderr") if out or err: return size = len(data) for i in range(0, size, BUFFER_SIZE): chunk_size = min(BUFFER_SIZE, size - i) chunk = data[i:i + chunk_size] self._execute( "__temp_written += __temp_f.write({chunk!r})".format( chunk=chunk), capture_output=True, ) bytes_written = self._evaluate( "__temp_written", cleanup=dedent(""" __temp_f.close() del __temp_f del __temp_written del __temp_path """), ) if bytes_written != size: raise UserError("Expected %d written bytes but wrote %d" % (size, bytes_written))
def _write_file_via_serial( self, source_fp: BinaryIO, target_path: str, file_size: int, callback: Callable[[int, int], None], ) -> None: out, err = self._execute( dedent(""" try: __thonny_path = '{path}' __thonny_written = 0 __thonny_fp = open(__thonny_path, 'wb') except Exception as e: print(str(e)) """).format(path=target_path), capture_output=True, ) if "readonly" in (out + err).replace("-", "").lower(): raise ReadOnlyFilesystemError() elif out + err: raise RuntimeError( "Could not open file %s for writing, output:\n%s" % (target_path, out + err)) # Define function to allow shorter write commands hex_mode = self._should_hexlify(target_path) if hex_mode: self._execute_without_output( dedent(""" from binascii import unhexlify as __thonny_unhex def __W(x): global __thonny_written __thonny_written += __thonny_fp.write(__thonny_unhex(x)) __thonny_fp.flush() if hasattr(__thonny_helper.os, "sync"): __thonny_helper.os.sync() """)) elif self._connected_to_microbit(): # doesn't have neither BytesIO.flush, nor os.sync self._execute_without_output( dedent(""" def __W(x): global __thonny_written __thonny_written += __thonny_fp.write(x) """)) else: self._execute_without_output( dedent(""" def __W(x): global __thonny_written __thonny_written += __thonny_fp.write(x) __thonny_fp.flush() if hasattr(__thonny_helper.os, "sync"): __thonny_helper.os.sync() """)) bytes_sent = 0 block_size = 512 while True: callback(bytes_sent, file_size) block = source_fp.read(block_size) if block: if hex_mode: script = "__W(%r)" % binascii.hexlify(block) else: script = "__W(%r)" % block out, err = self._execute(script, capture_output=True) if out or err: self._show_error( "\nCould not write next block after having written %d bytes to %s" % (bytes_sent, target_path)) if bytes_sent > 0: self._show_error( "Make sure your device's filesystem has enough free space. " + "(When overwriting a file, the old content may occupy space " "until the end of the operation.)\n") raise ManagementError(script, out, err) bytes_sent += len(block) if len(block) < block_size: break bytes_received = self._evaluate("__thonny_written") if bytes_received != bytes_sent: raise UserError("Expected %d written bytes but wrote %d" % (bytes_sent, bytes_received)) # clean up self._execute_without_output( dedent(""" try: del __W del __thonny_written del __thonny_path __thonny_fp.close() del __thonny_fp del __thonny_result del __thonny_unhex except: pass """)) return bytes_sent
def _start_new_process(self, cmd=None): this_python = get_runner().get_frontend_python() # deque, because in one occasion I need to put messages back self._message_queue = collections.deque() # prepare the environment my_env = os.environ.copy() # Delete some environment variables if the backend is (based on) a different Python instance if self._executable not in [ this_python, this_python.replace("python.exe", "pythonw.exe"), this_python.replace("pythonw.exe", "python.exe"), get_private_venv_executable() ]: # Keep only the variables, that are not related to Python my_env = { name: my_env[name] for name in my_env if "python" not in name.lower() and name not in ["TK_LIBRARY", "TCL_LIBRARY"] } # Remove variables used to tweak bundled Thonny-private Python if using_bundled_python(): my_env = { name: my_env[name] for name in my_env if name not in ["SSL_CERT_FILE", "SSL_CERT_DIR", "LD_LIBRARY_PATH"] } # variables controlling communication with the back-end process my_env["PYTHONIOENCODING"] = "ASCII" my_env["PYTHONUNBUFFERED"] = "1" my_env["THONNY_USER_DIR"] = THONNY_USER_DIR # venv may not find (correct) Tk without assistance (eg. in Ubuntu) if self._executable == get_private_venv_executable(): try: my_env["TCL_LIBRARY"] = get_workbench().tk.exprstring( '$tcl_library') my_env["TK_LIBRARY"] = get_workbench().tk.exprstring( '$tk_library') except: logging.exception("Can't find Tcl/Tk library") # If the back-end interpreter is something else than front-end's one, # then it may not have jedi installed. # In this case fffer front-end's jedi for the back-end if self._executable != get_runner().get_frontend_python(): # I don't want to use PYTHONPATH for making jedi available # because that would add it to the front of sys.path my_env["JEDI_LOCATION"] = self._prepare_jedi() if not os.path.exists(self._executable): raise UserError( "Interpreter (%s) not found. Please recheck corresponding option!" % self._executable) import thonny.shared.backend_launcher cmd_line = [ self._executable, '-u', # unbuffered IO (neccessary in Python 3.1) '-B', # don't write pyo/pyc files # (to avoid problems when using different Python versions without write permissions) thonny.shared.backend_launcher.__file__ ] if hasattr(cmd, "filename"): cmd_line.append(cmd.filename) if hasattr(cmd, "args"): cmd_line.extend(cmd.args) if hasattr(cmd, "environment"): my_env.update(cmd.environment) creationflags = 0 if running_on_windows(): creationflags = subprocess.CREATE_NEW_PROCESS_GROUP debug("Starting the backend: %s %s", cmd_line, self.cwd) self._proc = subprocess.Popen( cmd_line, #bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.cwd, env=my_env, universal_newlines=True, creationflags=creationflags) if cmd: # Consume the ready message, cmd will get its own result message ready_line = self._proc.stdout.readline() if ready_line == "": # There was some problem error_msg = self._proc.stderr.read() raise Exception("Error starting backend process: " + error_msg) #ready_msg = parse_message(ready_line) #self._sys_path = ready_msg["path"] #debug("Backend ready: %s", ready_msg) # setup asynchronous output listeners start_new_thread(self._listen_stdout, ()) start_new_thread(self._listen_stderr, ())
def _start_new_process(self, cmd=None): # deque, because in one occasion I need to put messages back self._message_queue = collections.deque() # prepare environment my_env = get_environment_for_python_subprocess(self._executable) # variables controlling communication with the back-end process my_env["PYTHONIOENCODING"] = "utf-8" # Let back-end know about plug-ins my_env["THONNY_USER_DIR"] = THONNY_USER_DIR if get_workbench().in_debug_mode(): my_env["THONNY_DEBUG"] = "1" elif "THONNY_DEBUG" in my_env: del my_env["THONNY_DEBUG"] if not os.path.exists(self._executable): raise UserError( "Interpreter (%s) not found. Please recheck corresponding option!" % self._executable) import thonny.backend_launcher cmd_line = [ self._executable, "-u", # unbuffered IO "-B", # don't write pyo/pyc files # (to avoid problems when using different Python versions without write permissions) thonny.backend_launcher.__file__, ] if hasattr(cmd, "filename"): cmd_line.append(cmd.filename) if hasattr(cmd, "args"): cmd_line.extend(cmd.args) if hasattr(cmd, "environment"): my_env.update(cmd.environment) creationflags = 0 if running_on_windows(): creationflags = subprocess.CREATE_NEW_PROCESS_GROUP debug("Starting the backend: %s %s", cmd_line, get_workbench().get_local_cwd()) self._proc = subprocess.Popen( cmd_line, # bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=get_workbench().get_local_cwd(), env=my_env, universal_newlines=True, creationflags=creationflags, ) # send init message self._send_msg({"frontend_sys_path": sys.path}) if cmd: # Consume the ready message, cmd will get its own result message ready_line = self._proc.stdout.readline() if ready_line == "": # There was some problem error_msg = self._proc.stderr.read() raise Exception("Error starting backend process: " + error_msg) self._store_state_info(parse_message(ready_line)) # setup asynchronous output listeners Thread(target=self._listen_stdout, daemon=True).start() Thread(target=self._listen_stderr, daemon=True).start()
def _write_file_via_serial(self, content_blocks, target_path, notifier=None): result = self._evaluate( dedent( """ try: __thonny_path = '{path}' __thonny_written = 0 __thonny_fp = open(__thonny_path, 'wb') __thonny_result = "OK" except Exception as e: __thonny_result = str(e) __thonny_helper.print_mgmt_value(__thonny_result) del __thonny_result """ ).format(path=target_path), ) if "readonly" in result.replace("-", "").lower(): raise ReadOnlyFilesystemError() elif result != "OK": raise RuntimeError("Problem opening file for writing: " + result) # Define function to allow shorter write commands hex_mode = self._should_hexlify(target_path) if hex_mode: self._execute_without_output( dedent( """ from binascii import unhexlify as __thonny_unhex def __W(x): global __thonny_written __thonny_written += __thonny_fp.write(__thonny_unhex(x)) __thonny_fp.flush() """ ) ) else: self._execute_without_output( dedent( """ def __W(x): global __thonny_written __thonny_written += __thonny_fp.write(x) """ ) ) bytes_sent = 0 for block in content_blocks: if hex_mode: script = "__W(%r)" % binascii.hexlify(block) else: script = "__W(%r)" % block self._execute_without_output(script) bytes_sent += len(block) if notifier is not None: notifier(bytes_sent) bytes_received = self._evaluate("__thonny_written") if bytes_received != bytes_sent: raise UserError("Expected %d written bytes but wrote %d" % (bytes_sent, bytes_received)) # clean up self._execute_without_output( dedent( """ try: del __W del __thonny_written del __thonny_path __thonny_fp.close() del __thonny_fp del __thonny_result del __thonny_unhex except: pass """ ) ) return bytes_sent
def _start_new_process(self, cmd): self._message_queue = collections.deque() # create new backend process my_env = {} for name in os.environ: if ("python" not in name.lower() # skip python vars, because we may use different Python version and name not in ["TK_LIBRARY", "TCL_LIBRARY"]): # They tend to point to frontend Python installation my_env[name] = os.environ[name] # TODO: take care of SSL_CERT_FILE # Unset when we're in builtin python and target python is external my_env["PYTHONIOENCODING"] = "ASCII" my_env["PYTHONUNBUFFERED"] = "1" if not os.path.exists(self._executable): raise UserError("Interpreter (%s) not found. Please recheck corresponding option!" % self._executable) if is_private_interpreter(self._executable): # in gui environment make "pip install" # use a folder outside thonny installation # in order to keep packages after reinstalling Thonny my_env["PIP_USER"] = "******" my_env["PYTHONUSERBASE"] = THONNY_USER_DIR backend_launcher = os.path.join(get_workbench().get_package_dir(), "backend_private", "thonny_backend.py") if not os.path.exists(backend_launcher): # in dev machine use the main source backend_launcher = os.path.realpath( os.path.join(get_workbench().get_package_dir(), "..", "thonny_backend.py") ) cmd_line = [ self._executable, '-u', # unbuffered IO (neccessary in Python 3.1) '-B', # don't write pyo/pyc files # (to avoid problems when using different Python versions without write permissions) backend_launcher ] if hasattr(cmd, "filename"): cmd_line.append(cmd.filename) if hasattr(cmd, "args"): cmd_line.extend(cmd.args) if hasattr(cmd, "environment"): my_env.update(cmd.environment) debug("Starting the backend: %s %s", cmd_line, self.cwd) self._proc = subprocess.Popen ( cmd_line, #bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.cwd, env=my_env, universal_newlines=True ) ready_line = self._proc.stdout.readline() if ready_line == "": # There was some problem error_msg = self._proc.stderr.read() raise Exception("Error starting backend process: " + error_msg) ready_msg = parse_message(ready_line) debug("Backend ready: %s", ready_msg) get_workbench().event_generate("BackendReady", **ready_msg) # setup asynchronous output listeners start_new_thread(self._listen_stdout, ()) start_new_thread(self._listen_stderr, ())
def _start_new_process(self, cmd=None): # deque, because in one occasion I need to put messages back self._message_queue = collections.deque() # create new backend process my_env = {} for name in os.environ: if ("python" not in name.lower() # skip python vars, because we may use different Python version and name not in ["TK_LIBRARY", "TCL_LIBRARY"]): # They tend to point to frontend Python installation my_env[name] = os.environ[name] # TODO: take care of SSL_CERT_FILE # Unset when we're in builtin python and target python is external # And set, wen we're in external and target is builtin (or it's venv) my_env["PYTHONIOENCODING"] = "ASCII" my_env["PYTHONUNBUFFERED"] = "1" my_env["THONNY_USER_DIR"] = THONNY_USER_DIR # venv may not find (correct) Tk without assistance (eg. in Ubuntu) if self._executable == get_private_venv_executable(): try: my_env["TCL_LIBRARY"] = get_workbench().tk.exprstring('$tcl_library') my_env["TK_LIBRARY"] = get_workbench().tk.exprstring('$tk_library') except: logging.exception("Can't find Tcl/Tk library") # I don't want to use PYTHONPATH for making jedi available # because that would add it to the front of sys.path my_env["JEDI_LOCATION"] = self._prepare_jedi() if not os.path.exists(self._executable): raise UserError("Interpreter (%s) not found. Please recheck corresponding option!" % self._executable) import thonny.shared.backend_launcher cmd_line = [ self._executable, '-u', # unbuffered IO (neccessary in Python 3.1) '-B', # don't write pyo/pyc files # (to avoid problems when using different Python versions without write permissions) thonny.shared.backend_launcher.__file__ ] if hasattr(cmd, "filename"): cmd_line.append(cmd.filename) if hasattr(cmd, "args"): cmd_line.extend(cmd.args) if hasattr(cmd, "environment"): my_env.update(cmd.environment) creationflags = 0 if running_on_windows(): creationflags = subprocess.CREATE_NEW_PROCESS_GROUP debug("Starting the backend: %s %s", cmd_line, self.cwd) self._proc = subprocess.Popen ( cmd_line, #bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.cwd, env=my_env, universal_newlines=True, creationflags=creationflags ) if cmd: # Consume the ready message, cmd will get its own result message ready_line = self._proc.stdout.readline() if ready_line == "": # There was some problem error_msg = self._proc.stderr.read() raise Exception("Error starting backend process: " + error_msg) #ready_msg = parse_message(ready_line) #self._sys_path = ready_msg["path"] #debug("Backend ready: %s", ready_msg) # setup asynchronous output listeners start_new_thread(self._listen_stdout, ()) start_new_thread(self._listen_stderr, ())