diff options
author | nkozlovskiy <nmk@ydb.tech> | 2023-09-29 12:24:06 +0300 |
---|---|---|
committer | nkozlovskiy <nmk@ydb.tech> | 2023-09-29 12:41:34 +0300 |
commit | e0e3e1717e3d33762ce61950504f9637a6e669ed (patch) | |
tree | bca3ff6939b10ed60c3d5c12439963a1146b9711 /library/python/runtime | |
parent | 38f2c5852db84c7b4d83adfcb009eb61541d1ccd (diff) | |
download | ydb-e0e3e1717e3d33762ce61950504f9637a6e669ed.tar.gz |
add ydb deps
Diffstat (limited to 'library/python/runtime')
-rw-r--r-- | library/python/runtime/__res.pyx | 31 | ||||
-rw-r--r-- | library/python/runtime/entry_points.py | 64 | ||||
-rw-r--r-- | library/python/runtime/importer.pxi | 297 | ||||
-rw-r--r-- | library/python/runtime/main/main.c | 46 | ||||
-rw-r--r-- | library/python/runtime/main/ya.make | 15 | ||||
-rw-r--r-- | library/python/runtime/sitecustomize.pyx | 1 | ||||
-rw-r--r-- | library/python/runtime/ya.make | 32 |
7 files changed, 486 insertions, 0 deletions
diff --git a/library/python/runtime/__res.pyx b/library/python/runtime/__res.pyx new file mode 100644 index 00000000000..5e2e72abf39 --- /dev/null +++ b/library/python/runtime/__res.pyx @@ -0,0 +1,31 @@ +from libcpp cimport bool + +from util.generic.string cimport TString, TStringBuf + + +cdef extern from "library/cpp/resource/resource.h" namespace "NResource": + cdef size_t Count() except + + cdef TStringBuf KeyByIndex(size_t idx) except + + cdef bool FindExact(const TStringBuf key, TString* result) nogil except + + + +def count(): + return Count() + + +def key_by_index(idx): + cdef TStringBuf ret = KeyByIndex(idx) + + return ret.Data()[:ret.Size()] + + +def find(s): + cdef TString res + + if FindExact(TStringBuf(s, len(s)), &res): + return res.c_str()[:res.length()] + + return None + + +include "importer.pxi" diff --git a/library/python/runtime/entry_points.py b/library/python/runtime/entry_points.py new file mode 100644 index 00000000000..ca5c38a26af --- /dev/null +++ b/library/python/runtime/entry_points.py @@ -0,0 +1,64 @@ +import sys + +import __res + + +def repl(): + user_ns = {} + py_main = __res.find('PY_MAIN') + + if py_main: + py_main_split = py_main.split(':', 1) + if len(py_main_split) == 2: + mod_name, func_name = py_main_split + else: + mod_name, func_name = py_main_split[0], 'main' + + if not mod_name: + mod_name = 'library.python.runtime.entry_points' + + try: + import importlib + mod = importlib.import_module(mod_name) + user_ns = mod.__dict__ + except: # noqa E722 + import traceback + traceback.print_exc() + + if '__main__' not in user_ns: + def run(args): + if isinstance(args, basestring): + import shlex + args = shlex.split(args) + + import sys + sys.argv = [sys.argv[0]] + args + getattr(mod, func_name)() + + user_ns['__main__'] = run + else: + try: + mod = __res.importer.load_module('__main__', fix_name='__main_real') + user_ns = mod.__dict__ + except ImportError: + pass + + try: + import IPython + except ImportError: + import code + code.interact(local=user_ns) + else: + IPython.start_ipython(user_ns=user_ns) + + +def resource_files(): + sys.stdout.write('\n'.join(sorted(__res.resfs_files()) + [''])) + + +def run_constructors(): + for key, module_name in __res.iter_keys('py/constructors/'): + import importlib + module = importlib.import_module(module_name) + init_func = getattr(module, __res.find(key)) + init_func() diff --git a/library/python/runtime/importer.pxi b/library/python/runtime/importer.pxi new file mode 100644 index 00000000000..e27eaa0549e --- /dev/null +++ b/library/python/runtime/importer.pxi @@ -0,0 +1,297 @@ +import copy +import imp +import importlib +import marshal +import os +import re +import sys +import traceback +from os.path import sep as path_sep + +import __res as __resource + +env_entry_point = 'Y_PYTHON_ENTRY_POINT' +env_source_root = 'Y_PYTHON_SOURCE_ROOT' +executable = sys.executable or 'Y_PYTHON' +sys.modules['run_import_hook'] = __resource + +find_py_module = lambda mod: __resource.find('/py_modules/' + mod) +find_py_code = lambda mod: __resource.find('/py_code/' + mod) + +Y_PYTHON_SOURCE_ROOT = os.environ.get(env_source_root) +if Y_PYTHON_SOURCE_ROOT is not None: + Y_PYTHON_SOURCE_ROOT = os.path.abspath(os.path.expanduser(Y_PYTHON_SOURCE_ROOT)) + os.environ[env_source_root] = Y_PYTHON_SOURCE_ROOT + + +def file_bytes(path): + with open(path, 'rb') as f: + return f.read() + + +def iter_keys(prefix): + l = len(prefix) + for idx in xrange(__resource.count()): + key = __resource.key_by_index(idx) + if key.startswith(prefix): + yield key, key[l:] + + +def iter_py_modules(with_keys=False): + for key, mod in iter_keys('/py_modules/'): + if '/' in mod: + raise Exception('Incorrect py_modules resource: ' + repr(key)) + if with_keys: + yield key, mod + else: + yield mod + + +def iter_prefixes(s): + i = s.find('.') + while i >= 0: + yield s[:i] + i = s.find('.', i + 1) + + +def resfs_resolve(path): + """ + Return the absolute path of a root-relative path if it exists. + """ + if Y_PYTHON_SOURCE_ROOT: + abspath = os.path.join(Y_PYTHON_SOURCE_ROOT, path) + if os.path.exists(abspath): + return abspath + + +def resfs_src(key, resfs_file=False): + """ + Return the root-relative file path of a resource key. + """ + if resfs_file: + key = 'resfs/file/' + key + return __resource.find('resfs/src/' + key) + + +def resfs_read(path, builtin=None): + """ + Return the bytes of the resource file at path, or None. + If builtin is True, do not look for it on the filesystem. + If builtin is False, do not look in the builtin resources. + """ + if builtin is not True: + arcpath = resfs_src(path, resfs_file=True) + if arcpath: + fspath = resfs_resolve(arcpath) + if fspath: + return file_bytes(fspath) + + if builtin is not False: + return __resource.find('resfs/file/' + path) + + +def resfs_files(prefix=''): + """ + List builtin resource file paths. + """ + return [key[11:] for key, _ in iter_keys('resfs/file/' + prefix)] + + +class ResourceImporter(object): + + """ A meta_path importer that loads code from built-in resources. + """ + + def __init__(self): + self.memory = set(iter_py_modules()) # Set of importable module names. + self.source_map = {} # Map from file names to module names. + self._source_name = {} # Map from original to altered module names. + self._package_prefix = '' + + for p in list(self.memory) + list(sys.builtin_module_names): + for pp in iter_prefixes(p): + k = pp + '.__init__' + if k not in self.memory: + self.memory.add(k) + + def for_package(self, name): + importer = copy.copy(self) + importer._package_prefix = name + '.' + return importer + + # PEP-302 finder. + def find_module(self, fullname, path=None): + try: + self.is_package(fullname) + except ImportError: + return None + return self + + # PEP-302 extension 1 of 3: data loader. + def get_data(self, path): + abspath = resfs_resolve(path) + if abspath: + return file_bytes(abspath) + path = path.replace('\\', '/') + data = resfs_read(path, builtin=True) + if data is None: + raise IOError(path) # Y_PYTHON_ENTRY_POINT=:resource_files + return data + + # PEP-302 extension 2 of 3: get __file__ without importing. + def get_filename(self, fullname): + modname = fullname + if self.is_package(fullname): + fullname += '.__init__' + return resfs_src('/py_modules/' + fullname) or modname + + # PEP-302 extension 3 of 3: packaging introspection. + # Used by `linecache` (while printing tracebacks) unless module filename + # exists on the filesystem. + def get_source(self, fullname): + fullname = self._source_name.get(fullname, fullname) + if self.is_package(fullname): + fullname += '.__init__' + + abspath = resfs_resolve(self.get_filename(fullname)) + if abspath: + return file_bytes(abspath) + return find_py_module(fullname) + + def get_code(self, fullname): + modname = fullname + if self.is_package(fullname): + fullname += '.__init__' + + abspath = resfs_resolve(self.get_filename(fullname)) + if abspath: + data = file_bytes(abspath) + return compile(data, abspath, 'exec', dont_inherit=True) + + pyc = find_py_code(fullname) + if pyc: + return marshal.loads(pyc) + else: + # This covers packages with no __init__.py in resources. + return compile('', modname, 'exec', dont_inherit=True) + + def is_package(self, fullname): + if fullname in self.memory: + return False + + if fullname + '.__init__' in self.memory: + return True + + raise ImportError(fullname) + + # Extension for contrib/python/coverage. + def file_source(self, filename): + """ + Return the key of the module source by its resource path. + """ + if not self.source_map: + for key, mod in iter_py_modules(with_keys=True): + path = self.get_filename(mod) + self.source_map[path] = key + + if filename in self.source_map: + return self.source_map[filename] + + if resfs_read(filename, builtin=True) is not None: + return 'resfs/file/' + filename + + return '' + + # Extension for pkgutil.iter_modules. + def iter_modules(self, prefix=''): + rx = re.compile(re.escape(self._package_prefix) + r'([^.]+)(\.__init__)?$') + for p in self.memory: + m = rx.match(p) + if m: + yield prefix + m.group(1), m.group(2) is not None + + # PEP-302 loader. + def load_module(self, mod_name, fix_name=None): + code = self.get_code(mod_name) + is_package = self.is_package(mod_name) + source_name = self._source_name + + mod = imp.new_module(mod_name) + mod.__loader__ = self + mod.__file__ = code.co_filename + + if is_package: + mod.__path__ = [executable + path_sep + mod_name.replace('.', path_sep)] + mod.__package__ = mod_name + else: + mod.__package__ = mod_name.rpartition('.')[0] + + if fix_name: + mod.__name__ = fix_name + self._source_name = dict(source_name, **{fix_name: mod_name}) + + old_mod = sys.modules.get(mod_name, None) + sys.modules[mod_name] = mod + + try: + exec code in mod.__dict__ + old_mod = sys.modules[mod_name] + finally: + sys.modules[mod_name] = old_mod + + # Some hacky modules (e.g. pygments.lexers) replace themselves in + # `sys.modules` with proxies. + return sys.modules[mod_name] + + def run_main(self): + entry_point = os.environ.pop(env_entry_point, None) + + if entry_point is None: + entry_point = __resource.find('PY_MAIN') + + if entry_point is None: + entry_point = '__main__' + + if ':' in entry_point: + mod_name, func_name = entry_point.split(':', 1) + if mod_name == '': + mod_name = 'library.python.runtime.entry_points' + mod = importlib.import_module(mod_name) + func = getattr(mod, func_name) + return func() + + if entry_point not in self.memory: + raise Exception(entry_point + ' not found') + + self.load_module(entry_point, fix_name='__main__') + + +importer = ResourceImporter() +sys.meta_path.insert(0, importer) + + +def executable_path_hook(path): + if path == executable: + return importer + + if path.startswith(executable + path_sep): + return importer.for_package(path[len(executable + path_sep):].replace(path_sep, '.')) + + raise ImportError(path) + + +if executable not in sys.path: + sys.path.insert(0, executable) +sys.path_hooks.insert(0, executable_path_hook) +sys.path_importer_cache[executable] = importer + +# Indicator that modules and resources are built-in rather than on the file system. +sys.is_standalone_binary = True +sys.frozen = True + +# Set of names of importable modules. +sys.extra_modules = importer.memory + +# Use pure-python implementation of traceback printer. +# Built-in printer (PyTraceBack_Print) does not support custom module loaders +sys.excepthook = traceback.print_exception diff --git a/library/python/runtime/main/main.c b/library/python/runtime/main/main.c new file mode 100644 index 00000000000..69dd80e5583 --- /dev/null +++ b/library/python/runtime/main/main.c @@ -0,0 +1,46 @@ +#include <Python.h> + +#include <stdlib.h> +#include <string.h> + +void Py_InitArgcArgv(int argc, char** argv); + +static const char* env_entry_point = "Y_PYTHON_ENTRY_POINT"; + +#ifdef _MSC_VER +extern char** environ; + +void unsetenv(const char* name) { + const int n = strlen(name); + char** dst = environ; + for (char** src = environ; *src; src++) + if (strncmp(*src, name, n) || (*src)[n] != '=') + *dst++ = *src; + *dst = NULL; +} +#endif + +static int pymain(int argc, char** argv) { + const char* entry_point = getenv(env_entry_point); + if (entry_point && !strcmp(entry_point, ":main")) { + unsetenv(env_entry_point); + return Py_Main(argc, argv); + } + Py_InitArgcArgv(argc, argv); + Py_SetProgramName(argv[0]); + Py_Initialize(); + PySys_SetArgv(argc, argv); + int rc = PyRun_SimpleString( + "from library.python.runtime import entry_points\n" + "entry_points.run_constructors()\n" + "import __res\n" + "__res.importer.run_main()\n"); + Py_Finalize(); + return rc == 0 ? 0 : 1; +} + +int (*mainptr)(int argc, char** argv) = pymain; + +int main(int argc, char** argv) { + return mainptr(argc, argv); +} diff --git a/library/python/runtime/main/ya.make b/library/python/runtime/main/ya.make new file mode 100644 index 00000000000..245ffe680de --- /dev/null +++ b/library/python/runtime/main/ya.make @@ -0,0 +1,15 @@ +LIBRARY() + +PEERDIR( + contrib/tools/python/base +) + +ADDINCL( + contrib/tools/python/src/Include +) + +SRCS( + main.c +) + +END() diff --git a/library/python/runtime/sitecustomize.pyx b/library/python/runtime/sitecustomize.pyx new file mode 100644 index 00000000000..03fdf36085b --- /dev/null +++ b/library/python/runtime/sitecustomize.pyx @@ -0,0 +1 @@ +import __res diff --git a/library/python/runtime/ya.make b/library/python/runtime/ya.make new file mode 100644 index 00000000000..30147d241d3 --- /dev/null +++ b/library/python/runtime/ya.make @@ -0,0 +1,32 @@ +PY2_LIBRARY() + +NO_WSHADOW() + +PEERDIR( + contrib/tools/python/lib + library/cpp/resource +) + +CFLAGS(-DCYTHON_REGISTER_ABCS=0) + +NO_PYTHON_INCLUDES() + +PY_SRCS( + entry_points.py + TOP_LEVEL + __res.pyx + sitecustomize.pyx +) + +IF (CYTHON_COVERAGE) + # Let covarage support add all needed files to resources +ELSE() + RESOURCE_FILES( + PREFIX ${MODDIR}/ + __res.pyx + importer.pxi + sitecustomize.pyx + ) +ENDIF() + +END() |