import sys import os def run_file(path): with open(path, "r") as file: file_content = file.read() # ipykernel patches the loader, so that # `import __main__` does not produce the "real" __main__, but rather, # the facade that is the user facing __main__ # to get the "real" main, do: # d = sys.modules["__main__"].__dict__ from __main__ import __dict__ as d exec(file_content, d, d) class RunMainScriptContext: def __init__(self, path, argv=None): self.path = path self.argv = tuple(argv) if argv is not None else None def __enter__(self): sys.path.insert(0, os.path.dirname(self.path)) if self.argv is not None: self._orig_sys_argv = sys.argv sys.argv = [self.path] + list(self.argv) def __exit__(self, *_): # try restore sys.path try: sys.path.remove(os.path.dirname(self.path)) except ValueError: pass # try restore sys.argv if we patched it if self.argv is not None: # restore sys.argv if it's unmodified from what we set it to. # otherwise, leave it as-is. patched_argv = [self.path] + list(self.argv) if sys.argv == patched_argv: sys.argv = self._orig_sys_argv def _launch_lsp_server_on_thread(path, args): # used by Positron reticulate launcher... # TODO: update Positron to replace usage of this with `run_file_on_thread()` return run_file_on_thread(path, args) def set_blank_io_streams(): import sys import os sys.stdout = open(os.devnull, "w") sys.stderr = open(os.devnull, "w") def run_file_on_thread(path, argv=None, init_globals=None, run_name="__main__"): # for now, leave sys.argv and sys.path permanently modified. # Later, revisit if it's desirable/safe to restore after the initial # lsp event loop startup. import _thread from runpy import run_path RunMainScriptContext(path, argv).__enter__() _thread.start_new_thread( run_path, (path,), { "run_name": run_name, "init_globals": init_globals, }, )