Inside python interpreter === ###### tags:`python` :::info Python 3.5 ::: # default builtins sys.modules is a dictionary mapping the names of imported modules to the module object holding the code ```python import sys dir(sys.modules['builtins']) == dir(__builtins__) ``` True ```python sorted(sys.modules.keys()) ``` ['IPython', 'IPython.core', 'IPython.core.alias', 'IPython.core.application', 'IPython.core.autocall', 'IPython.core.builtin_trap', 'IPython.core.compilerop', 'IPython.core.completer', 'IPython.core.completerlib', 'IPython.core.crashhandler', 'IPython.core.debugger', 'IPython.core.display', 'IPython.core.display_trap', 'IPython.core.displayhook', 'IPython.core.displaypub', 'IPython.core.error', 'IPython.core.events', 'IPython.core.excolors', 'IPython.core.extensions', 'IPython.core.formatters', 'IPython.core.getipython', 'IPython.core.history', 'IPython.core.hooks', 'IPython.core.inputsplitter', 'IPython.core.inputtransformer', 'IPython.core.interactiveshell', 'IPython.core.latex_symbols', 'IPython.core.logger', 'IPython.core.macro', 'IPython.core.magic', 'IPython.core.magic_arguments', 'IPython.core.magics', 'IPython.core.magics.auto', 'IPython.core.magics.basic', 'IPython.core.magics.code', 'IPython.core.magics.config', 'IPython.core.magics.display', 'IPython.core.magics.execution', 'IPython.core.magics.extension', 'IPython.core.magics.history', 'IPython.core.magics.logging', 'IPython.core.magics.namespace', 'IPython.core.magics.osm', 'IPython.core.magics.pylab', 'IPython.core.magics.script', 'IPython.core.oinspect', 'IPython.core.page', 'IPython.core.payload', 'IPython.core.payloadpage', 'IPython.core.prefilter', 'IPython.core.profiledir', 'IPython.core.pylabtools', 'IPython.core.release', 'IPython.core.shellapp', 'IPython.core.splitinput', 'IPython.core.ultratb', 'IPython.core.usage', 'IPython.display', 'IPython.extensions', 'IPython.extensions.storemagic', 'IPython.lib', 'IPython.lib.backgroundjobs', 'IPython.lib.clipboard', 'IPython.lib.display', 'IPython.lib.pretty', 'IPython.lib.security', 'IPython.paths', 'IPython.terminal', 'IPython.terminal.debugger', 'IPython.terminal.embed', 'IPython.terminal.interactiveshell', 'IPython.terminal.ipapp', 'IPython.terminal.magics', 'IPython.terminal.prompts', 'IPython.terminal.pt_inputhooks', 'IPython.terminal.ptutils', 'IPython.terminal.shortcuts', 'IPython.testing', 'IPython.testing.skipdoctest', 'IPython.utils', 'IPython.utils.PyColorize', 'IPython.utils._process_common', 'IPython.utils._process_posix', 'IPython.utils._sysinfo', 'IPython.utils.capture', 'IPython.utils.colorable', 'IPython.utils.coloransi', 'IPython.utils.contexts', 'IPython.utils.data', 'IPython.utils.decorators', 'IPython.utils.dir2', 'IPython.utils.encoding', 'IPython.utils.frame', 'IPython.utils.generics', 'IPython.utils.importstring', 'IPython.utils.io', 'IPython.utils.ipstruct', 'IPython.utils.module_paths', 'IPython.utils.openpy', 'IPython.utils.path', 'IPython.utils.process', 'IPython.utils.py3compat', 'IPython.utils.sentinel', 'IPython.utils.strdispatch', 'IPython.utils.sysinfo', 'IPython.utils.syspathcontext', 'IPython.utils.tempdir', 'IPython.utils.terminal', 'IPython.utils.text', 'IPython.utils.timing', 'IPython.utils.tokenize2', 'IPython.utils.tokenutil', 'IPython.utils.wildcard', '__future__', '__main__', '__mp_main__', '_ast', '_bisect', '_bootlocale', '_bz2', '_codecs', '_collections', '_collections_abc', '_compat_pickle', '_compression', '_ctypes', '_curses', '_cython_0_25_1', '_datetime', '_decimal', '_frozen_importlib', '_frozen_importlib_external', '_functools', '_hashlib', '_heapq', '_imp', '_io', '_json', '_locale', '_lsprof', '_lzma', '_multiprocessing', '_opcode', '_operator', '_pickle', '_posixsubprocess', '_random', '_signal', '_sitebuiltins', '_socket', '_sqlite3', '_sre', '_stat', '_string', '_strptime', '_struct', '_sysconfigdata', '_sysconfigdata_m', '_thread', '_warnings', '_weakref', '_weakrefset', 'abc', 'apport_python_hook', 'argparse', 'array', 'ast', 'atexit', 'base64', 'bdb', 'binascii', 'bisect', 'builtins', 'bz2', 'cProfile', 'calendar', 'cmd', 'code', 'codecs', 'codeop', 'collections', 'collections.abc', 'concurrent', 'concurrent.futures', 'concurrent.futures._base', 'concurrent.futures.process', 'concurrent.futures.thread', 'contextlib', 'copy', 'copyreg', 'ctypes', 'ctypes._endian', 'ctypes.util', 'curses', 'datetime', 'dateutil', 'dateutil._common', 'dateutil.parser', 'dateutil.relativedelta', 'dateutil.tz', 'dateutil.tz._common', 'dateutil.tz.tz', 'decimal', 'decorator', 'difflib', 'dis', 'email', 'email._encoded_words', 'email._parseaddr', 'email._policybase', 'email.base64mime', 'email.charset', 'email.encoders', 'email.errors', 'email.feedparser', 'email.header', 'email.iterators', 'email.message', 'email.parser', 'email.quoprimime', 'email.utils', 'encodings', 'encodings.aliases', 'encodings.latin_1', 'encodings.utf_8', 'enum', 'errno', 'faulthandler', 'fcntl', 'fnmatch', 'functools', 'gc', 'genericpath', 'getopt', 'getpass', 'gettext', 'glob', 'grp', 'hashlib', 'heapq', 'hmac', 'html', 'html.entities', 'imp', 'importlib', 'importlib._bootstrap', 'importlib._bootstrap_external', 'importlib.abc', 'importlib.machinery', 'importlib.util', 'inspect', 'io', 'ipykernel', 'ipykernel._version', 'ipykernel.codeutil', 'ipykernel.comm', 'ipykernel.comm.comm', 'ipykernel.comm.manager', 'ipykernel.connect', 'ipykernel.datapub', 'ipykernel.displayhook', 'ipykernel.heartbeat', 'ipykernel.iostream', 'ipykernel.ipkernel', 'ipykernel.jsonutil', 'ipykernel.kernelapp', 'ipykernel.kernelbase', 'ipykernel.parentpoller', 'ipykernel.pickleutil', 'ipykernel.serialize', 'ipykernel.zmqshell', 'ipython_genutils', 'ipython_genutils._version', 'ipython_genutils.encoding', 'ipython_genutils.importstring', 'ipython_genutils.path', 'ipython_genutils.py3compat', 'ipython_genutils.text', 'ipywidgets', 'ipywidgets._version', 'ipywidgets.widgets', 'ipywidgets.widgets.domwidget', 'ipywidgets.widgets.interaction', 'ipywidgets.widgets.trait_types', 'ipywidgets.widgets.valuewidget', 'ipywidgets.widgets.widget', 'ipywidgets.widgets.widget_bool', 'ipywidgets.widgets.widget_box', 'ipywidgets.widgets.widget_button', 'ipywidgets.widgets.widget_color', 'ipywidgets.widgets.widget_controller', 'ipywidgets.widgets.widget_core', 'ipywidgets.widgets.widget_date', 'ipywidgets.widgets.widget_float', 'ipywidgets.widgets.widget_image', 'ipywidgets.widgets.widget_int', 'ipywidgets.widgets.widget_layout', 'ipywidgets.widgets.widget_link', 'ipywidgets.widgets.widget_output', 'ipywidgets.widgets.widget_selection', 'ipywidgets.widgets.widget_selectioncontainer', 'ipywidgets.widgets.widget_string', 'ipywidgets.widgets.widget_style', 'itertools', 'jedi', 'jedi._compatibility', 'jedi.api', 'jedi.api.classes', 'jedi.api.completion', 'jedi.api.helpers', 'jedi.api.interpreter', 'jedi.api.keywords', 'jedi.api.usages', 'jedi.cache', 'jedi.common', 'jedi.debug', 'jedi.evaluate', 'jedi.evaluate.analysis', 'jedi.evaluate.cache', 'jedi.evaluate.compiled', 'jedi.evaluate.compiled.fake', 'jedi.evaluate.compiled.mixed', 'jedi.evaluate.context', 'jedi.evaluate.docstrings', 'jedi.evaluate.dynamic', 'jedi.evaluate.filters', 'jedi.evaluate.finder', 'jedi.evaluate.flow_analysis', 'jedi.evaluate.helpers', 'jedi.evaluate.imports', 'jedi.evaluate.instance', 'jedi.evaluate.iterable', 'jedi.evaluate.param', 'jedi.evaluate.pep0484', 'jedi.evaluate.precedence', 'jedi.evaluate.recursion', 'jedi.evaluate.representation', 'jedi.evaluate.site', 'jedi.evaluate.stdlib', 'jedi.evaluate.sys_path', 'jedi.parser', 'jedi.parser.cache', 'jedi.parser.parser', 'jedi.parser.pgen2', 'jedi.parser.pgen2.grammar', 'jedi.parser.pgen2.parse', 'jedi.parser.pgen2.pgen', 'jedi.parser.python', 'jedi.parser.python.diff', 'jedi.parser.python.parser', 'jedi.parser.python.tree', 'jedi.parser.token', 'jedi.parser.tokenize', 'jedi.parser.tree', 'jedi.settings', 'json', 'json.decoder', 'json.encoder', 'json.scanner', 'jupyter_client', 'jupyter_client._version', 'jupyter_client.adapter', 'jupyter_client.blocking', 'jupyter_client.blocking.channels', 'jupyter_client.blocking.client', 'jupyter_client.channels', 'jupyter_client.channelsabc', 'jupyter_client.client', 'jupyter_client.clientabc', 'jupyter_client.connect', 'jupyter_client.jsonutil', 'jupyter_client.kernelspec', 'jupyter_client.launcher', 'jupyter_client.localinterfaces', 'jupyter_client.manager', 'jupyter_client.managerabc', 'jupyter_client.multikernelmanager', 'jupyter_client.session', 'jupyter_core', 'jupyter_core.paths', 'jupyter_core.version', 'keyrings', 'keyword', 'linecache', 'locale', 'logging', 'logging.handlers', 'lzma', 'marshal', 'math', 'mimetypes', 'multiprocessing', 'multiprocessing.connection', 'multiprocessing.context', 'multiprocessing.process', 'multiprocessing.reduction', 'multiprocessing.util', 'ntpath', 'numbers', 'opcode', 'operator', 'optparse', 'os', 'os.path', 'pathlib', 'pdb', 'pexpect', 'pexpect.exceptions', 'pexpect.expect', 'pexpect.pty_spawn', 'pexpect.run', 'pexpect.spawnbase', 'pexpect.utils', 'pickle', 'pickleshare', 'pkg_resources', 'pkg_resources._vendor', 'pkg_resources._vendor.packaging.__about__', 'pkg_resources._vendor.six', 'pkg_resources._vendor.six.moves', 'pkg_resources.extern', 'pkg_resources.extern.appdirs', 'pkg_resources.extern.packaging', 'pkg_resources.extern.packaging._compat', 'pkg_resources.extern.packaging._structures', 'pkg_resources.extern.packaging.markers', 'pkg_resources.extern.packaging.requirements', 'pkg_resources.extern.packaging.specifiers', 'pkg_resources.extern.packaging.version', 'pkg_resources.extern.pyparsing', 'pkg_resources.extern.six', 'pkg_resources.extern.six.moves', 'pkg_resources.extern.six.moves.urllib', 'pkgutil', 'platform', 'plistlib', 'posix', 'posixpath', 'pprint', 'profile', 'prompt_toolkit', 'prompt_toolkit.application', 'prompt_toolkit.auto_suggest', 'prompt_toolkit.buffer', 'prompt_toolkit.buffer_mapping', 'prompt_toolkit.cache', 'prompt_toolkit.clipboard', 'prompt_toolkit.clipboard.base', 'prompt_toolkit.clipboard.in_memory', 'prompt_toolkit.completion', 'prompt_toolkit.document', 'prompt_toolkit.enums', 'prompt_toolkit.eventloop', 'prompt_toolkit.eventloop.base', 'prompt_toolkit.eventloop.callbacks', 'prompt_toolkit.filters', 'prompt_toolkit.filters.base', 'prompt_toolkit.filters.cli', 'prompt_toolkit.filters.types', 'prompt_toolkit.filters.utils', 'prompt_toolkit.history', 'prompt_toolkit.input', 'prompt_toolkit.interface', 'prompt_toolkit.key_binding', 'prompt_toolkit.key_binding.bindings', 'prompt_toolkit.key_binding.bindings.basic', 'prompt_toolkit.key_binding.bindings.completion', 'prompt_toolkit.key_binding.bindings.emacs', 'prompt_toolkit.key_binding.bindings.named_commands', 'prompt_toolkit.key_binding.bindings.scroll', 'prompt_toolkit.key_binding.bindings.vi', 'prompt_toolkit.key_binding.defaults', 'prompt_toolkit.key_binding.digraphs', 'prompt_toolkit.key_binding.input_processor', 'prompt_toolkit.key_binding.manager', 'prompt_toolkit.key_binding.registry', 'prompt_toolkit.key_binding.vi_state', 'prompt_toolkit.keys', 'prompt_toolkit.layout', 'prompt_toolkit.layout.containers', 'prompt_toolkit.layout.controls', 'prompt_toolkit.layout.dimension', 'prompt_toolkit.layout.lexers', 'prompt_toolkit.layout.margins', 'prompt_toolkit.layout.menus', 'prompt_toolkit.layout.mouse_handlers', 'prompt_toolkit.layout.processors', 'prompt_toolkit.layout.prompt', 'prompt_toolkit.layout.screen', 'prompt_toolkit.layout.toolbars', 'prompt_toolkit.layout.utils', 'prompt_toolkit.mouse_events', 'prompt_toolkit.output', 'prompt_toolkit.reactive', 'prompt_toolkit.renderer', 'prompt_toolkit.search_state', 'prompt_toolkit.selection', 'prompt_toolkit.shortcuts', 'prompt_toolkit.styles', 'prompt_toolkit.styles.base', 'prompt_toolkit.styles.defaults', 'prompt_toolkit.styles.from_dict', 'prompt_toolkit.styles.from_pygments', 'prompt_toolkit.styles.utils', 'prompt_toolkit.terminal', 'prompt_toolkit.terminal.vt100_input', 'prompt_toolkit.terminal.vt100_output', 'prompt_toolkit.token', 'prompt_toolkit.utils', 'prompt_toolkit.validation', 'pstats', 'pty', 'ptyprocess', 'ptyprocess.ptyprocess', 'ptyprocess.util', 'pwd', 'pydoc', 'pydoc_data', 'pydoc_data.topics', 'pyexpat', 'pyexpat.errors', 'pyexpat.model', 'pygments', 'pygments.filter', 'pygments.filters', 'pygments.formatter', 'pygments.formatters', 'pygments.formatters._mapping', 'pygments.formatters.html', 'pygments.lexer', 'pygments.lexers', 'pygments.lexers._mapping', 'pygments.lexers.python', 'pygments.modeline', 'pygments.plugin', 'pygments.regexopt', 'pygments.style', 'pygments.styles', 'pygments.styles.default', 'pygments.token', 'pygments.unistring', 'pygments.util', 'queue', 'quopri', 'random', 're', 'reprlib', 'resource', 'runpy', 'select', 'selectors', 'shlex', 'shutil', 'signal', 'simplegeneric', 'simplejson', 'simplejson._speedups', 'simplejson.compat', 'simplejson.decoder', 'simplejson.encoder', 'simplejson.scanner', 'site', 'sitecustomize', 'six', 'six.moves', 'socket', 'sqlite3', 'sqlite3.dbapi2', 'sre_compile', 'sre_constants', 'sre_parse', 'stat', 'storemagic', 'string', 'struct', 'subprocess', 'sys', 'sysconfig', 'tempfile', 'termios', 'textwrap', 'threading', 'time', 'timeit', 'token', 'tokenize', 'tornado', 'tornado.concurrent', 'tornado.escape', 'tornado.ioloop', 'tornado.log', 'tornado.platform', 'tornado.platform.auto', 'tornado.platform.common', 'tornado.platform.interface', 'tornado.platform.posix', 'tornado.speedups', 'tornado.stack_context', 'tornado.util', 'traceback', 'traitlets', 'traitlets._version', 'traitlets.config', 'traitlets.config.application', 'traitlets.config.configurable', 'traitlets.config.loader', 'traitlets.log', 'traitlets.traitlets', 'traitlets.utils', 'traitlets.utils.bunch', 'traitlets.utils.getargspec', 'traitlets.utils.importstring', 'traitlets.utils.sentinel', 'tty', 'types', 'typing', 'typing.io', 'typing.re', 'unicodedata', 'urllib', 'urllib.parse', 'uu', 'uuid', 'warnings', 'wcwidth', 'wcwidth.table_wide', 'wcwidth.table_zero', 'wcwidth.wcwidth', 'weakref', 'xml', 'xml.parsers', 'xml.parsers.expat', 'xml.parsers.expat.errors', 'xml.parsers.expat.model', 'zipfile', 'zipimport', 'zlib', 'zmq', 'zmq.backend', 'zmq.backend.cython', 'zmq.backend.cython._device', 'zmq.backend.cython._poll', 'zmq.backend.cython._version', 'zmq.backend.cython.constants', 'zmq.backend.cython.context', 'zmq.backend.cython.error', 'zmq.backend.cython.message', 'zmq.backend.cython.socket', 'zmq.backend.cython.utils', 'zmq.backend.select', 'zmq.error', 'zmq.eventloop', 'zmq.eventloop.ioloop', 'zmq.eventloop.zmqstream', 'zmq.sugar', 'zmq.sugar.attrsettr', 'zmq.sugar.constants', 'zmq.sugar.context', 'zmq.sugar.frame', 'zmq.sugar.poll', 'zmq.sugar.socket', 'zmq.sugar.stopwatch', 'zmq.sugar.tracker', 'zmq.sugar.version', 'zmq.utils', 'zmq.utils.constant_names', 'zmq.utils.jsonapi', 'zmq.utils.strtypes'] ## Built-in Modules ```python for name in sys.builtin_module_names: print(name) ``` _ast _bisect _codecs _collections _datetime _elementtree _functools _heapq _imp _io _locale _md5 _operator _pickle _posixsubprocess _random _sha1 _sha256 _sha512 _signal _socket _sre _stat _string _struct _symtable _thread _tracemalloc _warnings _weakref array atexit binascii builtins errno faulthandler fcntl gc grp itertools marshal math posix pwd pyexpat select spwd sys syslog time unicodedata xxsubtype zipimport zlib ## Import Path ```python import sys for d in sys.path: print(d) ``` /usr/lib/python35.zip /usr/lib/python3.5 /usr/lib/python3.5/plat-x86_64-linux-gnu /usr/lib/python3.5/lib-dynload /home/splasky/.local/lib/python3.5/site-packages /usr/local/lib/python3.5/dist-packages /usr/lib/python3/dist-packages /usr/local/lib/python3.5/dist-packages/IPython/extensions /home/splasky/.ipython ```python %%writefile example.py DATA='a' ``` Writing example.py ```python import example print(example.__file__) print(example.DATA) ``` /home/splasky/jupyter-notebook/example.py a > Changing the path between the initial import and the call to `reload()` means a different module may be loaded the second time. # Import hook ```python import sys class NoisyImportFinder(object): PATH_TRIGGER = 'NoisyImportFinder_PATH_TRIGGER' def __init__(self, path_entry): print ('Checking NoisyImportFinder support for %s' % path_entry) if path_entry != self.PATH_TRIGGER: print ('NoisyImportFinder does not work for %s' % path_entry) raise ImportError() return def find_module(self, fullname, path=None): print ('NoisyImportFinder looking for "%s"' % fullname) return None sys.path_hooks.append(NoisyImportFinder) sys.path.insert(0, NoisyImportFinder.PATH_TRIGGER) try: import target_module except Exception as e: print ('Import failed:', e) ``` Checking NoisyImportFinder support for NoisyImportFinder_PATH_TRIGGER NoisyImportFinder looking for "target_module" Import failed: No module named 'target_module' # inside the main ```python dir(sys.modules['__main__']) ``` ['In', 'NamespaceMagics', 'NoisyImportFinder', 'Out', '_', '_10', '_7', '_Jupyter', '__', '___', '__builtin__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', '_dh', '_getsizeof', '_i', '_i1', '_i10', '_i11', '_i12', '_i13', '_i14', '_i15', '_i16', '_i17', '_i18', '_i19', '_i2', '_i20', '_i21', '_i22', '_i23', '_i24', '_i3', '_i4', '_i5', '_i6', '_i7', '_i8', '_i9', '_ih', '_ii', '_iii', '_nms', '_oh', 'd', 'dis', 'example', 'exit', 'get_ipython', 'getsizeof', 'json', 'name', 'oo', 'pprint', 'print_function', 'quit', 'sys', 'var_dic_list'] ```python from IPython.core.interactiveshell import InteractiveShell InteractiveShell.ast_node_interactivity = "all" ``` # How python import work? ```python import dis ``` ```python dis.dis("import sys") ``` 1 0 LOAD_CONST 0 (0) 3 LOAD_CONST 1 (None) 6 IMPORT_NAME 0 (sys) 9 STORE_NAME 0 (sys) 12 LOAD_CONST 1 (None) 15 RETURN_VALUE ```python dis.dis("import os") ``` 1 0 LOAD_CONST 0 (0) 3 LOAD_CONST 1 (None) 6 IMPORT_NAME 0 (os) 9 STORE_NAME 0 (os) 12 LOAD_CONST 1 (None) 15 RETURN_VALUE ```python # import module dynamically sys = __import__('sys') print(sys) ``` <module 'sys' (built-in)> ```python oo = __import__('os') print(oo) ``` <module 'os' from '/usr/lib/python3.5/os.py'> ```python %%writefile c_modules.c #include <Python.h> static PyObject* helloworld(PyObject* self) { return Py_BuildValue("s", "Hello, Python extensions!!"); } static char helloworld_docs[] = "helloworld( ): Any message you want to put here!!\n"; static PyMethodDef helloworld_funcs[] = { {"helloworld", helloworld, METH_NOARGS, helloworld_docs}, {NULL, NULL, 0, NULL} }; static struct PyModuleDef helloworldModule = { PyModuleDef_HEAD_INIT, "hello", /* name of module */ helloworld_docs, /* module documentation, may be NULL */ -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ helloworld_funcs }; PyMODINIT_FUNC PyInit_helloworld(void) { return PyModule_Create(&helloworldModule); } ``` Overwriting c_modules.c ```python %%writefile setup.py from distutils.core import setup, Extension setup(name='helloworld', version='1.0', \ ext_modules=[Extension('helloworld', ['c_modules.c'])]) ``` Overwriting setup.py python3 ./setup.py install ## look inside how c module import into python ```python # dis_import.py import helloworld helloworld.helloworld() ``` dis assembly the module ``` python -m dis dis_import.py 6 0 LOAD_CONST 0 (0) 2 LOAD_CONST 1 (None) 4 IMPORT_NAME 0 (helloworld) 6 STORE_NAME 0 (helloworld) 8 8 LOAD_NAME 0 (helloworld) 10 LOAD_METHOD 0 (helloworld) 12 CALL_METHOD 0 14 POP_TOP 16 LOAD_CONST 1 (None) 18 RETURN_VALUE ``` as same as like import python module ## take more look inside source code IMPORT_NAME: ```c TARGET(IMPORT_NAME) { ¦ ¦ PyObject *name = GETITEM(names, oparg); ¦ ¦ PyObject *fromlist = POP(); ¦ ¦ PyObject *level = TOP(); ¦ ¦ PyObject *res; ¦ ¦ res = import_name(f, name, fromlist, level); ¦ ¦ Py_DECREF(level); ¦ ¦ Py_DECREF(fromlist); ¦ ¦ SET_TOP(res); ¦ ¦ if (res == NULL) ¦ ¦ ¦ goto error; ¦ ¦ DISPATCH(); ¦ } ... static PyObject * import_name(PyFrameObject *f, PyObject *name, PyObject *fromlist, PyObject *level) { _Py_IDENTIFIER(__import__); PyObject *import_func, *res; PyObject* stack[5]; import_func = _PyDict_GetItemId(f->f_builtins, &PyId___import__); if (import_func == NULL) { ¦ PyErr_SetString(PyExc_ImportError, "__import__ not found"); ¦ return NULL; } /* Fast path for not overloaded __import__. */ if (import_func == PyThreadState_GET()->interp->import_func) { ¦ int ilevel = _PyLong_AsInt(level); ¦ if (ilevel == -1 && PyErr_Occurred()) { ¦ ¦ return NULL; ¦ } ¦ res = PyImport_ImportModuleLevelObject( ¦ ¦ ¦ ¦ ¦ name, ¦ ¦ ¦ ¦ ¦ f->f_globals, ¦ ¦ ¦ ¦ ¦ f->f_locals == NULL ? Py_None : f->f_locals, ¦ ¦ ¦ ¦ ¦ fromlist, ¦ ¦ ¦ ¦ ¦ ilevel); ¦ return res; } Py_INCREF(import_func); stack[0] = name; stack[1] = f->f_globals; stack[2] = f->f_locals == NULL ? Py_None : f->f_locals; stack[3] = fromlist; stack[4] = level; res = _PyObject_FastCall(import_func, stack, 5); Py_DECREF(import_func); return res; } PyObject * PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦PyObject *locals, PyObject *fromlist, ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦int level) { _Py_IDENTIFIER(_find_and_load); _Py_IDENTIFIER(_handle_fromlist); PyObject *abs_name = NULL; PyObject *final_mod = NULL; PyObject *mod = NULL; PyObject *package = NULL; PyInterpreterState *interp = PyThreadState_GET()->interp; int has_from; if (name == NULL) { ¦ PyErr_SetString(PyExc_ValueError, "Empty module name"); ¦ goto error; } /* The below code is importlib.__import__() & _gcd_import(), ported to C ¦ for added performance. */ if (!PyUnicode_Check(name)) { ¦ PyErr_SetString(PyExc_TypeError, "module name must be a string"); ¦ goto error; } if (PyUnicode_READY(name) < 0) { ¦ goto error; } if (level < 0) { ¦ PyErr_SetString(PyExc_ValueError, "level must be >= 0"); ¦ goto error; } if (level > 0) { ¦ abs_name = resolve_name(name, globals, level); ¦ if (abs_name == NULL) ¦ ¦ goto error; } else { /* level == 0 */ ¦ if (PyUnicode_GET_LENGTH(name) == 0) { ¦ ¦ PyErr_SetString(PyExc_ValueError, "Empty module name"); ¦ ¦ goto error; ¦ } ¦ abs_name = name; ¦ Py_INCREF(abs_name); } mod = PyImport_GetModule(abs_name); if (mod != NULL && mod != Py_None) { ¦ _Py_IDENTIFIER(__spec__); ¦ _Py_IDENTIFIER(_initializing); ¦ _Py_IDENTIFIER(_lock_unlock_module); ¦ PyObject *value = NULL; ¦ PyObject *spec; ¦ int initializing = 0; ¦ /* Optimization: only call _bootstrap._lock_unlock_module() if ¦ ¦ __spec__._initializing is true. ¦ ¦ NOTE: because of this, initializing must be set *before* ¦ ¦ stuffing the new module in sys.modules. ¦ ¦*/ ¦ spec = _PyObject_GetAttrId(mod, &PyId___spec__); ¦ if (spec != NULL) { ¦ ¦ value = _PyObject_GetAttrId(spec, &PyId__initializing); ¦ ¦ Py_DECREF(spec); ¦ } ¦ if (value == NULL) ¦ ¦ PyErr_Clear(); ¦ else { ¦ ¦ initializing = PyObject_IsTrue(value); ¦ ¦ Py_DECREF(value); ¦ ¦ if (initializing == -1) ¦ ¦ ¦ PyErr_Clear(); ¦ ¦ if (initializing > 0) { ¦ ¦ ¦ value = _PyObject_CallMethodIdObjArgs(interp->importlib, ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ &PyId__lock_unlock_module, abs_name, ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ NULL); ¦ if (value == NULL) ¦ ¦ ¦ ¦ goto error; ¦ ¦ ¦ Py_DECREF(value); ¦ ¦ } ¦ } } else { ¦ static int ximporttime = 0; ¦ static int import_level; ¦ static _PyTime_t accumulated; ¦ _Py_IDENTIFIER(importtime); ¦ _PyTime_t t1 = 0, accumulated_copy = accumulated; ¦ Py_XDECREF(mod); ¦ /* XOptions is initialized after first some imports. ¦ ¦* So we can't have negative cache. ¦ ¦* Anyway, importlib.__find_and_load is much slower than ¦ ¦* _PyDict_GetItemId() ¦ ¦*/ ¦ if (ximporttime == 0) { ¦ ¦ PyObject *xoptions = PySys_GetXOptions(); ¦ ¦ if (xoptions) { ¦ ¦ ¦ PyObject *value = _PyDict_GetItemId(xoptions, &PyId_importtime); ¦ ¦ ¦ ximporttime = (value == Py_True); ¦ ¦ } ¦ ¦ if (ximporttime) { ¦ ¦ ¦ fputs("import time: self [us] | cumulative | imported package\n", ¦ ¦ ¦ ¦ ¦ stderr); ¦ ¦ } ¦ } ¦ if (ximporttime) { ¦ ¦ import_level++; ¦ ¦ t1 = _PyTime_GetMonotonicClock(); ¦ ¦ accumulated = 0; ¦ } ¦ if (PyDTrace_IMPORT_FIND_LOAD_START_ENABLED()) ¦ ¦ PyDTrace_IMPORT_FIND_LOAD_START(PyUnicode_AsUTF8(abs_name)); ¦ mod = _PyObject_CallMethodIdObjArgs(interp->importlib, ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ &PyId__find_and_load, abs_name, ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ interp->import_func, NULL); ¦ if (PyDTrace_IMPORT_FIND_LOAD_DONE_ENABLED()) ¦ ¦ PyDTrace_IMPORT_FIND_LOAD_DONE(PyUnicode_AsUTF8(abs_name), ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ mod != NULL); ¦ if (ximporttime) { ¦ ¦ _PyTime_t cum = _PyTime_GetMonotonicClock() - t1; ¦ ¦ import_level--; ¦ ¦ fprintf(stderr, "import time: %9ld | %10ld | %*s%s\n", ¦ ¦ ¦ ¦ (long)_PyTime_AsMicroseconds(cum - accumulated, _PyTime_ROUND_CEILING), ¦ ¦ ¦ ¦ (long)_PyTime_AsMicroseconds(cum, _PyTime_ROUND_CEILING), ¦ ¦ ¦ ¦ import_level*2, "", PyUnicode_AsUTF8(abs_name)); ¦ ¦ accumulated = accumulated_copy + cum; ¦ } ¦ if (mod == NULL) { ¦ ¦ goto error; ¦ } } has_from = 0; if (fromlist != NULL && fromlist != Py_None) { ¦ has_from = PyObject_IsTrue(fromlist); ¦ if (has_from < 0) ¦ ¦ goto error; } if (!has_from) { ¦ Py_ssize_t len = PyUnicode_GET_LENGTH(name); ¦ if (level == 0 || len > 0) { ¦ ¦ Py_ssize_t dot; ¦ dot = PyUnicode_FindChar(name, '.', 0, len, 1); ¦ ¦ if (dot == -2) { ¦ ¦ ¦ goto error; ¦ ¦ } ¦ ¦ if (dot == -1) { ¦ ¦ ¦ /* No dot in module name, simple exit */ ¦ ¦ ¦ final_mod = mod; ¦ ¦ ¦ Py_INCREF(mod); ¦ ¦ ¦ goto error; ¦ ¦ } ¦ ¦ if (level == 0) { ¦ ¦ ¦ PyObject *front = PyUnicode_Substring(name, 0, dot); ¦ ¦ ¦ if (front == NULL) { ¦ ¦ ¦ ¦ goto error; ¦ ¦ ¦ } ¦ ¦ ¦ final_mod = PyImport_ImportModuleLevelObject(front, NULL, NULL, NULL, 0); ¦ ¦ ¦ Py_DECREF(front); ¦ ¦ } ¦ ¦ else { ¦ ¦ ¦ Py_ssize_t cut_off = len - dot; ¦ ¦ ¦ Py_ssize_t abs_name_len = PyUnicode_GET_LENGTH(abs_name); ¦ ¦ ¦ PyObject *to_return = PyUnicode_Substring(abs_name, 0, ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ abs_name_len - cut_off); ¦ ¦ ¦ if (to_return == NULL) { ¦ ¦ ¦ ¦ goto error; ¦ ¦ ¦ } ¦ ¦ ¦ final_mod = PyImport_GetModule(to_return); ¦ ¦ ¦ Py_DECREF(to_return); ¦ ¦ ¦ if (final_mod == NULL) { ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ PyErr_Format(PyExc_KeyError, ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦"%R not in sys.modules as expected", ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦to_return); ¦ ¦ ¦ ¦ goto error; ¦ ¦ ¦ } ¦ ¦ } ¦ } ¦ else { ¦ ¦ final_mod = mod; ¦ ¦ Py_INCREF(mod); ¦ } } else { ¦ final_mod = _PyObject_CallMethodIdObjArgs(interp->importlib, ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ &PyId__handle_fromlist, mod, ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ fromlist, interp->import_func, ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ NULL); } error: Py_XDECREF(abs_name); Py_XDECREF(mod); Py_XDECREF(package); if (final_mod == NULL) ¦ remove_importlib_frames(); return final_mod; } ``` ```c /* --- Core PyObject call functions ------------------------------- */ PyObject * _PyObject_FastCallDict(PyObject *callable, PyObject **args, Py_ssize_t nargs, ¦ ¦ ¦ ¦ ¦ PyObject *kwargs) { /* _PyObject_FastCallDict() must not be called with an exception set, ¦ because it can clear it (directly or indirectly) and so the ¦ caller loses its exception */ assert(!PyErr_Occurred()); assert(callable != NULL); assert(nargs >= 0); assert(nargs == 0 || args != NULL); assert(kwargs == NULL || PyDict_Check(kwargs)); if (PyFunction_Check(callable)) { ¦ return _PyFunction_FastCallDict(callable, args, nargs, kwargs); } else if (PyCFunction_Check(callable)) { ¦ return _PyCFunction_FastCallDict(callable, args, nargs, kwargs); } else { ¦ PyObject *argstuple, *result; ¦ ternaryfunc call; ¦ /* Slow-path: build a temporary tuple */ ¦ call = callable->ob_type->tp_call; ¦ if (call == NULL) { ¦ ¦ PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", ¦ ¦ ¦ ¦ ¦ ¦callable->ob_type->tp_name); ¦ ¦ return NULL; ¦ } ¦ argstuple = _PyStack_AsTuple(args, nargs); ¦ if (argstuple == NULL) { ¦ ¦ return NULL; ¦ } ¦ if (Py_EnterRecursiveCall(" while calling a Python object")) { ¦ ¦ Py_DECREF(argstuple); ¦ ¦ return NULL; ¦ } ¦ result = (*call)(callable, argstuple, kwargs); ¦ Py_LeaveRecursiveCall(); ¦ Py_DECREF(argstuple); ¦ result = _Py_CheckFunctionResult(callable, result, NULL); ¦ return result; } } ``` # compile and eval ```python compile? ``` ``` Signature: compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1) Docstring: Compile source into a code object that can be executed by exec() or eval(). The source code may represent a Python module, statement or expression. The filename will be used for run-time error messages. The mode must be 'exec' to compile a module, 'single' to compile a single (interactive) statement, or 'eval' to compile an expression. The flags argument, if present, controls which future statements influence the compilation of the code. The dont_inherit argument, if true, stops the compilation inheriting the effects of any future statements in effect in the code calling compile; if absent or false these statements do influence the compilation, in addition to any features explicitly specified. Type: builtin_function_or_method ``` # bytecode Python source code is compiled into bytecode, the internal representation of a Python program in the CPython interpreter. The bytecode is also cached in .pyc files so that executing the same file is faster the second time (recompilation from source to bytecode can be avoided). This “intermediate language” is said to run on a virtual machine that executes the machine code corresponding to each bytecode. Do note that bytecodes are not expected to work between different Python virtual machines, nor to be stable between Python releases. > A list of bytecode instructions can be found in the documentation for the dis module. ```python help(exec) ``` Help on built-in function exec in module builtins: exec(source, globals=None, locals=None, /) Execute the given source in the context of globals and locals. The source may be a string representing one or more Python statements or a code object as returned by compile(). The globals must be a dictionary and locals can be any mapping, defaulting to the current globals and locals. If only globals is given, locals defaults to it. ```python help(eval) ``` Help on built-in function eval in module builtins: eval(source, globals=None, locals=None, /) Evaluate the given source in the context of globals and locals. The source may be a string representing a Python expression or a code object as returned by compile(). The globals must be a dictionary and locals can be any mapping, defaulting to the current globals and locals. If only globals is given, locals defaults to it. 所以`eval`執行的內容必須是`expression`,而`exec`是都可以執行,但是回傳值固定為None。 ```python code_str=""" def hello(): print("hello world") hello() """ ``` ```python exec('print(5)') # prints 5. # exec 'print 5' if you use Python 2.x, nor the exec neither the print is a function there exec('print(5)\nprint(6)') # prints 5{newline}6. exec('if True: print(6)') # prints 6. exec('5') ``` 5 5 6 6 ```python eval('5') # x <- 5 eval('%d + 6' % x) # x <- 11 eval('abs(%d)' % -100) # x <- 100 eval('x = 5') # INVALID; assignment is not an expression. eval('if 1: x = 4') # INVALID; if is a statement, not an expression. ``` --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-107-40888bad4179> in <module>() 1 eval('5') # x <- 5 ----> 2 eval('%d + 6' % x) # x <- 11 3 eval('abs(%d)' % -100) # x <- 100 4 eval('x = 5') # INVALID; assignment is not an expression. 5 eval('if 1: x = 4') # INVALID; if is a statement, not an expression. TypeError: %d format: a number is required, not generator ```python code_obj_exec=compile(code_str,'string','exec') code_obj_exec exec(code_obj_exec) ``` hello world ```python eval(compile('42', '<string>', 'exec')) # code return None ``` ```python eval(compile('42', '<string>', 'eval')) # code returns 42 ``` 42 ```python exec(compile('42', '<string>', 'eval')) # code returns 42,but ignored by exec ``` ```python code_obj_signle=compile(code_str,'string','single') exec(code_obj_signle) ``` Traceback (most recent call last): File "/usr/local/lib/python3.5/dist-packages/IPython/core/interactiveshell.py", line 2862, in run_code exec(code_obj, self.user_global_ns, self.user_ns) File "<ipython-input-112-e920440df200>", line 1, in <module> code_obj_signle=compile(code_str,'string','single') File "string", line 4 hello() ^ SyntaxError: invalid syntax 而single 只接受oneline statment ```python code_obj_eval=compile(code_str,'string','eval') ``` Traceback (most recent call last): File "/usr/local/lib/python3.5/dist-packages/IPython/core/interactiveshell.py", line 2862, in run_code exec(code_obj, self.user_global_ns, self.user_ns) File "<ipython-input-113-686a1487d9cc>", line 1, in <module> code_obj_eval=compile(code_str,'string','eval') File "string", line 2 def hello(): ^ SyntaxError: invalid syntax ```python exec('print("hello")') ``` hello ```python eval("print('hello')") ``` hello ```python exec("3+5") ``` ```python eval("3+5") ``` 8 Actually the statement "<span class="girk">eval accepts only a single expression</span>" applies only when a string (which contains Python source code) is passed to eval. Then it is internally compiled to bytecode using compile(source, '<string>', 'eval') This is where the difference really comes from. If a code object (which contains Python bytecode) is passed to exec or eval, <span class="mark">they behave identically</span>, excepting for the fact that exec ignores the return value, still returning None always. So it is possible use eval to execute something that has statements, if you just compiled it into bytecode before instead of passing it as a string: ```python eval(compile('if 1: print("Hello")', '<string>', 'exec')) ``` Hello It should work. ## The longer answer, a.k.a the gory details * The `exec` function (which was a statement in Python 2) is used for executing a dynamically created statement or program. * The `eval` function does the same for a single expression, and returns the value of the expression. exec and eval both accept the program/expression to be run either as a str, unicode or bytes object containing source code, or as a code object which contains Python bytecode. If a str/unicode/bytes containing source code was passed to exec, it behaves equivalently to: ``` exec(compile(source, '<string>', 'exec')) ``` and eval similarly behaves equivalent to: ``` eval(compile(source, '<string>', 'eval')) ``` ```python def my_func(arg): print("Called with %d" % arg) return arg * 2 ``` ```python exec('my_func(42)') ``` Called with 42 ```python eval('my_func(42)') ``` Called with 42 84 # 深入code object ## Code objects Code objects represent byte-compiled executable Python code, or bytecode. The difference between a code object and a function object is that the function object contains an explicit reference to the function’s globals (the module in which it was defined), while a code object contains no context; also the default argument values are stored in the function object, not in the code object (because they represent values calculated at run-time). Unlike function objects, code objects are immutable and contain no references (directly or indirectly) to mutable objects. ``` code co_argcount number of arguments (not including keyword only arguments, * or ** args) co_code string of raw compiled bytecode co_cellvars tuple of names of cell variables (referenced by containing scopes) co_consts tuple of constants used in the bytecode co_filename name of file in which this code object was created co_firstlineno number of first line in Python source code co_flags bitmap of CO_* flags, read more here co_lnotab encoded mapping of line numbers to bytecode indices co_freevars tuple of names of free variables (referenced via a function’s closure) co_kwonlyargcount number of keyword only arguments (not including ** arg) co_name name with which this code object was defined co_names tuple of names of local variables co_nlocals number of local variables co_stacksize virtual machine stack space required co_varnames tuple of names of arguments and local variables ``` 可以參考python的[inspect lib](https://docs.python.org/3/library/inspect.html) ## code object implement inside `include/code.h` ```c /* Bytecode object */ typedef struct { PyObject_HEAD int co_argcount; /* #arguments, except *args */ int co_kwonlyargcount; /* #keyword only arguments */ int co_nlocals; /* #local variables */ int co_stacksize; /* #entries needed for evaluation stack */ int co_flags; /* CO_..., see below */ int co_firstlineno; /* first source line number */ PyObject* co_code; /* instruction opcodes */ PyObject* co_consts; /* list (constants used) */ PyObject* co_names; /* list of strings (names used) */ PyObject* co_varnames; /* tuple of strings (local variable names) */ PyObject* co_freevars; /* tuple of strings (free variable names) */ PyObject* co_cellvars; /* tuple of strings (cell variable names) */ /* The rest aren't used in either hash or comparisons, except for co_name, used in both. This is done to preserve the name and line number for tracebacks and debuggers; otherwise, constant de-duplication would collapse identical functions/lambdas defined on different lines. */ Py_ssize_t* co_cell2arg; /* Maps cell vars which are arguments. */ PyObject* co_filename; /* unicode (where it was loaded from) */ PyObject* co_name; /* unicode (name, for reference) */ PyObject* co_lnotab; /* string (encoding addr<->lineno mapping) See Objects/lnotab_notes.txt for details. */ void* co_zombieframe; /* for optimization only (see frameobject.c) */ PyObject* co_weakreflist; /* to support weakrefs to code objects */ /* Scratch space for extra data relating to the code object. Type is a void* to keep the format private in codeobject.c to force people to go through the proper APIs. */ void* co_extra; } PyCodeObject; ``` ## more about code object ### inside the code object ```python code_obj=compile(code_str,'string','exec') dir(code_obj) ``` ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames'] ### get code object code ```python code_obj.co_code ``` b'd\x00\x00d\x01\x00\x84\x00\x00Z\x00\x00e\x00\x00\x83\x00\x00\x01d\x02\x00S' ```python [x for x in code_obj.co_code] ``` [100, 0, 0, 100, 1, 0, 132, 0, 0, 90, 0, 0, 101, 0, 0, 131, 0, 0, 1, 100, 2, 0, 83] ```python dis.show_code(code_obj) ``` Name: <module> Filename: string Argument count: 0 Kw-only arguments: 0 Number of locals: 0 Stack size: 2 Flags: NOFREE Constants: 0: <code object hello at 0x7f01fc67f030, file "string", line 2> 1: 'hello' 2: None Names: 0: hello ```python dis.dis(code_obj) ``` 2 0 LOAD_CONST 0 (<code object hello at 0x7f01fc67f030, file "string", line 2>) 3 LOAD_CONST 1 ('hello') 6 MAKE_FUNCTION 0 9 STORE_NAME 0 (hello) 4 12 LOAD_NAME 0 (hello) 15 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 18 POP_TOP 19 LOAD_CONST 2 (None) 22 RETURN_VALUE 和上面的code object做對照 ```python iterator=dis.get_instructions(code_str) for it in iterator: print(it.opcode) ``` 100 100 132 90 101 131 1 100 83 ```python op=code_obj.co_code[0] op ``` 100 ```python opcode.opname[op] ``` 'LOAD_CONST' 可以發現opcode是python的一個module,他負責定義python interpreter會用到的instruction,所以我們才可以透過dis來disassembly python bytecode,而code object是bytecode的表現形式。 python中opcode的value和python virtual machine所定義的instruction相同(定義於opcode.h中,和dis.opmap的value互相對應),因此可以透過實做interpreter(不管任何形式)產生的python bytecode來讓python virtual machine執行。 ```python code_obj.co_consts ``` (<code object hello at 0x7f3768227d20, file "string", line 2>, 'hello', None) ```python code_obj.co_lnotab ``` b'\x0c\x03' ```python code_obj.co_freevars ``` () ```python code_obj.co_stacksize ``` 3 # A Detour into Code Disassembly ```python import dis def hello(): print("Hello world") ``` ```python code_str=""" def print_hello(x,y): print("hello") return x+y hello(10,11) """ ``` ```python code_obj=compile(code_str,'string','exec') dis.dis(code_obj) ``` 2 0 LOAD_CONST 0 (<code object print_hello at 0x7f01fc6fde40, file "string", line 2>) 3 LOAD_CONST 1 ('print_hello') 6 MAKE_FUNCTION 0 9 STORE_NAME 0 (print_hello) 5 12 LOAD_NAME 1 (hello) 15 LOAD_CONST 2 (10) 18 LOAD_CONST 3 (11) 21 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 24 POP_TOP 25 LOAD_CONST 4 (None) 28 RETURN_VALUE |line number|bytecode offect|bytecode|bytecode argument| |:---------:|:-------------:|:------:|:---------------:| |5|12|LOAD_NAME|0(hello) ```python bytecode=dis.Bytecode(hello) bytecode ``` Bytecode(<function hello at 0x7f01fc74fd90>) ```python help(bytecode) ``` Help on Bytecode in module dis object: class Bytecode(builtins.object) | The bytecode operations of a piece of code | | Instantiate this with a function, method, string of code, or a code object | (as returned by compile()). | | Iterating over this yields the bytecode operations as Instruction instances. | | Methods defined here: | | __init__(self, x, *, first_line=None, current_offset=None) | Initialize self. See help(type(self)) for accurate signature. | | __iter__(self) | | __repr__(self) | Return repr(self). | | dis(self) | Return a formatted view of the bytecode operations. | | info(self) | Return formatted information about the code object. | | ---------------------------------------------------------------------- | Class methods defined here: | | from_traceback(tb) from builtins.type | Construct a Bytecode from the given traceback | | ---------------------------------------------------------------------- | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) ```python bytecode.dis() ``` " 3 0 LOAD_GLOBAL 0 (print)\n 3 LOAD_CONST 1 ('Hello world')\n 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)\n 9 POP_TOP\n 10 LOAD_CONST 0 (None)\n 13 RETURN_VALUE\n" ```python bytecode.info() ``` "Name: hello\nFilename: <ipython-input-70-9b74c1c13393>\nArgument count: 0\nKw-only arguments: 0\nNumber of locals: 0\nStack size: 2\nFlags: OPTIMIZED, NEWLOCALS, NOFREE\nConstants:\n 0: None\n 1: 'Hello world'\nNames:\n 0: print" ```python for instr in bytecode: print(instr.opname) ``` LOAD_GLOBAL LOAD_CONST CALL_FUNCTION POP_TOP LOAD_CONST RETURN_VALUE ```python dis.dis(hello) ``` 3 0 LOAD_GLOBAL 0 (print) 3 LOAD_CONST 1 ('Hello world') 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 9 POP_TOP 10 LOAD_CONST 0 (None) 13 RETURN_VALUE ```python import opcode help(opcode) ``` Help on module opcode: NAME opcode MODULE REFERENCE https://docs.python.org/3.5/library/opcode The following documentation is automatically generated from the Python source files. It may be incomplete, incorrect or include features that are considered implementation detail and may vary between Python implementations. When in doubt, consult the module reference at the location listed above. DESCRIPTION opcode module - potentially shared between dis and other modules which operate on bytecodes (e.g. peephole optimizers). FUNCTIONS stack_effect(opcode, oparg=None, /) Compute the stack effect of the opcode. DATA EXTENDED_ARG = 144 HAVE_ARGUMENT = 90 __all__ = ['cmp_op', 'hasconst', 'hasname', 'hasjrel', 'hasjabs', 'has... cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 'is ... hascompare = [107] hasconst = [100] hasfree = [135, 136, 137, 138, 148] hasjabs = [111, 112, 113, 114, 115, 119] hasjrel = [93, 110, 120, 121, 122, 143, 154] haslocal = [124, 125, 126] hasname = [90, 91, 95, 96, 97, 98, 101, 106, 108, 109, 116] hasnargs = [131, 140, 141, 142] opmap = {'BEFORE_ASYNC_WITH': 52, 'BINARY_ADD': 23, 'BINARY_AND': 64, ... opname = ['<0>', 'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'DUP_TOP', 'DUP_TO... FILE /usr/lib/python3.5/opcode.py ```python dis.code_info(hello) ``` "Name: hello\nFilename: <ipython-input-70-9b74c1c13393>\nArgument count: 0\nKw-only arguments: 0\nNumber of locals: 0\nStack size: 2\nFlags: OPTIMIZED, NEWLOCALS, NOFREE\nConstants:\n 0: None\n 1: 'Hello world'\nNames:\n 0: print" ```python dis.show_code(hello) ``` Name: hello Filename: <ipython-input-70-9b74c1c13393> Argument count: 0 Kw-only arguments: 0 Number of locals: 0 Stack size: 2 Flags: OPTIMIZED, NEWLOCALS, NOFREE Constants: 0: None 1: 'Hello world' Names: 0: print ```python help(dis.distb) ``` Help on function distb in module dis: distb(tb=None, *, file=None) Disassemble a traceback (default: last traceback). ```python dis.disassemble(code_obj) ``` 2 0 LOAD_CONST 0 (<code object print_hello at 0x7f01fc6fde40, file "string", line 2>) 3 LOAD_CONST 1 ('print_hello') 6 MAKE_FUNCTION 0 9 STORE_NAME 0 (print_hello) 5 12 LOAD_NAME 1 (hello) 15 LOAD_CONST 2 (10) 18 LOAD_CONST 3 (11) 21 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 24 POP_TOP 25 LOAD_CONST 4 (None) 28 RETURN_VALUE ```python dis.disco(code_obj) ``` 2 0 LOAD_CONST 0 (<code object print_hello at 0x7f01fc6fde40, file "string", line 2>) 3 LOAD_CONST 1 ('print_hello') 6 MAKE_FUNCTION 0 9 STORE_NAME 0 (print_hello) 5 12 LOAD_NAME 1 (hello) 15 LOAD_CONST 2 (10) 18 LOAD_CONST 3 (11) 21 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 24 POP_TOP 25 LOAD_CONST 4 (None) 28 RETURN_VALUE 這兩個function的作用是一樣的 ```python iterator=dis.get_instructions(code_str) for it in iterator: print(it) ``` Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=<code object print_hello at 0x7f01fc6f5030, file "<disassembly>", line 2>, argrepr='<code object print_hello at 0x7f01fc6f5030, file "<disassembly>", line 2>', offset=0, starts_line=2, is_jump_target=False) Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval='print_hello', argrepr="'print_hello'", offset=3, starts_line=None, is_jump_target=False) Instruction(opname='MAKE_FUNCTION', opcode=132, arg=0, argval=0, argrepr='', offset=6, starts_line=None, is_jump_target=False) Instruction(opname='STORE_NAME', opcode=90, arg=0, argval='print_hello', argrepr='print_hello', offset=9, starts_line=None, is_jump_target=False) Instruction(opname='LOAD_NAME', opcode=101, arg=1, argval='hello', argrepr='hello', offset=12, starts_line=5, is_jump_target=False) Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=10, argrepr='10', offset=15, starts_line=None, is_jump_target=False) Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=11, argrepr='11', offset=18, starts_line=None, is_jump_target=False) Instruction(opname='CALL_FUNCTION', opcode=131, arg=2, argval=2, argrepr='2 positional, 0 keyword pair', offset=21, starts_line=None, is_jump_target=False) Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=24, starts_line=None, is_jump_target=False) Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval=None, argrepr='None', offset=25, starts_line=None, is_jump_target=False) Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=28, starts_line=None, is_jump_target=False) ``` opname - human readable name for operation opcode - numeric code for operation arg - numeric argument to operation (if any), otherwise None argval - resolved arg value (if known), otherwise same as arg argrepr - human readable description of operation argument offset - start index of operation within bytecode sequence starts_line - line started by this opcode (if any), otherwise None is_jump_target - True if other code jumps to here, otherwise False ``` ```python x=dis.findlinestarts(code_obj) next(x) ``` (0, 2) ```python next(x) ``` (12, 5) ```python help(dis.stack_effect) ``` Help on built-in function stack_effect in module _opcode: stack_effect(opcode, oparg=None, /) Compute the stack effect of the opcode. ```python dis.stack_effect(1) # POP_TOP ``` -1 ```python help(dis.findlabels) ``` Help on function findlabels in module dis: findlabels(code) Detect all offsets in a byte code which are jump targets. Return the list of offsets. ```python def f(x): if x == 0: return 1 elif x==1: return 2 ``` ```python dis.disco(f.__code__) ``` 2 0 LOAD_FAST 0 (x) 3 LOAD_CONST 1 (0) 6 COMPARE_OP 2 (==) 9 POP_JUMP_IF_FALSE 16 3 12 LOAD_CONST 2 (1) 15 RETURN_VALUE 4 >> 16 LOAD_FAST 0 (x) 19 LOAD_CONST 2 (1) 22 COMPARE_OP 2 (==) 25 POP_JUMP_IF_FALSE 32 5 28 LOAD_CONST 3 (2) 31 RETURN_VALUE >> 32 LOAD_CONST 0 (None) 35 RETURN_VALUE ```python dis.findlabels(f.__code__.co_code) ``` [16, 32] ```python dis.opname ``` ['<0>', 'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'DUP_TOP', 'DUP_TOP_TWO', '<6>', '<7>', '<8>', 'NOP', 'UNARY_POSITIVE', 'UNARY_NEGATIVE', 'UNARY_NOT', '<13>', '<14>', 'UNARY_INVERT', 'BINARY_MATRIX_MULTIPLY', 'INPLACE_MATRIX_MULTIPLY', '<18>', 'BINARY_POWER', 'BINARY_MULTIPLY', '<21>', 'BINARY_MODULO', 'BINARY_ADD', 'BINARY_SUBTRACT', 'BINARY_SUBSCR', 'BINARY_FLOOR_DIVIDE', 'BINARY_TRUE_DIVIDE', 'INPLACE_FLOOR_DIVIDE', 'INPLACE_TRUE_DIVIDE', '<30>', '<31>', '<32>', '<33>', '<34>', '<35>', '<36>', '<37>', '<38>', '<39>', '<40>', '<41>', '<42>', '<43>', '<44>', '<45>', '<46>', '<47>', '<48>', '<49>', 'GET_AITER', 'GET_ANEXT', 'BEFORE_ASYNC_WITH', '<53>', '<54>', 'INPLACE_ADD', 'INPLACE_SUBTRACT', 'INPLACE_MULTIPLY', '<58>', 'INPLACE_MODULO', 'STORE_SUBSCR', 'DELETE_SUBSCR', 'BINARY_LSHIFT', 'BINARY_RSHIFT', 'BINARY_AND', 'BINARY_XOR', 'BINARY_OR', 'INPLACE_POWER', 'GET_ITER', 'GET_YIELD_FROM_ITER', 'PRINT_EXPR', 'LOAD_BUILD_CLASS', 'YIELD_FROM', 'GET_AWAITABLE', '<74>', 'INPLACE_LSHIFT', 'INPLACE_RSHIFT', 'INPLACE_AND', 'INPLACE_XOR', 'INPLACE_OR', 'BREAK_LOOP', 'WITH_CLEANUP_START', 'WITH_CLEANUP_FINISH', 'RETURN_VALUE', 'IMPORT_STAR', '<85>', 'YIELD_VALUE', 'POP_BLOCK', 'END_FINALLY', 'POP_EXCEPT', 'STORE_NAME', 'DELETE_NAME', 'UNPACK_SEQUENCE', 'FOR_ITER', 'UNPACK_EX', 'STORE_ATTR', 'DELETE_ATTR', 'STORE_GLOBAL', 'DELETE_GLOBAL', '<99>', 'LOAD_CONST', 'LOAD_NAME', 'BUILD_TUPLE', 'BUILD_LIST', 'BUILD_SET', 'BUILD_MAP', 'LOAD_ATTR', 'COMPARE_OP', 'IMPORT_NAME', 'IMPORT_FROM', 'JUMP_FORWARD', 'JUMP_IF_FALSE_OR_POP', 'JUMP_IF_TRUE_OR_POP', 'JUMP_ABSOLUTE', 'POP_JUMP_IF_FALSE', 'POP_JUMP_IF_TRUE', 'LOAD_GLOBAL', '<117>', '<118>', 'CONTINUE_LOOP', 'SETUP_LOOP', 'SETUP_EXCEPT', 'SETUP_FINALLY', '<123>', 'LOAD_FAST', 'STORE_FAST', 'DELETE_FAST', '<127>', '<128>', '<129>', 'RAISE_VARARGS', 'CALL_FUNCTION', 'MAKE_FUNCTION', 'BUILD_SLICE', 'MAKE_CLOSURE', 'LOAD_CLOSURE', 'LOAD_DEREF', 'STORE_DEREF', 'DELETE_DEREF', '<139>', 'CALL_FUNCTION_VAR', 'CALL_FUNCTION_KW', 'CALL_FUNCTION_VAR_KW', 'SETUP_WITH', 'EXTENDED_ARG', 'LIST_APPEND', 'SET_ADD', 'MAP_ADD', 'LOAD_CLASSDEREF', 'BUILD_LIST_UNPACK', 'BUILD_MAP_UNPACK', 'BUILD_MAP_UNPACK_WITH_CALL', 'BUILD_TUPLE_UNPACK', 'BUILD_SET_UNPACK', 'SETUP_ASYNC_WITH', '<155>', '<156>', '<157>', '<158>', '<159>', '<160>', '<161>', '<162>', '<163>', '<164>', '<165>', '<166>', '<167>', '<168>', '<169>', '<170>', '<171>', '<172>', '<173>', '<174>', '<175>', '<176>', '<177>', '<178>', '<179>', '<180>', '<181>', '<182>', '<183>', '<184>', '<185>', '<186>', '<187>', '<188>', '<189>', '<190>', '<191>', '<192>', '<193>', '<194>', '<195>', '<196>', '<197>', '<198>', '<199>', '<200>', '<201>', '<202>', '<203>', '<204>', '<205>', '<206>', '<207>', '<208>', '<209>', '<210>', '<211>', '<212>', '<213>', '<214>', '<215>', '<216>', '<217>', '<218>', '<219>', '<220>', '<221>', '<222>', '<223>', '<224>', '<225>', '<226>', '<227>', '<228>', '<229>', '<230>', '<231>', '<232>', '<233>', '<234>', '<235>', '<236>', '<237>', '<238>', '<239>', '<240>', '<241>', '<242>', '<243>', '<244>', '<245>', '<246>', '<247>', '<248>', '<249>', '<250>', '<251>', '<252>', '<253>', '<254>', '<255>'] ```python sorted(dis.opmap.items(),key=lambda x:x[1]) ``` [('POP_TOP', 1), ('ROT_TWO', 2), ('ROT_THREE', 3), ('DUP_TOP', 4), ('DUP_TOP_TWO', 5), ('NOP', 9), ('UNARY_POSITIVE', 10), ('UNARY_NEGATIVE', 11), ('UNARY_NOT', 12), ('UNARY_INVERT', 15), ('BINARY_MATRIX_MULTIPLY', 16), ('INPLACE_MATRIX_MULTIPLY', 17), ('BINARY_POWER', 19), ('BINARY_MULTIPLY', 20), ('BINARY_MODULO', 22), ('BINARY_ADD', 23), ('BINARY_SUBTRACT', 24), ('BINARY_SUBSCR', 25), ('BINARY_FLOOR_DIVIDE', 26), ('BINARY_TRUE_DIVIDE', 27), ('INPLACE_FLOOR_DIVIDE', 28), ('INPLACE_TRUE_DIVIDE', 29), ('GET_AITER', 50), ('GET_ANEXT', 51), ('BEFORE_ASYNC_WITH', 52), ('INPLACE_ADD', 55), ('INPLACE_SUBTRACT', 56), ('INPLACE_MULTIPLY', 57), ('INPLACE_MODULO', 59), ('STORE_SUBSCR', 60), ('DELETE_SUBSCR', 61), ('BINARY_LSHIFT', 62), ('BINARY_RSHIFT', 63), ('BINARY_AND', 64), ('BINARY_XOR', 65), ('BINARY_OR', 66), ('INPLACE_POWER', 67), ('GET_ITER', 68), ('GET_YIELD_FROM_ITER', 69), ('PRINT_EXPR', 70), ('LOAD_BUILD_CLASS', 71), ('YIELD_FROM', 72), ('GET_AWAITABLE', 73), ('INPLACE_LSHIFT', 75), ('INPLACE_RSHIFT', 76), ('INPLACE_AND', 77), ('INPLACE_XOR', 78), ('INPLACE_OR', 79), ('BREAK_LOOP', 80), ('WITH_CLEANUP_START', 81), ('WITH_CLEANUP_FINISH', 82), ('RETURN_VALUE', 83), ('IMPORT_STAR', 84), ('YIELD_VALUE', 86), ('POP_BLOCK', 87), ('END_FINALLY', 88), ('POP_EXCEPT', 89), ('STORE_NAME', 90), ('DELETE_NAME', 91), ('UNPACK_SEQUENCE', 92), ('FOR_ITER', 93), ('UNPACK_EX', 94), ('STORE_ATTR', 95), ('DELETE_ATTR', 96), ('STORE_GLOBAL', 97), ('DELETE_GLOBAL', 98), ('LOAD_CONST', 100), ('LOAD_NAME', 101), ('BUILD_TUPLE', 102), ('BUILD_LIST', 103), ('BUILD_SET', 104), ('BUILD_MAP', 105), ('LOAD_ATTR', 106), ('COMPARE_OP', 107), ('IMPORT_NAME', 108), ('IMPORT_FROM', 109), ('JUMP_FORWARD', 110), ('JUMP_IF_FALSE_OR_POP', 111), ('JUMP_IF_TRUE_OR_POP', 112), ('JUMP_ABSOLUTE', 113), ('POP_JUMP_IF_FALSE', 114), ('POP_JUMP_IF_TRUE', 115), ('LOAD_GLOBAL', 116), ('CONTINUE_LOOP', 119), ('SETUP_LOOP', 120), ('SETUP_EXCEPT', 121), ('SETUP_FINALLY', 122), ('LOAD_FAST', 124), ('STORE_FAST', 125), ('DELETE_FAST', 126), ('RAISE_VARARGS', 130), ('CALL_FUNCTION', 131), ('MAKE_FUNCTION', 132), ('BUILD_SLICE', 133), ('MAKE_CLOSURE', 134), ('LOAD_CLOSURE', 135), ('LOAD_DEREF', 136), ('STORE_DEREF', 137), ('DELETE_DEREF', 138), ('CALL_FUNCTION_VAR', 140), ('CALL_FUNCTION_KW', 141), ('CALL_FUNCTION_VAR_KW', 142), ('SETUP_WITH', 143), ('EXTENDED_ARG', 144), ('LIST_APPEND', 145), ('SET_ADD', 146), ('MAP_ADD', 147), ('LOAD_CLASSDEREF', 148), ('BUILD_LIST_UNPACK', 149), ('BUILD_MAP_UNPACK', 150), ('BUILD_MAP_UNPACK_WITH_CALL', 151), ('BUILD_TUPLE_UNPACK', 152), ('BUILD_SET_UNPACK', 153), ('SETUP_ASYNC_WITH', 154)] ```python len(dis.opmap.items()) # numbers of the python instructions ``` 114 而這些bytecode被定義在opcode module中 ```python opcode?? ``` ```python dis.cmp_op ``` ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 'is not', 'exception match', 'BAD') ```python dis.hasconst ``` [100] ```python dis.hasfree ``` [135, 136, 137, 138, 148] ```python dis.hasname ``` [90, 91, 95, 96, 97, 98, 101, 106, 108, 109, 116] ```python dis.hasjrel ``` [93, 110, 120, 121, 122, 143, 154] ```python dis.hasjabs ``` [111, 112, 113, 114, 115, 119] ```python dis.haslocal ``` [124, 125, 126] ```python dis.hascompare ``` [107] ## Ref Python dis module <https://docs.python.org/3.7/library/dis.html?module-dis> stack overflow 對於比較exec和eval的回答 <https://stackoverflow.com/questions/2220699/whats-the-difference-between-eval-exec-and-compile-in-python> # Python virtual machine implementation `Python/ceval.c` ```c PyObject* _Py_HOT_FUNCTION _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) ``` python interpreter main的進入點 ``` Py_Main(int arg,wchar_t argv**) ``` python interpreter initiali ``` _Py_InitializeCore ``` PyFrameObject `Include/frameobject.h` ```c typedef struct _frame { PyObject_VAR_HEAD struct _frame *f_back; /* previous frame, or NULL */ PyCodeObject *f_code; /* code segment */ PyObject *f_builtins; /* builtin symbol table (PyDictObject) */ PyObject *f_globals; /* global symbol table (PyDictObject) */ PyObject *f_locals; /* local symbol table (any mapping) */ PyObject **f_valuestack; /* points after the last local */ /* Next free slot in f_valuestack. Frame creation sets to f_valuestack. Frame evaluation usually NULLs it, but a frame that yields sets it to the current stack top. */ PyObject **f_stacktop; PyObject *f_trace; /* Trace function */ /* In a generator, we need to be able to swap between the exception state inside the generator and the exception state of the calling frame (which shouldn't be impacted when the generator "yields" from an except handler). These three fields exist exactly for that, and are unused for non-generator frames. See the save_exc_state and swap_exc_state functions in ceval.c for details of their use. */ PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; /* Borrowed reference to a generator, or NULL */ PyObject *f_gen; int f_lasti; /* Last instruction if called */ /* Call PyFrame_GetLineNumber() instead of reading this field directly. As of 2.3 f_lineno is only valid when tracing is active (i.e. when f_trace is set). At other times we use PyCode_Addr2Line to calculate the line from the current bytecode index. */ int f_lineno; /* Current line number */ int f_iblock; /* index in f_blockstack */ char f_executing; /* whether the frame is still executing */ PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ } PyFrameObject; ``` ## python object ```c /* Define pointers to support a doubly-linked list of all live heap objects. */ #define _PyObject_HEAD_EXTRA \ struct _object *_ob_next; \ struct _object *_ob_prev; #define _PyObject_EXTRA_INIT 0, 0, #else #define _PyObject_HEAD_EXTRA #define _PyObject_EXTRA_INIT #endif /* PyObject_HEAD defines the initial segment of every PyObject. */ #define PyObject_HEAD PyObject ob_base; #define PyObject_HEAD_INIT(type) \ { _PyObject_EXTRA_INIT \ 1, type }, #define PyVarObject_HEAD_INIT(type, size) \ { PyObject_HEAD_INIT(type) size }, /* PyObject_VAR_HEAD defines the initial segment of all variable-size * container objects. These end with a declaration of an array with 1 * element, but enough space is malloc'ed so that the array actually * has room for ob_size elements. Note that ob_size is an element count, * not necessarily a byte count. */ #define PyObject_VAR_HEAD PyVarObject ob_base; #define Py_INVALID_SIZE (Py_ssize_t)-1 /* Nothing is actually declared to be a PyObject, but every pointer to * a Python object can be cast to a PyObject*. This is inheritance built * by hand. Similarly every pointer to a variable-size Python object can, * in addition, be cast to PyVarObject*. */ typedef struct _object { _PyObject_HEAD_EXTRA Py_ssize_t ob_refcnt; struct _typeobject *ob_type; } PyObject; typedef struct { PyObject ob_base; Py_ssize_t ob_size; /* Number of items in variable part */ } PyVarObject; #define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt) #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) #define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) typedef void (*freefunc)(void *); typedef void (*destructor)(PyObject *); #ifndef Py_LIMITED_API /* We can't provide a full compile-time check that limited-API users won't implement tp_print. However, not defining printfunc and making tp_print of a different function pointer type should at least cause a warning in most cases. */ typedef int (*printfunc)(PyObject *, FILE *, int); #endif typedef PyObject *(*getattrfunc)(PyObject *, char *); typedef PyObject *(*getattrofunc)(PyObject *, PyObject *); typedef int (*setattrfunc)(PyObject *, char *, PyObject *); typedef int (*setattrofunc)(PyObject *, PyObject *, PyObject *); typedef PyObject *(*reprfunc)(PyObject *); typedef Py_hash_t (*hashfunc)(PyObject *); typedef PyObject *(*richcmpfunc) (PyObject *, PyObject *, int); typedef PyObject *(*getiterfunc) (PyObject *); typedef PyObject *(*iternextfunc) (PyObject *); typedef PyObject *(*descrgetfunc) (PyObject *, PyObject *, PyObject *); typedef int (*descrsetfunc) (PyObject *, PyObject *, PyObject *); typedef int (*initproc)(PyObject *, PyObject *, PyObject *); typedef PyObject *(*newfunc)(struct _typeobject *, PyObject *, PyObject *); typedef PyObject *(*allocfunc)(struct _typeobject *, Py_ssize_t); #ifdef Py_LIMITED_API typedef struct _typeobject PyTypeObject; /* opaque */ #else typedef struct _typeobject { PyObject_VAR_HEAD const char *tp_name; /* For printing, in format "<module>.<name>" */ Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */ /* Methods to implement standard operations */ destructor tp_dealloc; printfunc tp_print; getattrfunc tp_getattr; setattrfunc tp_setattr; PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2) or tp_reserved (Python 3) */ reprfunc tp_repr; /* Method suites for standard classes */ PyNumberMethods *tp_as_number; PySequenceMethods *tp_as_sequence; PyMappingMethods *tp_as_mapping; /* More standard operations (here for binary compatibility) */ hashfunc tp_hash; ternaryfunc tp_call; reprfunc tp_str; getattrofunc tp_getattro; setattrofunc tp_setattro; /* Functions to access object as input/output buffer */ PyBufferProcs *tp_as_buffer; /* Flags to define presence of optional/expanded features */ unsigned long tp_flags; const char *tp_doc; /* Documentation string */ /* Assigned meaning in release 2.0 */ /* call function for all accessible objects */ traverseproc tp_traverse; /* delete references to contained objects */ inquiry tp_clear; /* Assigned meaning in release 2.1 */ /* rich comparisons */ richcmpfunc tp_richcompare; /* weak reference enabler */ Py_ssize_t tp_weaklistoffset; /* Iterators */ getiterfunc tp_iter; iternextfunc tp_iternext; /* Attribute descriptor and subclassing stuff */ struct PyMethodDef *tp_methods; struct PyMemberDef *tp_members; struct PyGetSetDef *tp_getset; struct _typeobject *tp_base; PyObject *tp_dict; descrgetfunc tp_descr_get; descrsetfunc tp_descr_set; Py_ssize_t tp_dictoffset; initproc tp_init; allocfunc tp_alloc; newfunc tp_new; freefunc tp_free; /* Low-level free-memory routine */ inquiry tp_is_gc; /* For PyObject_IS_GC */ PyObject *tp_bases; PyObject *tp_mro; /* method resolution order */ PyObject *tp_cache; PyObject *tp_subclasses; PyObject *tp_weaklist; destructor tp_del; /* Type attribute cache version tag. Added in version 2.6 */ unsigned int tp_version_tag; destructor tp_finalize; #ifdef COUNT_ALLOCS /* these must be last and never explicitly initialized */ Py_ssize_t tp_allocs; Py_ssize_t tp_frees; Py_ssize_t tp_maxalloc; struct _typeobject *tp_prev; struct _typeobject *tp_next; #endif } PyTypeObject; #endif ``` # Design of CPython’s Compiler `2017-08-25 12:30:51` In CPython, the compilation from source code to bytecode involves several steps: * Parse source code into a parse tree (Parser/pgen.c) * Transform parse tree into an Abstract Syntax Tree (Python/ast.c) * Transform AST into a Control Flow Graph (Python/compile.c) * Emit bytecode based on the Control Flow Graph (Python/compile.c) ## Important Files * Parser/ * Python.asdl ASDL syntax file * asdl.py Parser for ASDL definition files. Reads in an ASDL description and parses it into an AST that describes it. * asdl_c.py “Generate C code from an ASDL description.” Generates Python/Python-ast.c and Include/Python-ast.h. * Python/ * Python-ast.c Creates C structs corresponding to the ASDL types. Also contains code for marshalling AST nodes (core ASDL types have marshalling code in asdl.c). “File automatically generated by Parser/asdl_c.py”. This file must be committed separately after every grammar change is committed since the __version__ value is set to the latest grammar change revision number. * asdl.c Contains code to handle the ASDL sequence type. Also has code to handle marshalling the core ASDL types, such as number and identifier. Used by Python-ast.c for marshalling AST nodes. * ast.c Converts Python’s parse tree into the abstract syntax tree. * ceval.c Executes byte code (aka, eval loop). * compile.c Emits bytecode based on the AST. * symtable.c Generates a symbol table from AST. * pyarena.c Implementation of the arena memory manager. * import.c Home of the magic number (named MAGIC) for bytecode versioning * Include/ * Python-ast.h Contains the actual definitions of the C structs as generated by Python/Python-ast.c. “Automatically generated by Parser/asdl_c.py”. * asdl.h Header for the corresponding Python/ast.c. * ast.h Declares PyAST_FromNode() external (from Python/ast.c). * code.h Header file for Objects/codeobject.c; contains definition of PyCodeObject. * symtable.h Header for Python/symtable.c. struct symtable and PySTEntryObject are defined here. * pyarena.h Header file for the corresponding Python/pyarena.c. * opcode.h Master list of bytecode; if this file is modified you must modify several other files accordingly (see “Introducing New Bytecode”) * Objects/ * codeobject.c Contains PyCodeObject-related code (originally in Python/compile.c). * Lib/ * opcode.py One of the files that must be modified if Include/opcode.h is. ## Ref https://docs.python.org/devguide/compiler.html http://blog.csdn.net/zhsenl/article/category/2307109/1 https://leanpub.com/insidethepythonvirtualmachine/read#leanpub-auto-the-block-stack