Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
runpy.py320 linesDownload Raw Back to Lib
1"""runpy.py - locating and running Python code using the module namespace2 3Provides support for locating and running Python scripts using the Python4module namespace instead of the native filesystem.5 6This allows Python code to play nicely with non-filesystem based PEP 3027importers when locating support scripts as well as when importing modules.8"""9# Written by Nick Coghlan <ncoghlan at gmail.com>10#    to implement PEP 338 (Executing Modules as Scripts)11 12 13import sys14import importlib.machinery # importlib first so we can test #15386 via -m15import importlib.util16import io17import os18 19__all__ = [20    "run_module", "run_path",21]22 23# avoid 'import types' just for ModuleType24ModuleType = type(sys)25 26class _TempModule(object):27    """Temporarily replace a module in sys.modules with an empty namespace"""28    def __init__(self, mod_name):29        self.mod_name = mod_name30        self.module = ModuleType(mod_name)31        self._saved_module = []32 33    def __enter__(self):34        mod_name = self.mod_name35        try:36            self._saved_module.append(sys.modules[mod_name])37        except KeyError:38            pass39        sys.modules[mod_name] = self.module40        return self41 42    def __exit__(self, *args):43        if self._saved_module:44            sys.modules[self.mod_name] = self._saved_module[0]45        else:46            del sys.modules[self.mod_name]47        self._saved_module = []48 49class _ModifiedArgv0(object):50    def __init__(self, value):51        self.value = value52        self._saved_value = self._sentinel = object()53 54    def __enter__(self):55        if self._saved_value is not self._sentinel:56            raise RuntimeError("Already preserving saved value")57        self._saved_value = sys.argv[0]58        sys.argv[0] = self.value59 60    def __exit__(self, *args):61        self.value = self._sentinel62        sys.argv[0] = self._saved_value63 64# TODO: Replace these helpers with importlib._bootstrap_external functions.65def _run_code(code, run_globals, init_globals=None,66              mod_name=None, mod_spec=None,67              pkg_name=None, script_name=None):68    """Helper to run code in nominated namespace"""69    if init_globals is not None:70        run_globals.update(init_globals)71    if mod_spec is None:72        loader = None73        fname = script_name74        cached = None75    else:76        loader = mod_spec.loader77        fname = mod_spec.origin78        cached = mod_spec.cached79        if pkg_name is None:80            pkg_name = mod_spec.parent81    run_globals.update(__name__ = mod_name,82                       __file__ = fname,83                       __cached__ = cached,84                       __doc__ = None,85                       __loader__ = loader,86                       __package__ = pkg_name,87                       __spec__ = mod_spec)88    exec(code, run_globals)89    return run_globals90 91def _run_module_code(code, init_globals=None,92                    mod_name=None, mod_spec=None,93                    pkg_name=None, script_name=None):94    """Helper to run code in new namespace with sys modified"""95    fname = script_name if mod_spec is None else mod_spec.origin96    with _TempModule(mod_name) as temp_module, _ModifiedArgv0(fname):97        mod_globals = temp_module.module.__dict__98        _run_code(code, mod_globals, init_globals,99                  mod_name, mod_spec, pkg_name, script_name)100    # Copy the globals of the temporary module, as they101    # may be cleared when the temporary module goes away102    return mod_globals.copy()103 104# Helper to get the full name, spec and code for a module105def _get_module_details(mod_name, error=ImportError):106    if mod_name.startswith("."):107        raise error("Relative module names not supported")108    pkg_name, _, _ = mod_name.rpartition(".")109    if pkg_name:110        # Try importing the parent to avoid catching initialization errors111        try:112            __import__(pkg_name)113        except ImportError as e:114            # If the parent or higher ancestor package is missing, let the115            # error be raised by find_spec() below and then be caught. But do116            # not allow other errors to be caught.117            if e.name is None or (e.name != pkg_name and118                    not pkg_name.startswith(e.name + ".")):119                raise120        # Warn if the module has already been imported under its normal name121        existing = sys.modules.get(mod_name)122        if existing is not None and not hasattr(existing, "__path__"):123            from warnings import warn124            msg = "{mod_name!r} found in sys.modules after import of " \125                "package {pkg_name!r}, but prior to execution of " \126                "{mod_name!r}; this may result in unpredictable " \127                "behaviour".format(mod_name=mod_name, pkg_name=pkg_name)128            warn(RuntimeWarning(msg))129 130    try:131        spec = importlib.util.find_spec(mod_name)132    except (ImportError, AttributeError, TypeError, ValueError) as ex:133        # This hack fixes an impedance mismatch between pkgutil and134        # importlib, where the latter raises other errors for cases where135        # pkgutil previously raised ImportError136        msg = "Error while finding module specification for {!r} ({}: {})"137        if mod_name.endswith(".py"):138            msg += (f". Try using '{mod_name[:-3]}' instead of "139                    f"'{mod_name}' as the module name.")140        raise error(msg.format(mod_name, type(ex).__name__, ex)) from ex141    if spec is None:142        raise error("No module named %s" % mod_name)143    if spec.submodule_search_locations is not None:144        if mod_name == "__main__" or mod_name.endswith(".__main__"):145            raise error("Cannot use package as __main__ module")146        try:147            pkg_main_name = mod_name + ".__main__"148            return _get_module_details(pkg_main_name, error)149        except error as e:150            if mod_name not in sys.modules:151                raise  # No module loaded; being a package is irrelevant152            raise error(("%s; %r is a package and cannot " +153                               "be directly executed") %(e, mod_name))154    loader = spec.loader155    if loader is None:156        raise error("%r is a namespace package and cannot be executed"157                                                                 % mod_name)158    try:159        code = loader.get_code(mod_name)160    except ImportError as e:161        raise error(format(e)) from e162    if code is None:163        raise error("No code object available for %s" % mod_name)164    return mod_name, spec, code165 166class _Error(Exception):167    """Error that _run_module_as_main() should report without a traceback"""168 169# XXX ncoghlan: Should this be documented and made public?170# (Current thoughts: don't repeat the mistake that lead to its171# creation when run_module() no longer met the needs of172# mainmodule.c, but couldn't be changed because it was public)173def _run_module_as_main(mod_name, alter_argv=True):174    """Runs the designated module in the __main__ namespace175 176       Note that the executed module will have full access to the177       __main__ namespace. If this is not desirable, the run_module()178       function should be used to run the module code in a fresh namespace.179 180       At the very least, these variables in __main__ will be overwritten:181           __name__182           __file__183           __cached__184           __loader__185           __package__186    """187    try:188        if alter_argv or mod_name != "__main__": # i.e. -m switch189            mod_name, mod_spec, code = _get_module_details(mod_name, _Error)190        else:          # i.e. directory or zipfile execution191            mod_name, mod_spec, code = _get_main_module_details(_Error)192    except _Error as exc:193        msg = "%s: %s" % (sys.executable, exc)194        sys.exit(msg)195    main_globals = sys.modules["__main__"].__dict__196    if alter_argv:197        sys.argv[0] = mod_spec.origin198    return _run_code(code, main_globals, None,199                     "__main__", mod_spec)200 201def run_module(mod_name, init_globals=None,202               run_name=None, alter_sys=False):203    """Execute a module's code without importing it.204 205       mod_name -- an absolute module name or package name.206 207       Optional arguments:208       init_globals -- dictionary used to pre-populate the module’s209       globals dictionary before the code is executed.210 211       run_name -- if not None, this will be used for setting __name__;212       otherwise, __name__ will be set to mod_name + '__main__' if the213       named module is a package and to just mod_name otherwise.214 215       alter_sys -- if True, sys.argv[0] is updated with the value of216       __file__ and sys.modules[__name__] is updated with a temporary217       module object for the module being executed. Both are218       restored to their original values before the function returns.219 220       Returns the resulting module globals dictionary.221    """222    mod_name, mod_spec, code = _get_module_details(mod_name)223    if run_name is None:224        run_name = mod_name225    if alter_sys:226        return _run_module_code(code, init_globals, run_name, mod_spec)227    else:228        # Leave the sys module alone229        return _run_code(code, {}, init_globals, run_name, mod_spec)230 231def _get_main_module_details(error=ImportError):232    # Helper that gives a nicer error message when attempting to233    # execute a zipfile or directory by invoking __main__.py234    # Also moves the standard __main__ out of the way so that the235    # preexisting __loader__ entry doesn't cause issues236    main_name = "__main__"237    saved_main = sys.modules[main_name]238    del sys.modules[main_name]239    try:240        return _get_module_details(main_name)241    except ImportError as exc:242        if main_name in str(exc):243            raise error("can't find %r module in %r" %244                              (main_name, sys.path[0])) from exc245        raise246    finally:247        sys.modules[main_name] = saved_main248 249 250def _get_code_from_file(fname):251    # Check for a compiled file first252    from pkgutil import read_code253    code_path = os.path.abspath(fname)254    with io.open_code(code_path) as f:255        code = read_code(f)256    if code is None:257        # That didn't work, so try it as normal source code258        with io.open_code(code_path) as f:259            code = compile(f.read(), fname, 'exec')260    return code261 262def run_path(path_name, init_globals=None, run_name=None):263    """Execute code located at the specified filesystem location.264 265       path_name -- filesystem location of a Python script, zipfile,266       or directory containing a top level __main__.py script.267 268       Optional arguments:269       init_globals -- dictionary used to pre-populate the module’s270       globals dictionary before the code is executed.271 272       run_name -- if not None, this will be used to set __name__;273       otherwise, '<run_path>' will be used for __name__.274 275       Returns the resulting module globals dictionary.276    """277    if run_name is None:278        run_name = "<run_path>"279    pkg_name = run_name.rpartition(".")[0]280    from pkgutil import get_importer281    importer = get_importer(path_name)282    path_name = os.fsdecode(path_name)283    if isinstance(importer, type(None)):284        # Not a valid sys.path entry, so run the code directly285        # execfile() doesn't help as we want to allow compiled files286        code = _get_code_from_file(path_name)287        return _run_module_code(code, init_globals, run_name,288                                pkg_name=pkg_name, script_name=path_name)289    else:290        # Finder is defined for path, so add it to291        # the start of sys.path292        sys.path.insert(0, path_name)293        try:294            # Here's where things are a little different from the run_module295            # case. There, we only had to replace the module in sys while the296            # code was running and doing so was somewhat optional. Here, we297            # have no choice and we have to remove it even while we read the298            # code. If we don't do this, a __loader__ attribute in the299            # existing __main__ module may prevent location of the new module.300            mod_name, mod_spec, code = _get_main_module_details()301            with _TempModule(run_name) as temp_module, \302                 _ModifiedArgv0(path_name):303                mod_globals = temp_module.module.__dict__304                return _run_code(code, mod_globals, init_globals,305                                    run_name, mod_spec, pkg_name).copy()306        finally:307            try:308                sys.path.remove(path_name)309            except ValueError:310                pass311 312 313if __name__ == "__main__":314    # Run the module specified as the next command line argument315    if len(sys.argv) < 2:316        print("No module specified for execution", file=sys.stderr)317    else:318        del sys.argv[0] # Make the requested module sys.argv[0]319        _run_module_as_main(sys.argv[0])320 
codekingpro/portable-devtools · Team Ai