├── tests ├── __init__.py ├── vmtest.py ├── test_exceptions.py ├── test_with.py ├── test_functions.py └── test_basic.py ├── byterun ├── __init__.py ├── __main__.py ├── execfile.py ├── pyobj.py └── pyvm2.py ├── requirements.txt ├── .gitignore ├── AUTHORS ├── .treerc ├── .coveragerc ├── README.rst ├── setup.py ├── tox.ini ├── CONTRIBUTE.rst └── LICENSE /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /byterun/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tox 2 | six>=1.4 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .coverage 3 | htmlcov 4 | .tox 5 | MANIFEST 6 | *.egg-info 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Paul Swartz 2 | Ned Batchelder 3 | Allison Kaptur 4 | Laura Lindzey 5 | -------------------------------------------------------------------------------- /.treerc: -------------------------------------------------------------------------------- 1 | [default] 2 | ignore = 3 | *.pyc 4 | __pycache__ 5 | .tox 6 | htmlcov 7 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = true 3 | source = . 4 | omit = .tox/* 5 | 6 | [report] 7 | partial_branches = 8 | (?i)# *pragma[: ]*no *branch 9 | elif PY3: 10 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Byterun 2 | ------- 3 | 4 | This is a pure-Python implementation of a Python bytecode execution virtual 5 | machine. I started it to get a better understanding of bytecodes so I could 6 | fix branch coverage bugs in coverage.py. 7 | 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup 4 | 5 | setup( 6 | name='Byterun', 7 | version='1.0', 8 | description='Pure-Python Python bytecode execution', 9 | author='Ned Batchelder', 10 | author_email='ned@nedbatchelder.com', 11 | url='http://github.com/nedbat/byterun', 12 | packages=['byterun'], 13 | install_requires=['six'], 14 | ) 15 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # Tox (http://tox.testrun.org/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | envlist = py27, py33 8 | 9 | [testenv] 10 | commands = 11 | nosetests {posargs} 12 | 13 | deps = 14 | nose 15 | coverage 16 | -------------------------------------------------------------------------------- /CONTRIBUTE.rst: -------------------------------------------------------------------------------- 1 | Finding areas to contribute 2 | --------------------------- 3 | 4 | Eventually, all the CPython tests should pass under byterun. You can add 5 | CPython tests until some of them fail, and then you've found a bug! 6 | 7 | Tests 8 | ----- 9 | 10 | Byterun uses tox to run the test suite under both Python 2.7 and Python 3.3. 11 | Tox will create a virtualenv for each Python verison. Here are some useful tox 12 | + nosetests commands. 13 | 14 | General construction:: 15 | 16 | tox [args to tox] -- [args to nosetests] 17 | 18 | Running only one version of Python with tox:: 19 | 20 | tox -e py27 21 | tox -e py33 22 | 23 | Running one test with tox & nosetests:: 24 | 25 | tox -- tests.test_file:TestClass.test_name 26 | 27 | Pass the -s flag to make nosetests not capture stdout. Note that because of 28 | the byterun tests' structure, this will make the test fail even if it would 29 | otherwise pass. 30 | 31 | :: 32 | 33 | tox -e py33 -- -s tests.test_file:TestClass.test_name 34 | -------------------------------------------------------------------------------- /byterun/__main__.py: -------------------------------------------------------------------------------- 1 | """A main program for Byterun.""" 2 | 3 | import argparse 4 | import logging 5 | 6 | from . import execfile 7 | 8 | parser = argparse.ArgumentParser( 9 | prog="byterun", 10 | description="Run Python programs with a Python bytecode interpreter.", 11 | ) 12 | parser.add_argument( 13 | '-m', dest='module', action='store_true', 14 | help="prog is a module name, not a file name.", 15 | ) 16 | parser.add_argument( 17 | '-v', '--verbose', dest='verbose', action='store_true', 18 | help="trace the execution of the bytecode.", 19 | ) 20 | parser.add_argument( 21 | 'prog', 22 | help="The program to run.", 23 | ) 24 | parser.add_argument( 25 | 'args', nargs=argparse.REMAINDER, 26 | help="Arguments to pass to the program.", 27 | ) 28 | args = parser.parse_args() 29 | 30 | if args.module: 31 | run_fn = execfile.run_python_module 32 | else: 33 | run_fn = execfile.run_python_file 34 | 35 | level = logging.DEBUG if args.verbose else logging.WARNING 36 | logging.basicConfig(level=level) 37 | 38 | argv = [args.prog] + args.args 39 | run_fn(args.prog, argv) 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Ned Batchelder 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /tests/vmtest.py: -------------------------------------------------------------------------------- 1 | """Testing tools for byterun.""" 2 | 3 | from __future__ import print_function 4 | 5 | import dis 6 | import sys 7 | import textwrap 8 | import types 9 | import unittest 10 | 11 | import six 12 | 13 | from byterun.pyvm2 import VirtualMachine, VirtualMachineError 14 | 15 | # Make this false if you need to run the debugger inside a test. 16 | CAPTURE_STDOUT = ('-s' not in sys.argv) 17 | # Make this false to see the traceback from a failure inside pyvm2. 18 | CAPTURE_EXCEPTION = 1 19 | 20 | 21 | def dis_code(code): 22 | """Disassemble `code` and all the code it refers to.""" 23 | for const in code.co_consts: 24 | if isinstance(const, types.CodeType): 25 | dis_code(const) 26 | 27 | print("") 28 | print(code) 29 | dis.dis(code) 30 | 31 | 32 | class VmTestCase(unittest.TestCase): 33 | 34 | def assert_ok(self, code, raises=None): 35 | """Run `code` in our VM and in real Python: they behave the same.""" 36 | 37 | code = textwrap.dedent(code) 38 | code = compile(code, "<%s>" % self.id(), "exec", 0, 1) 39 | 40 | # Print the disassembly so we'll see it if the test fails. 41 | dis_code(code) 42 | 43 | real_stdout = sys.stdout 44 | 45 | # Run the code through our VM. 46 | 47 | vm_stdout = six.StringIO() 48 | if CAPTURE_STDOUT: # pragma: no branch 49 | sys.stdout = vm_stdout 50 | vm = VirtualMachine() 51 | 52 | vm_value = vm_exc = None 53 | try: 54 | vm_value = vm.run_code(code) 55 | except VirtualMachineError: # pragma: no cover 56 | # If the VM code raises an error, show it. 57 | raise 58 | except AssertionError: # pragma: no cover 59 | # If test code fails an assert, show it. 60 | raise 61 | except Exception as e: 62 | # Otherwise, keep the exception for comparison later. 63 | if not CAPTURE_EXCEPTION: # pragma: no cover 64 | raise 65 | vm_exc = e 66 | finally: 67 | real_stdout.write("-- stdout ----------\n") 68 | real_stdout.write(vm_stdout.getvalue()) 69 | 70 | # Run the code through the real Python interpreter, for comparison. 71 | 72 | py_stdout = six.StringIO() 73 | sys.stdout = py_stdout 74 | 75 | py_value = py_exc = None 76 | globs = {} 77 | try: 78 | py_value = eval(code, globs, globs) 79 | except AssertionError: # pragma: no cover 80 | raise 81 | except Exception as e: 82 | py_exc = e 83 | 84 | sys.stdout = real_stdout 85 | 86 | self.assert_same_exception(vm_exc, py_exc) 87 | self.assertEqual(vm_stdout.getvalue(), py_stdout.getvalue()) 88 | self.assertEqual(vm_value, py_value) 89 | if raises: 90 | self.assertIsInstance(vm_exc, raises) 91 | else: 92 | self.assertIsNone(vm_exc) 93 | 94 | def assert_same_exception(self, e1, e2): 95 | """Exceptions don't implement __eq__, check it ourselves.""" 96 | self.assertEqual(str(e1), str(e2)) 97 | self.assertIs(type(e1), type(e2)) 98 | -------------------------------------------------------------------------------- /tests/test_exceptions.py: -------------------------------------------------------------------------------- 1 | """Test exceptions for Byterun.""" 2 | 3 | from __future__ import print_function 4 | from . import vmtest 5 | 6 | import six 7 | 8 | PY3, PY2 = six.PY3, not six.PY3 9 | 10 | 11 | class TestExceptions(vmtest.VmTestCase): 12 | def test_catching_exceptions(self): 13 | # Catch the exception precisely 14 | self.assert_ok("""\ 15 | try: 16 | [][1] 17 | print("Shouldn't be here...") 18 | except IndexError: 19 | print("caught it!") 20 | """) 21 | # Catch the exception by a parent class 22 | self.assert_ok("""\ 23 | try: 24 | [][1] 25 | print("Shouldn't be here...") 26 | except Exception: 27 | print("caught it!") 28 | """) 29 | # Catch all exceptions 30 | self.assert_ok("""\ 31 | try: 32 | [][1] 33 | print("Shouldn't be here...") 34 | except: 35 | print("caught it!") 36 | """) 37 | 38 | def test_raise_exception(self): 39 | self.assert_ok("raise Exception('oops')", raises=Exception) 40 | 41 | def test_raise_exception_class(self): 42 | self.assert_ok("raise ValueError", raises=ValueError) 43 | 44 | if PY2: 45 | def test_raise_exception_2args(self): 46 | self.assert_ok("raise ValueError, 'bad'", raises=ValueError) 47 | 48 | def test_raise_exception_3args(self): 49 | self.assert_ok("""\ 50 | from sys import exc_info 51 | try: 52 | raise Exception 53 | except: 54 | _, _, tb = exc_info() 55 | raise ValueError, "message", tb 56 | """, raises=ValueError) 57 | 58 | def test_raise_and_catch_exception(self): 59 | self.assert_ok("""\ 60 | try: 61 | raise ValueError("oops") 62 | except ValueError as e: 63 | print("Caught: %s" % e) 64 | print("All done") 65 | """) 66 | 67 | if PY3: 68 | def test_raise_exception_from(self): 69 | self.assert_ok( 70 | "raise ValueError from NameError", 71 | raises=ValueError 72 | ) 73 | 74 | def test_raise_and_catch_exception_in_function(self): 75 | self.assert_ok("""\ 76 | def fn(): 77 | raise ValueError("oops") 78 | 79 | try: 80 | fn() 81 | except ValueError as e: 82 | print("Caught: %s" % e) 83 | print("done") 84 | """) 85 | 86 | def test_global_name_error(self): 87 | self.assert_ok("fooey", raises=NameError) 88 | self.assert_ok("""\ 89 | try: 90 | fooey 91 | print("Yes fooey?") 92 | except NameError: 93 | print("No fooey") 94 | """) 95 | 96 | def test_local_name_error(self): 97 | self.assert_ok("""\ 98 | def fn(): 99 | fooey 100 | fn() 101 | """, raises=NameError) 102 | 103 | def test_catch_local_name_error(self): 104 | self.assert_ok("""\ 105 | def fn(): 106 | try: 107 | fooey 108 | print("Yes fooey?") 109 | except NameError: 110 | print("No fooey") 111 | fn() 112 | """) 113 | 114 | def test_reraise(self): 115 | self.assert_ok("""\ 116 | def fn(): 117 | try: 118 | fooey 119 | print("Yes fooey?") 120 | except NameError: 121 | print("No fooey") 122 | raise 123 | fn() 124 | """, raises=NameError) 125 | 126 | def test_reraise_explicit_exception(self): 127 | self.assert_ok("""\ 128 | def fn(): 129 | try: 130 | raise ValueError("ouch") 131 | except ValueError as e: 132 | print("Caught %s" % e) 133 | raise 134 | fn() 135 | """, raises=ValueError) 136 | 137 | def test_finally_while_throwing(self): 138 | self.assert_ok("""\ 139 | def fn(): 140 | try: 141 | print("About to..") 142 | raise ValueError("ouch") 143 | finally: 144 | print("Finally") 145 | fn() 146 | print("Done") 147 | """, raises=ValueError) 148 | 149 | def test_coverage_issue_92(self): 150 | self.assert_ok("""\ 151 | l = [] 152 | for i in range(3): 153 | try: 154 | l.append(i) 155 | finally: 156 | l.append('f') 157 | l.append('e') 158 | l.append('r') 159 | print(l) 160 | assert l == [0, 'f', 'e', 1, 'f', 'e', 2, 'f', 'e', 'r'] 161 | """) 162 | -------------------------------------------------------------------------------- /byterun/execfile.py: -------------------------------------------------------------------------------- 1 | """Execute files of Python code.""" 2 | 3 | import imp 4 | import os 5 | import sys 6 | import tokenize 7 | 8 | from .pyvm2 import VirtualMachine 9 | 10 | 11 | # This code is ripped off from coverage.py. Define things it expects. 12 | try: 13 | open_source = tokenize.open # pylint: disable=E1101 14 | except: 15 | def open_source(fname): 16 | """Open a source file the best way.""" 17 | return open(fname, "rU") 18 | 19 | NoSource = Exception 20 | 21 | 22 | def exec_code_object(code, env): 23 | vm = VirtualMachine() 24 | vm.run_code(code, f_globals=env) 25 | 26 | 27 | # from coverage.py: 28 | 29 | try: 30 | # In Py 2.x, the builtins were in __builtin__ 31 | BUILTINS = sys.modules['__builtin__'] 32 | except KeyError: 33 | # In Py 3.x, they're in builtins 34 | BUILTINS = sys.modules['builtins'] 35 | 36 | 37 | def rsplit1(s, sep): 38 | """The same as s.rsplit(sep, 1), but works in 2.3""" 39 | parts = s.split(sep) 40 | return sep.join(parts[:-1]), parts[-1] 41 | 42 | 43 | def run_python_module(modulename, args): 44 | """Run a python module, as though with ``python -m name args...``. 45 | 46 | `modulename` is the name of the module, possibly a dot-separated name. 47 | `args` is the argument array to present as sys.argv, including the first 48 | element naming the module being executed. 49 | 50 | """ 51 | openfile = None 52 | glo, loc = globals(), locals() 53 | try: 54 | try: 55 | # Search for the module - inside its parent package, if any - using 56 | # standard import mechanics. 57 | if '.' in modulename: 58 | packagename, name = rsplit1(modulename, '.') 59 | package = __import__(packagename, glo, loc, ['__path__']) 60 | searchpath = package.__path__ 61 | else: 62 | packagename, name = None, modulename 63 | searchpath = None # "top-level search" in imp.find_module() 64 | openfile, pathname, _ = imp.find_module(name, searchpath) 65 | 66 | # Complain if this is a magic non-file module. 67 | if openfile is None and pathname is None: 68 | raise NoSource( 69 | "module does not live in a file: %r" % modulename 70 | ) 71 | 72 | # If `modulename` is actually a package, not a mere module, then we 73 | # pretend to be Python 2.7 and try running its __main__.py script. 74 | if openfile is None: 75 | packagename = modulename 76 | name = '__main__' 77 | package = __import__(packagename, glo, loc, ['__path__']) 78 | searchpath = package.__path__ 79 | openfile, pathname, _ = imp.find_module(name, searchpath) 80 | except ImportError: 81 | _, err, _ = sys.exc_info() 82 | raise NoSource(str(err)) 83 | finally: 84 | if openfile: 85 | openfile.close() 86 | 87 | # Finally, hand the file off to run_python_file for execution. 88 | args[0] = pathname 89 | run_python_file(pathname, args, package=packagename) 90 | 91 | 92 | def run_python_file(filename, args, package=None): 93 | """Run a python file as if it were the main program on the command line. 94 | 95 | `filename` is the path to the file to execute, it need not be a .py file. 96 | `args` is the argument array to present as sys.argv, including the first 97 | element naming the file being executed. `package` is the name of the 98 | enclosing package, if any. 99 | 100 | """ 101 | # Create a module to serve as __main__ 102 | old_main_mod = sys.modules['__main__'] 103 | main_mod = imp.new_module('__main__') 104 | sys.modules['__main__'] = main_mod 105 | main_mod.__file__ = filename 106 | if package: 107 | main_mod.__package__ = package 108 | main_mod.__builtins__ = BUILTINS 109 | 110 | # Set sys.argv and the first path element properly. 111 | old_argv = sys.argv 112 | old_path0 = sys.path[0] 113 | sys.argv = args 114 | if package: 115 | sys.path[0] = '' 116 | else: 117 | sys.path[0] = os.path.abspath(os.path.dirname(filename)) 118 | 119 | try: 120 | # Open the source file. 121 | try: 122 | source_file = open_source(filename) 123 | except IOError: 124 | raise NoSource("No file to run: %r" % filename) 125 | 126 | try: 127 | source = source_file.read() 128 | finally: 129 | source_file.close() 130 | 131 | # We have the source. `compile` still needs the last line to be clean, 132 | # so make sure it is, then compile a code object from it. 133 | if not source or source[-1] != '\n': 134 | source += '\n' 135 | code = compile(source, filename, "exec") 136 | 137 | # Execute the source file. 138 | exec_code_object(code, main_mod.__dict__) 139 | finally: 140 | # Restore the old __main__ 141 | sys.modules['__main__'] = old_main_mod 142 | 143 | # Restore the old argv and path 144 | sys.argv = old_argv 145 | sys.path[0] = old_path0 146 | -------------------------------------------------------------------------------- /byterun/pyobj.py: -------------------------------------------------------------------------------- 1 | """Implementations of Python fundamental objects for Byterun.""" 2 | 3 | import collections 4 | import inspect 5 | import types 6 | 7 | import six 8 | 9 | PY3, PY2 = six.PY3, not six.PY3 10 | 11 | 12 | def make_cell(value): 13 | # Thanks to Alex Gaynor for help with this bit of twistiness. 14 | # Construct an actual cell object by creating a closure right here, 15 | # and grabbing the cell object out of the function we create. 16 | fn = (lambda x: lambda: x)(value) 17 | if PY3: 18 | return fn.__closure__[0] 19 | else: 20 | return fn.func_closure[0] 21 | 22 | 23 | class Function(object): 24 | __slots__ = [ 25 | 'func_code', 'func_name', 'func_defaults', 'func_globals', 26 | 'func_locals', 'func_dict', 'func_closure', 27 | '__name__', '__dict__', '__doc__', 28 | '_vm', '_func', 29 | ] 30 | 31 | def __init__(self, name, code, globs, defaults, closure, vm): 32 | self._vm = vm 33 | self.func_code = code 34 | self.func_name = self.__name__ = name or code.co_name 35 | self.func_defaults = tuple(defaults) 36 | self.func_globals = globs 37 | self.func_locals = self._vm.frame.f_locals 38 | self.__dict__ = {} 39 | self.func_closure = closure 40 | self.__doc__ = code.co_consts[0] if code.co_consts else None 41 | 42 | # Sometimes, we need a real Python function. This is for that. 43 | kw = { 44 | 'argdefs': self.func_defaults, 45 | } 46 | if closure: 47 | kw['closure'] = tuple(make_cell(0) for _ in closure) 48 | self._func = types.FunctionType(code, globs, **kw) 49 | 50 | def __repr__(self): # pragma: no cover 51 | return '' % ( 52 | self.func_name, id(self) 53 | ) 54 | 55 | def __get__(self, instance, owner): 56 | if instance is not None: 57 | return Method(instance, owner, self) 58 | if PY2: 59 | return Method(None, owner, self) 60 | else: 61 | return self 62 | 63 | def __call__(self, *args, **kwargs): 64 | if PY2 and self.func_name in ["", "", ""]: 65 | # D'oh! http://bugs.python.org/issue19611 Py2 doesn't know how to 66 | # inspect set comprehensions, dict comprehensions, or generator 67 | # expressions properly. They are always functions of one argument, 68 | # so just do the right thing. 69 | assert len(args) == 1 and not kwargs, "Surprising comprehension!" 70 | callargs = {".0": args[0]} 71 | else: 72 | callargs = inspect.getcallargs(self._func, *args, **kwargs) 73 | frame = self._vm.make_frame( 74 | self.func_code, callargs, self.func_globals, {} 75 | ) 76 | CO_GENERATOR = 32 # flag for "this code uses yield" 77 | if self.func_code.co_flags & CO_GENERATOR: 78 | gen = Generator(frame, self._vm) 79 | frame.generator = gen 80 | retval = gen 81 | else: 82 | retval = self._vm.run_frame(frame) 83 | return retval 84 | 85 | class Method(object): 86 | def __init__(self, obj, _class, func): 87 | self.im_self = obj 88 | self.im_class = _class 89 | self.im_func = func 90 | 91 | def __repr__(self): # pragma: no cover 92 | name = "%s.%s" % (self.im_class.__name__, self.im_func.func_name) 93 | if self.im_self is not None: 94 | return '' % (name, self.im_self) 95 | else: 96 | return '' % (name,) 97 | 98 | def __call__(self, *args, **kwargs): 99 | if self.im_self is not None: 100 | return self.im_func(self.im_self, *args, **kwargs) 101 | else: 102 | return self.im_func(*args, **kwargs) 103 | 104 | 105 | class Cell(object): 106 | """A fake cell for closures. 107 | 108 | Closures keep names in scope by storing them not in a frame, but in a 109 | separate object called a cell. Frames share references to cells, and 110 | the LOAD_DEREF and STORE_DEREF opcodes get and set the value from cells. 111 | 112 | This class acts as a cell, though it has to jump through two hoops to make 113 | the simulation complete: 114 | 115 | 1. In order to create actual FunctionType functions, we have to have 116 | actual cell objects, which are difficult to make. See the twisty 117 | double-lambda in __init__. 118 | 119 | 2. Actual cell objects can't be modified, so to implement STORE_DEREF, 120 | we store a one-element list in our cell, and then use [0] as the 121 | actual value. 122 | 123 | """ 124 | def __init__(self, value): 125 | self.contents = value 126 | 127 | def get(self): 128 | return self.contents 129 | 130 | def set(self, value): 131 | self.contents = value 132 | 133 | 134 | Block = collections.namedtuple("Block", "type, handler, level") 135 | 136 | 137 | class Frame(object): 138 | def __init__(self, f_code, f_globals, f_locals, f_back): 139 | self.f_code = f_code 140 | self.f_globals = f_globals 141 | self.f_locals = f_locals 142 | self.f_back = f_back 143 | self.stack = [] 144 | if f_back and f_back.f_globals is f_globals: 145 | # If we share the globals, we share the builtins. 146 | self.f_builtins = f_back.f_builtins 147 | else: 148 | try: 149 | self.f_builtins = f_globals['__builtins__'] 150 | if hasattr(self.f_builtins, '__dict__'): 151 | self.f_builtins = self.f_builtins.__dict__ 152 | except KeyError: 153 | # No builtins! Make up a minimal one with None. 154 | self.f_builtins = {'None': None} 155 | 156 | self.f_lineno = f_code.co_firstlineno 157 | self.f_lasti = 0 158 | 159 | if f_code.co_cellvars: 160 | self.cells = {} 161 | if not f_back.cells: 162 | f_back.cells = {} 163 | for var in f_code.co_cellvars: 164 | # Make a cell for the variable in our locals, or None. 165 | cell = Cell(self.f_locals.get(var)) 166 | f_back.cells[var] = self.cells[var] = cell 167 | else: 168 | self.cells = None 169 | 170 | if f_code.co_freevars: 171 | if not self.cells: 172 | self.cells = {} 173 | for var in f_code.co_freevars: 174 | assert self.cells is not None 175 | assert f_back.cells, "f_back.cells: %r" % (f_back.cells,) 176 | self.cells[var] = f_back.cells[var] 177 | 178 | self.block_stack = [] 179 | self.generator = None 180 | 181 | def __repr__(self): # pragma: no cover 182 | return '' % ( 183 | id(self), self.f_code.co_filename, self.f_lineno 184 | ) 185 | 186 | def line_number(self): 187 | """Get the current line number the frame is executing.""" 188 | # We don't keep f_lineno up to date, so calculate it based on the 189 | # instruction address and the line number table. 190 | lnotab = self.f_code.co_lnotab 191 | byte_increments = six.iterbytes(lnotab[0::2]) 192 | line_increments = six.iterbytes(lnotab[1::2]) 193 | 194 | byte_num = 0 195 | line_num = self.f_code.co_firstlineno 196 | 197 | for byte_incr, line_incr in zip(byte_increments, line_increments): 198 | byte_num += byte_incr 199 | if byte_num > self.f_lasti: 200 | break 201 | line_num += line_incr 202 | 203 | return line_num 204 | 205 | 206 | class Generator(object): 207 | def __init__(self, g_frame, vm): 208 | self.gi_frame = g_frame 209 | self.vm = vm 210 | self.started = False 211 | self.finished = False 212 | 213 | def __iter__(self): 214 | return self 215 | 216 | def next(self): 217 | return self.send(None) 218 | 219 | def send(self, value=None): 220 | if not self.started and value is not None: 221 | raise TypeError("Can't send non-None value to a just-started generator") 222 | self.gi_frame.stack.append(value) 223 | self.started = True 224 | val = self.vm.resume_frame(self.gi_frame) 225 | if self.finished: 226 | raise StopIteration(val) 227 | return val 228 | 229 | __next__ = next 230 | -------------------------------------------------------------------------------- /tests/test_with.py: -------------------------------------------------------------------------------- 1 | """Test the with statement for Byterun.""" 2 | 3 | from __future__ import print_function 4 | from . import vmtest 5 | 6 | import six 7 | PY3 = six.PY3 8 | 9 | class TestWithStatement(vmtest.VmTestCase): 10 | 11 | def test_simple_context_manager(self): 12 | self.assert_ok("""\ 13 | class NullContext(object): 14 | def __enter__(self): 15 | l.append('i') 16 | # __enter__ usually returns self, but doesn't have to. 17 | return 17 18 | 19 | def __exit__(self, exc_type, exc_val, exc_tb): 20 | l.append('o') 21 | return False 22 | 23 | l = [] 24 | for i in range(3): 25 | with NullContext() as val: 26 | assert val == 17 27 | l.append('w') 28 | l.append('e') 29 | l.append('r') 30 | s = ''.join(l) 31 | print("Look: %r" % s) 32 | assert s == "iwoeiwoeiwoer" 33 | """) 34 | 35 | def test_raise_in_context_manager(self): 36 | self.assert_ok("""\ 37 | class NullContext(object): 38 | def __enter__(self): 39 | l.append('i') 40 | return self 41 | 42 | def __exit__(self, exc_type, exc_val, exc_tb): 43 | assert exc_type is ValueError, \\ 44 | "Expected ValueError: %r" % exc_type 45 | l.append('o') 46 | return False 47 | 48 | l = [] 49 | try: 50 | with NullContext(): 51 | l.append('w') 52 | raise ValueError("Boo!") 53 | l.append('e') 54 | except ValueError: 55 | l.append('x') 56 | l.append('r') 57 | s = ''.join(l) 58 | print("Look: %r" % s) 59 | assert s == "iwoxr" 60 | """) 61 | 62 | def test_suppressed_raise_in_context_manager(self): 63 | self.assert_ok("""\ 64 | class SuppressingContext(object): 65 | def __enter__(self): 66 | l.append('i') 67 | return self 68 | 69 | def __exit__(self, exc_type, exc_val, exc_tb): 70 | assert exc_type is ValueError, \\ 71 | "Expected ValueError: %r" % exc_type 72 | l.append('o') 73 | return True 74 | 75 | l = [] 76 | try: 77 | with SuppressingContext(): 78 | l.append('w') 79 | raise ValueError("Boo!") 80 | l.append('e') 81 | except ValueError: 82 | l.append('x') 83 | l.append('r') 84 | s = ''.join(l) 85 | print("Look: %r" % s) 86 | assert s == "iwoer" 87 | """) 88 | 89 | def test_return_in_with(self): 90 | self.assert_ok("""\ 91 | class NullContext(object): 92 | def __enter__(self): 93 | l.append('i') 94 | return self 95 | 96 | def __exit__(self, exc_type, exc_val, exc_tb): 97 | l.append('o') 98 | return False 99 | 100 | l = [] 101 | def use_with(val): 102 | with NullContext(): 103 | l.append('w') 104 | return val 105 | l.append('e') 106 | 107 | assert use_with(23) == 23 108 | l.append('r') 109 | s = ''.join(l) 110 | print("Look: %r" % s) 111 | assert s == "iwor" 112 | """) 113 | 114 | def test_continue_in_with(self): 115 | self.assert_ok("""\ 116 | class NullContext(object): 117 | def __enter__(self): 118 | l.append('i') 119 | return self 120 | 121 | def __exit__(self, exc_type, exc_val, exc_tb): 122 | l.append('o') 123 | return False 124 | 125 | l = [] 126 | for i in range(3): 127 | with NullContext(): 128 | l.append('w') 129 | if i % 2: 130 | continue 131 | l.append('z') 132 | l.append('e') 133 | 134 | l.append('r') 135 | s = ''.join(l) 136 | print("Look: %r" % s) 137 | assert s == "iwzoeiwoiwzoer" 138 | """) 139 | 140 | def test_break_in_with(self): 141 | self.assert_ok("""\ 142 | class NullContext(object): 143 | def __enter__(self): 144 | l.append('i') 145 | return self 146 | 147 | def __exit__(self, exc_type, exc_val, exc_tb): 148 | l.append('o') 149 | return False 150 | 151 | l = [] 152 | for i in range(3): 153 | with NullContext(): 154 | l.append('w') 155 | if i % 2: 156 | break 157 | l.append('z') 158 | l.append('e') 159 | 160 | l.append('r') 161 | s = ''.join(l) 162 | print("Look: %r" % s) 163 | assert s == "iwzoeiwor" 164 | """) 165 | 166 | def test_raise_in_with(self): 167 | self.assert_ok("""\ 168 | class NullContext(object): 169 | def __enter__(self): 170 | l.append('i') 171 | return self 172 | 173 | def __exit__(self, exc_type, exc_val, exc_tb): 174 | l.append('o') 175 | return False 176 | 177 | l = [] 178 | try: 179 | with NullContext(): 180 | l.append('w') 181 | raise ValueError("oops") 182 | l.append('z') 183 | l.append('e') 184 | except ValueError as e: 185 | assert str(e) == "oops" 186 | l.append('x') 187 | l.append('r') 188 | s = ''.join(l) 189 | print("Look: %r" % s) 190 | assert s == "iwoxr", "What!?" 191 | """) 192 | 193 | def test_at_context_manager_simplified(self): 194 | self.assert_ok("""\ 195 | class GeneratorContextManager(object): 196 | def __init__(self, gen): 197 | self.gen = gen 198 | 199 | def __enter__(self): 200 | try: 201 | return next(self.gen) 202 | except StopIteration: 203 | raise RuntimeError("generator didn't yield") 204 | 205 | def __exit__(self, type, value, traceback): 206 | if type is None: 207 | try: 208 | next(self.gen) 209 | except StopIteration: 210 | return 211 | else: 212 | raise RuntimeError("generator didn't stop") 213 | else: 214 | if value is None: 215 | value = type() 216 | try: 217 | self.gen.throw(type, value, traceback) 218 | raise RuntimeError( 219 | "generator didn't stop after throw()" 220 | ) 221 | except StopIteration as exc: 222 | return exc is not value 223 | except: 224 | if sys.exc_info()[1] is not value: 225 | raise 226 | 227 | def contextmanager(func): 228 | def helper(*args, **kwds): 229 | return GeneratorContextManager(func(*args, **kwds)) 230 | return helper 231 | 232 | @contextmanager 233 | def my_context_manager(val): 234 | yield val 235 | 236 | with my_context_manager(17) as x: 237 | assert x == 17 238 | """) 239 | 240 | def test_at_context_manager_complete(self): 241 | # The complete code for an @contextmanager example, lifted from 242 | # the stdlib. 243 | self.assert_ok("""\ 244 | from _functools import partial 245 | 246 | WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') 247 | WRAPPER_UPDATES = ('__dict__',) 248 | 249 | def update_wrapper(wrapper, 250 | wrapped, 251 | assigned = WRAPPER_ASSIGNMENTS, 252 | updated = WRAPPER_UPDATES): 253 | for attr in assigned: 254 | setattr(wrapper, attr, getattr(wrapped, attr)) 255 | for attr in updated: 256 | getattr(wrapper, attr).update(getattr(wrapped, attr, {})) 257 | # Return the wrapper so this can be used as a decorator 258 | # via partial(). 259 | return wrapper 260 | 261 | def wraps(wrapped, 262 | assigned = WRAPPER_ASSIGNMENTS, 263 | updated = WRAPPER_UPDATES): 264 | return partial(update_wrapper, wrapped=wrapped, 265 | assigned=assigned, updated=updated) 266 | 267 | class GeneratorContextManager(object): 268 | def __init__(self, gen): 269 | self.gen = gen 270 | 271 | def __enter__(self): 272 | try: 273 | return next(self.gen) 274 | except StopIteration: 275 | raise RuntimeError("generator didn't yield") 276 | 277 | def __exit__(self, type, value, traceback): 278 | if type is None: 279 | try: 280 | next(self.gen) 281 | except StopIteration: 282 | return 283 | else: 284 | raise RuntimeError("generator didn't stop") 285 | else: 286 | if value is None: 287 | value = type() 288 | try: 289 | self.gen.throw(type, value, traceback) 290 | raise RuntimeError( 291 | "generator didn't stop after throw()" 292 | ) 293 | except StopIteration as exc: 294 | return exc is not value 295 | except: 296 | if sys.exc_info()[1] is not value: 297 | raise 298 | 299 | def contextmanager(func): 300 | @wraps(func) 301 | def helper(*args, **kwds): 302 | return GeneratorContextManager(func(*args, **kwds)) 303 | return helper 304 | 305 | @contextmanager 306 | def my_context_manager(val): 307 | yield val 308 | 309 | with my_context_manager(17) as x: 310 | assert x == 17 311 | """) 312 | 313 | if PY3: 314 | def test_generator_with_context_manager(self): 315 | self.assert_ok("""\ 316 | from contextlib import contextmanager 317 | 318 | def inner(): 319 | yield "I'm inner!" 320 | 321 | def foo(): 322 | yield from inner() 323 | 324 | @contextmanager 325 | def cmgr(): 326 | yield "Context Manager!" 327 | raise StopIteration(cmgr()) 328 | 329 | def main(): 330 | with (yield from foo()) as x: 331 | print(x) 332 | 333 | def run(fn, *args): 334 | x = fn(*args) 335 | while True: 336 | try: 337 | print(next(x)) 338 | except StopIteration as e: 339 | return e.value 340 | run(main) 341 | """) 342 | -------------------------------------------------------------------------------- /tests/test_functions.py: -------------------------------------------------------------------------------- 1 | """Test functions etc, for Byterun.""" 2 | 3 | from __future__ import print_function 4 | from . import vmtest 5 | import six 6 | 7 | PY3 = six.PY3 8 | 9 | 10 | class TestFunctions(vmtest.VmTestCase): 11 | def test_functions(self): 12 | self.assert_ok("""\ 13 | def fn(a, b=17, c="Hello", d=[]): 14 | d.append(99) 15 | print(a, b, c, d) 16 | fn(1) 17 | fn(2, 3) 18 | fn(3, c="Bye") 19 | fn(4, d=["What?"]) 20 | fn(5, "b", "c") 21 | """) 22 | 23 | def test_recursion(self): 24 | self.assert_ok("""\ 25 | def fact(n): 26 | if n <= 1: 27 | return 1 28 | else: 29 | return n * fact(n-1) 30 | f6 = fact(6) 31 | print(f6) 32 | assert f6 == 720 33 | """) 34 | 35 | def test_nested_names(self): 36 | self.assert_ok("""\ 37 | def one(): 38 | x = 1 39 | def two(): 40 | x = 2 41 | print(x) 42 | two() 43 | print(x) 44 | one() 45 | """) 46 | 47 | def test_calling_functions_with_args_kwargs(self): 48 | self.assert_ok("""\ 49 | def fn(a, b=17, c="Hello", d=[]): 50 | d.append(99) 51 | print(a, b, c, d) 52 | fn(6, *[77, 88]) 53 | fn(**{'c': 23, 'a': 7}) 54 | fn(6, *[77], **{'c': 23, 'd': [123]}) 55 | """) 56 | 57 | def test_defining_functions_with_args_kwargs(self): 58 | self.assert_ok("""\ 59 | def fn(*args): 60 | print("args is %r" % (args,)) 61 | fn(1, 2) 62 | """) 63 | self.assert_ok("""\ 64 | def fn(**kwargs): 65 | print("kwargs is %r" % (kwargs,)) 66 | fn(red=True, blue=False) 67 | """) 68 | self.assert_ok("""\ 69 | def fn(*args, **kwargs): 70 | print("args is %r" % (args,)) 71 | print("kwargs is %r" % (kwargs,)) 72 | fn(1, 2, red=True, blue=False) 73 | """) 74 | self.assert_ok("""\ 75 | def fn(x, y, *args, **kwargs): 76 | print("x is %r, y is %r" % (x, y)) 77 | print("args is %r" % (args,)) 78 | print("kwargs is %r" % (kwargs,)) 79 | fn('a', 'b', 1, 2, red=True, blue=False) 80 | """) 81 | 82 | def test_defining_functions_with_empty_args_kwargs(self): 83 | self.assert_ok("""\ 84 | def fn(*args): 85 | print("args is %r" % (args,)) 86 | fn() 87 | """) 88 | self.assert_ok("""\ 89 | def fn(**kwargs): 90 | print("kwargs is %r" % (kwargs,)) 91 | fn() 92 | """) 93 | self.assert_ok("""\ 94 | def fn(*args, **kwargs): 95 | print("args is %r, kwargs is %r" % (args, kwargs)) 96 | fn() 97 | """) 98 | 99 | def test_partial(self): 100 | self.assert_ok("""\ 101 | from _functools import partial 102 | 103 | def f(a,b): 104 | return a-b 105 | 106 | f7 = partial(f, 7) 107 | four = f7(3) 108 | assert four == 4 109 | """) 110 | 111 | def test_partial_with_kwargs(self): 112 | self.assert_ok("""\ 113 | from _functools import partial 114 | 115 | def f(a,b,c=0,d=0): 116 | return (a,b,c,d) 117 | 118 | f7 = partial(f, b=7, c=1) 119 | them = f7(10) 120 | assert them == (10,7,1,0) 121 | """) 122 | 123 | def test_wraps(self): 124 | self.assert_ok("""\ 125 | from functools import wraps 126 | def my_decorator(f): 127 | dec = wraps(f) 128 | def wrapper(*args, **kwds): 129 | print('Calling decorated function') 130 | return f(*args, **kwds) 131 | wrapper = dec(wrapper) 132 | return wrapper 133 | 134 | @my_decorator 135 | def example(): 136 | '''Docstring''' 137 | return 17 138 | 139 | assert example() == 17 140 | """) 141 | 142 | def test_different_globals_may_have_different_builtins(self): 143 | self.assert_ok("""\ 144 | def replace_globals(f, new_globals): 145 | import sys 146 | if sys.version_info.major == 2: 147 | args = [ 148 | f.func_code, 149 | new_globals, 150 | f.func_name, 151 | f.func_defaults, 152 | f.func_closure, 153 | ] 154 | else: 155 | args = [ 156 | f.__code__, 157 | new_globals, 158 | f.__name__, 159 | f.__defaults__, 160 | f.__closure__, 161 | ] 162 | if hasattr(f, '_vm'): 163 | name = args.remove(args[2]) 164 | args.insert(0, name) 165 | args.append(f._vm) 166 | return type(lambda: None)(*args) 167 | 168 | 169 | def f(): 170 | assert g() == 2 171 | assert a == 1 172 | 173 | 174 | def g(): 175 | return a # a is in the builtins and set to 2 176 | 177 | 178 | # g and f have different builtins that both provide ``a``. 179 | g = replace_globals(g, {'__builtins__': {'a': 2}}) 180 | f = replace_globals(f, {'__builtins__': {'a': 1}, 'g': g}) 181 | 182 | 183 | f() 184 | """) 185 | 186 | def test_no_builtins(self): 187 | self.assert_ok("""\ 188 | def replace_globals(f, new_globals): 189 | import sys 190 | 191 | 192 | if sys.version_info.major == 2: 193 | args = [ 194 | f.func_code, 195 | new_globals, 196 | f.func_name, 197 | f.func_defaults, 198 | f.func_closure, 199 | ] 200 | else: 201 | args = [ 202 | f.__code__, 203 | new_globals, 204 | f.__name__, 205 | f.__defaults__, 206 | f.__closure__, 207 | ] 208 | if hasattr(f, '_vm'): 209 | name = args.remove(args[2]) 210 | args.insert(0, name) 211 | args.append(f._vm) 212 | return type(lambda: None)(*args) 213 | 214 | 215 | def f(NameError=NameError, AssertionError=AssertionError): 216 | # capture NameError and AssertionError early because 217 | # we are deleting the builtins 218 | None 219 | try: 220 | sum 221 | except NameError: 222 | pass 223 | else: 224 | raise AssertionError('sum in the builtins') 225 | 226 | 227 | f = replace_globals(f, {}) # no builtins provided 228 | f() 229 | """) 230 | 231 | 232 | class TestClosures(vmtest.VmTestCase): 233 | def test_closures(self): 234 | self.assert_ok("""\ 235 | def make_adder(x): 236 | def add(y): 237 | return x+y 238 | return add 239 | a = make_adder(10) 240 | print(a(7)) 241 | assert a(7) == 17 242 | """) 243 | 244 | def test_closures_store_deref(self): 245 | self.assert_ok("""\ 246 | def make_adder(x): 247 | z = x+1 248 | def add(y): 249 | return x+y+z 250 | return add 251 | a = make_adder(10) 252 | print(a(7)) 253 | assert a(7) == 28 254 | """) 255 | 256 | def test_closures_in_loop(self): 257 | self.assert_ok("""\ 258 | def make_fns(x): 259 | fns = [] 260 | for i in range(x): 261 | fns.append(lambda i=i: i) 262 | return fns 263 | fns = make_fns(3) 264 | for f in fns: 265 | print(f()) 266 | assert (fns[0](), fns[1](), fns[2]()) == (0, 1, 2) 267 | """) 268 | 269 | def test_closures_with_defaults(self): 270 | self.assert_ok("""\ 271 | def make_adder(x, y=13, z=43): 272 | def add(q, r=11): 273 | return x+y+z+q+r 274 | return add 275 | a = make_adder(10, 17) 276 | print(a(7)) 277 | assert a(7) == 88 278 | """) 279 | 280 | def test_deep_closures(self): 281 | self.assert_ok("""\ 282 | def f1(a): 283 | b = 2*a 284 | def f2(c): 285 | d = 2*c 286 | def f3(e): 287 | f = 2*e 288 | def f4(g): 289 | h = 2*g 290 | return a+b+c+d+e+f+g+h 291 | return f4 292 | return f3 293 | return f2 294 | answer = f1(3)(4)(5)(6) 295 | print(answer) 296 | assert answer == 54 297 | """) 298 | 299 | 300 | class TestGenerators(vmtest.VmTestCase): 301 | def test_first(self): 302 | self.assert_ok("""\ 303 | def two(): 304 | yield 1 305 | yield 2 306 | for i in two(): 307 | print(i) 308 | """) 309 | 310 | def test_partial_generator(self): 311 | self.assert_ok("""\ 312 | from _functools import partial 313 | 314 | def f(a,b): 315 | num = a+b 316 | while num: 317 | yield num 318 | num -= 1 319 | 320 | f2 = partial(f, 2) 321 | three = f2(1) 322 | assert list(three) == [3,2,1] 323 | """) 324 | 325 | def test_yield_multiple_values(self): 326 | self.assert_ok("""\ 327 | def triples(): 328 | yield 1, 2, 3 329 | yield 4, 5, 6 330 | 331 | for a, b, c in triples(): 332 | print(a, b, c) 333 | """) 334 | 335 | def test_simple_generator(self): 336 | self.assert_ok("""\ 337 | g = (x for x in [0,1,2]) 338 | print(list(g)) 339 | """) 340 | 341 | def test_generator_from_generator(self): 342 | self.assert_ok("""\ 343 | g = (x*x for x in range(5)) 344 | h = (y+1 for y in g) 345 | print(list(h)) 346 | """) 347 | 348 | def test_generator_from_generator2(self): 349 | self.assert_ok("""\ 350 | class Thing(object): 351 | RESOURCES = ('abc', 'def') 352 | def get_abc(self): 353 | return "ABC" 354 | def get_def(self): 355 | return "DEF" 356 | def resource_info(self): 357 | for name in self.RESOURCES: 358 | get_name = 'get_' + name 359 | yield name, getattr(self, get_name) 360 | 361 | def boom(self): 362 | #d = list((name, get()) for name, get in self.resource_info()) 363 | d = [(name, get()) for name, get in self.resource_info()] 364 | return d 365 | 366 | print(Thing().boom()) 367 | """) 368 | 369 | if PY3: # PY3.3+ only 370 | def test_yield_from(self): 371 | self.assert_ok("""\ 372 | def main(): 373 | x = outer() 374 | next(x) 375 | y = x.send("Hello, World") 376 | print(y) 377 | 378 | def outer(): 379 | yield from inner() 380 | 381 | def inner(): 382 | y = yield 383 | yield y 384 | 385 | main() 386 | """) 387 | 388 | def test_yield_from_tuple(self): 389 | self.assert_ok("""\ 390 | def main(): 391 | for x in outer(): 392 | print(x) 393 | 394 | def outer(): 395 | yield from (1, 2, 3, 4) 396 | 397 | main() 398 | """) 399 | 400 | def test_distinguish_iterators_and_generators(self): 401 | self.assert_ok("""\ 402 | class Foo(object): 403 | def __iter__(self): 404 | return FooIter() 405 | 406 | class FooIter(object): 407 | def __init__(self): 408 | self.state = 0 409 | 410 | def __next__(self): 411 | if self.state >= 10: 412 | raise StopIteration 413 | self.state += 1 414 | return self.state 415 | 416 | def send(self, n): 417 | print("sending") 418 | 419 | def outer(): 420 | yield from Foo() 421 | 422 | for x in outer(): 423 | print(x) 424 | """) 425 | 426 | def test_nested_yield_from(self): 427 | self.assert_ok("""\ 428 | def main(): 429 | x = outer() 430 | next(x) 431 | y = x.send("Hello, World") 432 | print(y) 433 | 434 | def outer(): 435 | yield from middle() 436 | 437 | def middle(): 438 | yield from inner() 439 | 440 | def inner(): 441 | y = yield 442 | yield y 443 | 444 | main() 445 | """) 446 | 447 | def test_return_from_generator(self): 448 | self.assert_ok("""\ 449 | def gen(): 450 | yield 1 451 | return 2 452 | 453 | x = gen() 454 | while True: 455 | try: 456 | print(next(x)) 457 | except StopIteration as e: 458 | print(e.value) 459 | break 460 | """) 461 | 462 | def test_return_from_generator_with_yield_from(self): 463 | self.assert_ok("""\ 464 | def returner(): 465 | if False: 466 | yield 467 | return 1 468 | 469 | def main(): 470 | y = yield from returner() 471 | print(y) 472 | 473 | list(main()) 474 | """) 475 | -------------------------------------------------------------------------------- /tests/test_basic.py: -------------------------------------------------------------------------------- 1 | """Basic tests for Byterun.""" 2 | 3 | from __future__ import print_function 4 | from . import vmtest 5 | 6 | import six 7 | 8 | PY3, PY2 = six.PY3, not six.PY3 9 | 10 | 11 | class TestIt(vmtest.VmTestCase): 12 | def test_constant(self): 13 | self.assert_ok("17") 14 | 15 | def test_globals(self): 16 | self.assert_ok("""\ 17 | global xyz 18 | xyz=2106 19 | 20 | def abc(): 21 | global xyz 22 | xyz+=1 23 | print("Midst:",xyz) 24 | 25 | 26 | print "Pre:",xyz 27 | abc() 28 | print "Post:",xyz 29 | """) 30 | 31 | def test_for_loop(self): 32 | self.assert_ok("""\ 33 | out = "" 34 | for i in range(5): 35 | out = out + str(i) 36 | print(out) 37 | """) 38 | 39 | def test_inplace_operators(self): 40 | self.assert_ok("""\ 41 | x, y = 2, 3 42 | x **= y 43 | assert x == 8 and y == 3 44 | x *= y 45 | assert x == 24 and y == 3 46 | x //= y 47 | assert x == 8 and y == 3 48 | x %= y 49 | assert x == 2 and y == 3 50 | x += y 51 | assert x == 5 and y == 3 52 | x -= y 53 | assert x == 2 and y == 3 54 | x <<= y 55 | assert x == 16 and y == 3 56 | x >>= y 57 | assert x == 2 and y == 3 58 | 59 | x = 0x8F 60 | x &= 0xA5 61 | assert x == 0x85 62 | x |= 0x10 63 | assert x == 0x95 64 | x ^= 0x33 65 | assert x == 0xA6 66 | """) 67 | 68 | if PY2: 69 | def test_inplace_division(self): 70 | self.assert_ok("""\ 71 | x, y = 24, 3 72 | x /= y 73 | assert x == 8 and y == 3 74 | assert isinstance(x, int) 75 | x /= y 76 | assert x == 2 and y == 3 77 | assert isinstance(x, int) 78 | """) 79 | elif PY3: 80 | def test_inplace_division(self): 81 | self.assert_ok("""\ 82 | x, y = 24, 3 83 | x /= y 84 | assert x == 8.0 and y == 3 85 | assert isinstance(x, float) 86 | x /= y 87 | assert x == (8.0/3.0) and y == 3 88 | assert isinstance(x, float) 89 | """) 90 | 91 | def test_slice(self): 92 | self.assert_ok("""\ 93 | print("hello, world"[3:8]) 94 | """) 95 | self.assert_ok("""\ 96 | print("hello, world"[:8]) 97 | """) 98 | self.assert_ok("""\ 99 | print("hello, world"[3:]) 100 | """) 101 | self.assert_ok("""\ 102 | print("hello, world"[:]) 103 | """) 104 | self.assert_ok("""\ 105 | print("hello, world"[::-1]) 106 | """) 107 | self.assert_ok("""\ 108 | print("hello, world"[3:8:2]) 109 | """) 110 | 111 | def test_slice_assignment(self): 112 | self.assert_ok("""\ 113 | l = list(range(10)) 114 | l[3:8] = ["x"] 115 | print(l) 116 | """) 117 | self.assert_ok("""\ 118 | l = list(range(10)) 119 | l[:8] = ["x"] 120 | print(l) 121 | """) 122 | self.assert_ok("""\ 123 | l = list(range(10)) 124 | l[3:] = ["x"] 125 | print(l) 126 | """) 127 | self.assert_ok("""\ 128 | l = list(range(10)) 129 | l[:] = ["x"] 130 | print(l) 131 | """) 132 | 133 | def test_slice_deletion(self): 134 | self.assert_ok("""\ 135 | l = list(range(10)) 136 | del l[3:8] 137 | print(l) 138 | """) 139 | self.assert_ok("""\ 140 | l = list(range(10)) 141 | del l[:8] 142 | print(l) 143 | """) 144 | self.assert_ok("""\ 145 | l = list(range(10)) 146 | del l[3:] 147 | print(l) 148 | """) 149 | self.assert_ok("""\ 150 | l = list(range(10)) 151 | del l[:] 152 | print(l) 153 | """) 154 | self.assert_ok("""\ 155 | l = list(range(10)) 156 | del l[::2] 157 | print(l) 158 | """) 159 | 160 | def test_building_stuff(self): 161 | self.assert_ok("""\ 162 | print((1+1, 2+2, 3+3)) 163 | """) 164 | self.assert_ok("""\ 165 | print([1+1, 2+2, 3+3]) 166 | """) 167 | self.assert_ok("""\ 168 | print({1:1+1, 2:2+2, 3:3+3}) 169 | """) 170 | 171 | def test_subscripting(self): 172 | self.assert_ok("""\ 173 | l = list(range(10)) 174 | print("%s %s %s" % (l[0], l[3], l[9])) 175 | """) 176 | self.assert_ok("""\ 177 | l = list(range(10)) 178 | l[5] = 17 179 | print(l) 180 | """) 181 | self.assert_ok("""\ 182 | l = list(range(10)) 183 | del l[5] 184 | print(l) 185 | """) 186 | 187 | def test_generator_expression(self): 188 | self.assert_ok("""\ 189 | x = "-".join(str(z) for z in range(5)) 190 | assert x == "0-1-2-3-4" 191 | """) 192 | # From test_regr.py 193 | # This failed a different way than the previous join when genexps were 194 | # broken: 195 | self.assert_ok("""\ 196 | from textwrap import fill 197 | x = set(['test_str']) 198 | width = 70 199 | indent = 4 200 | blanks = ' ' * indent 201 | res = fill(' '.join(str(elt) for elt in sorted(x)), width, 202 | initial_indent=blanks, subsequent_indent=blanks) 203 | print(res) 204 | """) 205 | def test_list_comprehension(self): 206 | self.assert_ok("""\ 207 | x = [z*z for z in range(5)] 208 | assert x == [0, 1, 4, 9, 16] 209 | """) 210 | 211 | def test_dict_comprehension(self): 212 | self.assert_ok("""\ 213 | x = {z:z*z for z in range(5)} 214 | assert x == {0:0, 1:1, 2:4, 3:9, 4:16} 215 | """) 216 | 217 | def test_set_comprehension(self): 218 | self.assert_ok("""\ 219 | x = {z*z for z in range(5)} 220 | assert x == {0, 1, 4, 9, 16} 221 | """) 222 | 223 | def test_strange_sequence_ops(self): 224 | # from stdlib: test/test_augassign.py 225 | self.assert_ok("""\ 226 | x = [1,2] 227 | x += [3,4] 228 | x *= 2 229 | 230 | assert x == [1, 2, 3, 4, 1, 2, 3, 4] 231 | 232 | x = [1, 2, 3] 233 | y = x 234 | x[1:2] *= 2 235 | y[1:2] += [1] 236 | 237 | assert x == [1, 2, 1, 2, 3] 238 | assert x is y 239 | """) 240 | 241 | def test_unary_operators(self): 242 | self.assert_ok("""\ 243 | x = 8 244 | print(-x, ~x, not x) 245 | """) 246 | 247 | def test_attributes(self): 248 | self.assert_ok("""\ 249 | l = lambda: 1 # Just to have an object... 250 | l.foo = 17 251 | print(hasattr(l, "foo"), l.foo) 252 | del l.foo 253 | print(hasattr(l, "foo")) 254 | """) 255 | 256 | def test_attribute_inplace_ops(self): 257 | self.assert_ok("""\ 258 | l = lambda: 1 # Just to have an object... 259 | l.foo = 17 260 | l.foo -= 3 261 | print(l.foo) 262 | """) 263 | 264 | def test_deleting_names(self): 265 | self.assert_ok("""\ 266 | g = 17 267 | assert g == 17 268 | del g 269 | g 270 | """, raises=NameError) 271 | 272 | def test_deleting_local_names(self): 273 | self.assert_ok("""\ 274 | def f(): 275 | l = 23 276 | assert l == 23 277 | del l 278 | l 279 | f() 280 | """, raises=NameError) 281 | 282 | def test_import(self): 283 | self.assert_ok("""\ 284 | import math 285 | print(math.pi, math.e) 286 | from math import sqrt 287 | print(sqrt(2)) 288 | from math import * 289 | print(sin(2)) 290 | """) 291 | 292 | def test_classes(self): 293 | self.assert_ok("""\ 294 | class Thing(object): 295 | def __init__(self, x): 296 | self.x = x 297 | def meth(self, y): 298 | return self.x * y 299 | thing1 = Thing(2) 300 | thing2 = Thing(3) 301 | print(thing1.x, thing2.x) 302 | print(thing1.meth(4), thing2.meth(5)) 303 | """) 304 | 305 | def test_calling_methods_wrong(self): 306 | self.assert_ok("""\ 307 | class Thing(object): 308 | def __init__(self, x): 309 | self.x = x 310 | def meth(self, y): 311 | return self.x * y 312 | thing1 = Thing(2) 313 | print(Thing.meth(14)) 314 | """, raises=TypeError) 315 | 316 | def test_calling_subclass_methods(self): 317 | self.assert_ok("""\ 318 | class Thing(object): 319 | def foo(self): 320 | return 17 321 | 322 | class SubThing(Thing): 323 | pass 324 | 325 | st = SubThing() 326 | print(st.foo()) 327 | """) 328 | 329 | def test_subclass_attribute(self): 330 | self.assert_ok("""\ 331 | class Thing(object): 332 | def __init__(self): 333 | self.foo = 17 334 | class SubThing(Thing): 335 | pass 336 | st = SubThing() 337 | print(st.foo) 338 | """) 339 | 340 | def test_subclass_attributes_not_shared(self): 341 | self.assert_ok("""\ 342 | class Thing(object): 343 | foo = 17 344 | class SubThing(Thing): 345 | foo = 25 346 | st = SubThing() 347 | t = Thing() 348 | assert st.foo == 25 349 | assert t.foo == 17 350 | """) 351 | 352 | def test_object_attrs_not_shared_with_class(self): 353 | self.assert_ok("""\ 354 | class Thing(object): 355 | pass 356 | t = Thing() 357 | t.foo = 1 358 | Thing.foo""", raises=AttributeError) 359 | 360 | def test_data_descriptors_precede_instance_attributes(self): 361 | self.assert_ok("""\ 362 | class Foo(object): 363 | pass 364 | f = Foo() 365 | f.des = 3 366 | class Descr(object): 367 | def __get__(self, obj, cls=None): 368 | return 2 369 | def __set__(self, obj, val): 370 | raise NotImplementedError 371 | Foo.des = Descr() 372 | assert f.des == 2 373 | """) 374 | 375 | def test_instance_attrs_precede_non_data_descriptors(self): 376 | self.assert_ok("""\ 377 | class Foo(object): 378 | pass 379 | f = Foo() 380 | f.des = 3 381 | class Descr(object): 382 | def __get__(self, obj, cls=None): 383 | return 2 384 | Foo.des = Descr() 385 | assert f.des == 3 386 | """) 387 | 388 | def test_subclass_attributes_dynamic(self): 389 | self.assert_ok("""\ 390 | class Foo(object): 391 | pass 392 | class Bar(Foo): 393 | pass 394 | b = Bar() 395 | Foo.baz = 3 396 | assert b.baz == 3 397 | """) 398 | 399 | def test_attribute_access(self): 400 | self.assert_ok("""\ 401 | class Thing(object): 402 | z = 17 403 | def __init__(self): 404 | self.x = 23 405 | t = Thing() 406 | print(Thing.z) 407 | print(t.z) 408 | print(t.x) 409 | """) 410 | 411 | self.assert_ok("""\ 412 | class Thing(object): 413 | z = 17 414 | def __init__(self): 415 | self.x = 23 416 | t = Thing() 417 | print(t.xyzzy) 418 | """, raises=AttributeError) 419 | 420 | def test_staticmethods(self): 421 | self.assert_ok("""\ 422 | class Thing(object): 423 | @staticmethod 424 | def smeth(x): 425 | print(x) 426 | @classmethod 427 | def cmeth(cls, x): 428 | print(x) 429 | 430 | Thing.smeth(1492) 431 | Thing.cmeth(1776) 432 | """) 433 | 434 | def test_unbound_methods(self): 435 | self.assert_ok("""\ 436 | class Thing(object): 437 | def meth(self, x): 438 | print(x) 439 | m = Thing.meth 440 | m(Thing(), 1815) 441 | """) 442 | 443 | def test_bound_methods(self): 444 | self.assert_ok("""\ 445 | class Thing(object): 446 | def meth(self, x): 447 | print(x) 448 | t = Thing() 449 | m = t.meth 450 | m(1815) 451 | """) 452 | 453 | def test_callback(self): 454 | self.assert_ok("""\ 455 | def lcase(s): 456 | return s.lower() 457 | l = ["xyz", "ABC"] 458 | l.sort(key=lcase) 459 | print(l) 460 | assert l == ["ABC", "xyz"] 461 | """) 462 | 463 | def test_unpacking(self): 464 | self.assert_ok("""\ 465 | a, b, c = (1, 2, 3) 466 | assert a == 1 467 | assert b == 2 468 | assert c == 3 469 | """) 470 | 471 | if PY2: 472 | def test_exec_statement(self): 473 | self.assert_ok("""\ 474 | g = {} 475 | exec "a = 11" in g, g 476 | assert g['a'] == 11 477 | """) 478 | elif PY3: 479 | def test_exec_statement(self): 480 | self.assert_ok("""\ 481 | g = {} 482 | exec("a = 11", g, g) 483 | assert g['a'] == 11 484 | """) 485 | 486 | def test_jump_if_true_or_pop(self): 487 | self.assert_ok("""\ 488 | def f(a, b): 489 | return a or b 490 | assert f(17, 0) == 17 491 | assert f(0, 23) == 23 492 | assert f(0, "") == "" 493 | """) 494 | 495 | def test_jump_if_false_or_pop(self): 496 | self.assert_ok("""\ 497 | def f(a, b): 498 | return not(a and b) 499 | assert f(17, 0) is True 500 | assert f(0, 23) is True 501 | assert f(0, "") is True 502 | assert f(17, 23) is False 503 | """) 504 | 505 | def test_pop_jump_if_true(self): 506 | self.assert_ok("""\ 507 | def f(a): 508 | if not a: 509 | return 'foo' 510 | else: 511 | return 'bar' 512 | assert f(0) == 'foo' 513 | assert f(1) == 'bar' 514 | """) 515 | 516 | def test_decorator(self): 517 | self.assert_ok("""\ 518 | def verbose(func): 519 | def _wrapper(*args, **kwargs): 520 | return func(*args, **kwargs) 521 | return _wrapper 522 | 523 | @verbose 524 | def add(x, y): 525 | return x+y 526 | 527 | add(7, 3) 528 | """) 529 | 530 | def test_multiple_classes(self): 531 | # Making classes used to mix together all the class-scoped values 532 | # across classes. This test would fail because A.__init__ would be 533 | # over-written with B.__init__, and A(1, 2, 3) would complain about 534 | # too many arguments. 535 | self.assert_ok("""\ 536 | class A(object): 537 | def __init__(self, a, b, c): 538 | self.sum = a + b + c 539 | 540 | class B(object): 541 | def __init__(self, x): 542 | self.x = x 543 | 544 | a = A(1, 2, 3) 545 | b = B(7) 546 | print(a.sum) 547 | print(b.x) 548 | """) 549 | 550 | 551 | if PY2: 552 | class TestPrinting(vmtest.VmTestCase): 553 | def test_printing(self): 554 | self.assert_ok("print 'hello'") 555 | self.assert_ok("a = 3; print a+4") 556 | self.assert_ok(""" 557 | print 'hi', 17, u'bye', 23, 558 | print "", "\t", "the end" 559 | """) 560 | 561 | def test_printing_in_a_function(self): 562 | self.assert_ok("""\ 563 | def fn(): 564 | print "hello" 565 | fn() 566 | print "bye" 567 | """) 568 | 569 | def test_printing_to_a_file(self): 570 | self.assert_ok("""\ 571 | import sys 572 | print >>sys.stdout, 'hello', 'there' 573 | """) 574 | 575 | 576 | class TestLoops(vmtest.VmTestCase): 577 | def test_for(self): 578 | self.assert_ok("""\ 579 | for i in range(10): 580 | print(i) 581 | print("done") 582 | """) 583 | 584 | def test_break(self): 585 | self.assert_ok("""\ 586 | for i in range(10): 587 | print(i) 588 | if i == 7: 589 | break 590 | print("done") 591 | """) 592 | 593 | def test_continue(self): 594 | # fun fact: this doesn't use CONTINUE_LOOP 595 | self.assert_ok("""\ 596 | for i in range(10): 597 | if i % 3 == 0: 598 | continue 599 | print(i) 600 | print("done") 601 | """) 602 | 603 | def test_continue_in_try_except(self): 604 | self.assert_ok("""\ 605 | for i in range(10): 606 | try: 607 | if i % 3 == 0: 608 | continue 609 | print(i) 610 | except ValueError: 611 | pass 612 | print("done") 613 | """) 614 | 615 | def test_continue_in_try_finally(self): 616 | self.assert_ok("""\ 617 | for i in range(10): 618 | try: 619 | if i % 3 == 0: 620 | continue 621 | print(i) 622 | finally: 623 | print(".") 624 | print("done") 625 | """) 626 | 627 | 628 | class TestComparisons(vmtest.VmTestCase): 629 | def test_in(self): 630 | self.assert_ok("""\ 631 | assert "x" in "xyz" 632 | assert "x" not in "abc" 633 | assert "x" in ("x", "y", "z") 634 | assert "x" not in ("a", "b", "c") 635 | """) 636 | 637 | def test_less(self): 638 | self.assert_ok("""\ 639 | assert 1 < 3 640 | assert 1 <= 2 and 1 <= 1 641 | assert "a" < "b" 642 | assert "a" <= "b" and "a" <= "a" 643 | """) 644 | 645 | def test_greater(self): 646 | self.assert_ok("""\ 647 | assert 3 > 1 648 | assert 3 >= 1 and 3 >= 3 649 | assert "z" > "a" 650 | assert "z" >= "a" and "z" >= "z" 651 | """) 652 | -------------------------------------------------------------------------------- /byterun/pyvm2.py: -------------------------------------------------------------------------------- 1 | """A pure-Python Python bytecode interpreter.""" 2 | # Based on: 3 | # pyvm2 by Paul Swartz (z3p), from http://www.twistedmatrix.com/users/z3p/ 4 | 5 | from __future__ import print_function, division 6 | import dis 7 | import inspect 8 | import linecache 9 | import logging 10 | import operator 11 | import sys 12 | 13 | import six 14 | from six.moves import reprlib 15 | 16 | PY3, PY2 = six.PY3, not six.PY3 17 | 18 | from .pyobj import Frame, Block, Method, Function, Generator 19 | 20 | log = logging.getLogger(__name__) 21 | 22 | if six.PY3: 23 | byteint = lambda b: b 24 | else: 25 | byteint = ord 26 | 27 | # Create a repr that won't overflow. 28 | repr_obj = reprlib.Repr() 29 | repr_obj.maxother = 120 30 | repper = repr_obj.repr 31 | 32 | 33 | class VirtualMachineError(Exception): 34 | """For raising errors in the operation of the VM.""" 35 | pass 36 | 37 | 38 | class VirtualMachine(object): 39 | def __init__(self): 40 | # The call stack of frames. 41 | self.frames = [] 42 | # The current frame. 43 | self.frame = None 44 | self.return_value = None 45 | self.last_exception = None 46 | 47 | def top(self): 48 | """Return the value at the top of the stack, with no changes.""" 49 | return self.frame.stack[-1] 50 | 51 | def pop(self, i=0): 52 | """Pop a value from the stack. 53 | 54 | Default to the top of the stack, but `i` can be a count from the top 55 | instead. 56 | 57 | """ 58 | return self.frame.stack.pop(-1-i) 59 | 60 | def push(self, *vals): 61 | """Push values onto the value stack.""" 62 | self.frame.stack.extend(vals) 63 | 64 | def popn(self, n): 65 | """Pop a number of values from the value stack. 66 | 67 | A list of `n` values is returned, the deepest value first. 68 | 69 | """ 70 | if n: 71 | ret = self.frame.stack[-n:] 72 | self.frame.stack[-n:] = [] 73 | return ret 74 | else: 75 | return [] 76 | 77 | def peek(self, n): 78 | """Get a value `n` entries down in the stack, without changing the stack.""" 79 | return self.frame.stack[-n] 80 | 81 | def jump(self, jump): 82 | """Move the bytecode pointer to `jump`, so it will execute next.""" 83 | self.frame.f_lasti = jump 84 | 85 | def push_block(self, type, handler=None, level=None): 86 | if level is None: 87 | level = len(self.frame.stack) 88 | self.frame.block_stack.append(Block(type, handler, level)) 89 | 90 | def pop_block(self): 91 | return self.frame.block_stack.pop() 92 | 93 | def make_frame(self, code, callargs={}, f_globals=None, f_locals=None): 94 | log.info("make_frame: code=%r, callargs=%s" % (code, repper(callargs))) 95 | if f_globals is not None: 96 | f_globals = f_globals 97 | if f_locals is None: 98 | f_locals = f_globals 99 | elif self.frames: 100 | f_globals = self.frame.f_globals 101 | f_locals = {} 102 | else: 103 | f_globals = f_locals = { 104 | '__builtins__': __builtins__, 105 | '__name__': '__main__', 106 | '__doc__': None, 107 | '__package__': None, 108 | } 109 | f_locals.update(callargs) 110 | frame = Frame(code, f_globals, f_locals, self.frame) 111 | return frame 112 | 113 | def push_frame(self, frame): 114 | self.frames.append(frame) 115 | self.frame = frame 116 | 117 | def pop_frame(self): 118 | self.frames.pop() 119 | if self.frames: 120 | self.frame = self.frames[-1] 121 | else: 122 | self.frame = None 123 | 124 | def print_frames(self): 125 | """Print the call stack, for debugging.""" 126 | for f in self.frames: 127 | filename = f.f_code.co_filename 128 | lineno = f.line_number() 129 | print(' File "%s", line %d, in %s' % ( 130 | filename, lineno, f.f_code.co_name 131 | )) 132 | linecache.checkcache(filename) 133 | line = linecache.getline(filename, lineno, f.f_globals) 134 | if line: 135 | print(' ' + line.strip()) 136 | 137 | def resume_frame(self, frame): 138 | frame.f_back = self.frame 139 | val = self.run_frame(frame) 140 | frame.f_back = None 141 | return val 142 | 143 | def run_code(self, code, f_globals=None, f_locals=None): 144 | frame = self.make_frame(code, f_globals=f_globals, f_locals=f_locals) 145 | val = self.run_frame(frame) 146 | # Check some invariants 147 | if self.frames: # pragma: no cover 148 | raise VirtualMachineError("Frames left over!") 149 | if self.frame and self.frame.stack: # pragma: no cover 150 | raise VirtualMachineError("Data left on stack! %r" % self.frame.stack) 151 | 152 | return val 153 | 154 | def unwind_block(self, block): 155 | if block.type == 'except-handler': 156 | offset = 3 157 | else: 158 | offset = 0 159 | 160 | while len(self.frame.stack) > block.level + offset: 161 | self.pop() 162 | 163 | if block.type == 'except-handler': 164 | tb, value, exctype = self.popn(3) 165 | self.last_exception = exctype, value, tb 166 | 167 | def parse_byte_and_args(self): 168 | """ Parse 1 - 3 bytes of bytecode into 169 | an instruction and optionally arguments.""" 170 | f = self.frame 171 | opoffset = f.f_lasti 172 | byteCode = byteint(f.f_code.co_code[opoffset]) 173 | f.f_lasti += 1 174 | byteName = dis.opname[byteCode] 175 | arg = None 176 | arguments = [] 177 | if byteCode >= dis.HAVE_ARGUMENT: 178 | arg = f.f_code.co_code[f.f_lasti:f.f_lasti+2] 179 | f.f_lasti += 2 180 | intArg = byteint(arg[0]) + (byteint(arg[1]) << 8) 181 | if byteCode in dis.hasconst: 182 | arg = f.f_code.co_consts[intArg] 183 | elif byteCode in dis.hasfree: 184 | if intArg < len(f.f_code.co_cellvars): 185 | arg = f.f_code.co_cellvars[intArg] 186 | else: 187 | var_idx = intArg - len(f.f_code.co_cellvars) 188 | arg = f.f_code.co_freevars[var_idx] 189 | elif byteCode in dis.hasname: 190 | arg = f.f_code.co_names[intArg] 191 | elif byteCode in dis.hasjrel: 192 | arg = f.f_lasti + intArg 193 | elif byteCode in dis.hasjabs: 194 | arg = intArg 195 | elif byteCode in dis.haslocal: 196 | arg = f.f_code.co_varnames[intArg] 197 | else: 198 | arg = intArg 199 | arguments = [arg] 200 | 201 | return byteName, arguments, opoffset 202 | 203 | def log(self, byteName, arguments, opoffset): 204 | """ Log arguments, block stack, and data stack for each opcode.""" 205 | op = "%d: %s" % (opoffset, byteName) 206 | if arguments: 207 | op += " %r" % (arguments[0],) 208 | indent = " "*(len(self.frames)-1) 209 | stack_rep = repper(self.frame.stack) 210 | block_stack_rep = repper(self.frame.block_stack) 211 | 212 | log.info(" %sdata: %s" % (indent, stack_rep)) 213 | log.info(" %sblks: %s" % (indent, block_stack_rep)) 214 | log.info("%s%s" % (indent, op)) 215 | 216 | def dispatch(self, byteName, arguments): 217 | """ Dispatch by bytename to the corresponding methods. 218 | Exceptions are caught and set on the virtual machine.""" 219 | why = None 220 | try: 221 | if byteName.startswith('UNARY_'): 222 | self.unaryOperator(byteName[6:]) 223 | elif byteName.startswith('BINARY_'): 224 | self.binaryOperator(byteName[7:]) 225 | elif byteName.startswith('INPLACE_'): 226 | self.inplaceOperator(byteName[8:]) 227 | elif 'SLICE+' in byteName: 228 | self.sliceOperator(byteName) 229 | else: 230 | # dispatch 231 | bytecode_fn = getattr(self, 'byte_%s' % byteName, None) 232 | if not bytecode_fn: # pragma: no cover 233 | raise VirtualMachineError( 234 | "unknown bytecode type: %s" % byteName 235 | ) 236 | why = bytecode_fn(*arguments) 237 | 238 | except: 239 | # deal with exceptions encountered while executing the op. 240 | self.last_exception = sys.exc_info()[:2] + (None,) 241 | log.exception("Caught exception during execution") 242 | why = 'exception' 243 | 244 | return why 245 | 246 | def manage_block_stack(self, why): 247 | """ Manage a frame's block stack. 248 | Manipulate the block stack and data stack for looping, 249 | exception handling, or returning.""" 250 | assert why != 'yield' 251 | 252 | block = self.frame.block_stack[-1] 253 | if block.type == 'loop' and why == 'continue': 254 | self.jump(self.return_value) 255 | why = None 256 | return why 257 | 258 | self.pop_block() 259 | self.unwind_block(block) 260 | 261 | if block.type == 'loop' and why == 'break': 262 | why = None 263 | self.jump(block.handler) 264 | return why 265 | 266 | if PY2: 267 | if ( 268 | block.type == 'finally' or 269 | (block.type == 'setup-except' and why == 'exception') or 270 | block.type == 'with' 271 | ): 272 | if why == 'exception': 273 | exctype, value, tb = self.last_exception 274 | self.push(tb, value, exctype) 275 | else: 276 | if why in ('return', 'continue'): 277 | self.push(self.return_value) 278 | self.push(why) 279 | 280 | why = None 281 | self.jump(block.handler) 282 | return why 283 | 284 | elif PY3: 285 | if ( 286 | why == 'exception' and 287 | block.type in ['setup-except', 'finally'] 288 | ): 289 | self.push_block('except-handler') 290 | exctype, value, tb = self.last_exception 291 | self.push(tb, value, exctype) 292 | # PyErr_Normalize_Exception goes here 293 | self.push(tb, value, exctype) 294 | why = None 295 | self.jump(block.handler) 296 | return why 297 | 298 | elif block.type == 'finally': 299 | if why in ('return', 'continue'): 300 | self.push(self.return_value) 301 | self.push(why) 302 | 303 | why = None 304 | self.jump(block.handler) 305 | return why 306 | 307 | return why 308 | 309 | 310 | def run_frame(self, frame): 311 | """Run a frame until it returns (somehow). 312 | 313 | Exceptions are raised, the return value is returned. 314 | 315 | """ 316 | self.push_frame(frame) 317 | while True: 318 | byteName, arguments, opoffset = self.parse_byte_and_args() 319 | if log.isEnabledFor(logging.INFO): 320 | self.log(byteName, arguments, opoffset) 321 | 322 | # When unwinding the block stack, we need to keep track of why we 323 | # are doing it. 324 | why = self.dispatch(byteName, arguments) 325 | if why == 'exception': 326 | # TODO: ceval calls PyTraceBack_Here, not sure what that does. 327 | pass 328 | 329 | if why == 'reraise': 330 | why = 'exception' 331 | 332 | if why != 'yield': 333 | while why and frame.block_stack: 334 | # Deal with any block management we need to do. 335 | why = self.manage_block_stack(why) 336 | 337 | if why: 338 | break 339 | 340 | # TODO: handle generator exception state 341 | 342 | self.pop_frame() 343 | 344 | if why == 'exception': 345 | six.reraise(*self.last_exception) 346 | 347 | return self.return_value 348 | 349 | ## Stack manipulation 350 | 351 | def byte_LOAD_CONST(self, const): 352 | self.push(const) 353 | 354 | def byte_POP_TOP(self): 355 | self.pop() 356 | 357 | def byte_DUP_TOP(self): 358 | self.push(self.top()) 359 | 360 | def byte_DUP_TOPX(self, count): 361 | items = self.popn(count) 362 | for i in [1, 2]: 363 | self.push(*items) 364 | 365 | def byte_DUP_TOP_TWO(self): 366 | # Py3 only 367 | a, b = self.popn(2) 368 | self.push(a, b, a, b) 369 | 370 | def byte_ROT_TWO(self): 371 | a, b = self.popn(2) 372 | self.push(b, a) 373 | 374 | def byte_ROT_THREE(self): 375 | a, b, c = self.popn(3) 376 | self.push(c, a, b) 377 | 378 | def byte_ROT_FOUR(self): 379 | a, b, c, d = self.popn(4) 380 | self.push(d, a, b, c) 381 | 382 | ## Names 383 | 384 | def byte_LOAD_NAME(self, name): 385 | frame = self.frame 386 | if name in frame.f_locals: 387 | val = frame.f_locals[name] 388 | elif name in frame.f_globals: 389 | val = frame.f_globals[name] 390 | elif name in frame.f_builtins: 391 | val = frame.f_builtins[name] 392 | else: 393 | raise NameError("name '%s' is not defined" % name) 394 | self.push(val) 395 | 396 | def byte_STORE_NAME(self, name): 397 | self.frame.f_locals[name] = self.pop() 398 | 399 | def byte_DELETE_NAME(self, name): 400 | del self.frame.f_locals[name] 401 | 402 | def byte_LOAD_FAST(self, name): 403 | if name in self.frame.f_locals: 404 | val = self.frame.f_locals[name] 405 | else: 406 | raise UnboundLocalError( 407 | "local variable '%s' referenced before assignment" % name 408 | ) 409 | self.push(val) 410 | 411 | def byte_STORE_FAST(self, name): 412 | self.frame.f_locals[name] = self.pop() 413 | 414 | def byte_DELETE_FAST(self, name): 415 | del self.frame.f_locals[name] 416 | 417 | def byte_LOAD_GLOBAL(self, name): 418 | f = self.frame 419 | if name in f.f_globals: 420 | val = f.f_globals[name] 421 | elif name in f.f_builtins: 422 | val = f.f_builtins[name] 423 | else: 424 | raise NameError("global name '%s' is not defined" % name) 425 | self.push(val) 426 | 427 | def byte_STORE_GLOBAL(self, name): 428 | f = self.frame 429 | f.f_globals[name] = self.pop() 430 | 431 | def byte_LOAD_DEREF(self, name): 432 | self.push(self.frame.cells[name].get()) 433 | 434 | def byte_STORE_DEREF(self, name): 435 | self.frame.cells[name].set(self.pop()) 436 | 437 | def byte_LOAD_LOCALS(self): 438 | self.push(self.frame.f_locals) 439 | 440 | ## Operators 441 | 442 | UNARY_OPERATORS = { 443 | 'POSITIVE': operator.pos, 444 | 'NEGATIVE': operator.neg, 445 | 'NOT': operator.not_, 446 | 'CONVERT': repr, 447 | 'INVERT': operator.invert, 448 | } 449 | 450 | def unaryOperator(self, op): 451 | x = self.pop() 452 | self.push(self.UNARY_OPERATORS[op](x)) 453 | 454 | BINARY_OPERATORS = { 455 | 'POWER': pow, 456 | 'MULTIPLY': operator.mul, 457 | 'DIVIDE': getattr(operator, 'div', lambda x, y: None), 458 | 'FLOOR_DIVIDE': operator.floordiv, 459 | 'TRUE_DIVIDE': operator.truediv, 460 | 'MODULO': operator.mod, 461 | 'ADD': operator.add, 462 | 'SUBTRACT': operator.sub, 463 | 'SUBSCR': operator.getitem, 464 | 'LSHIFT': operator.lshift, 465 | 'RSHIFT': operator.rshift, 466 | 'AND': operator.and_, 467 | 'XOR': operator.xor, 468 | 'OR': operator.or_, 469 | } 470 | 471 | def binaryOperator(self, op): 472 | x, y = self.popn(2) 473 | self.push(self.BINARY_OPERATORS[op](x, y)) 474 | 475 | def inplaceOperator(self, op): 476 | x, y = self.popn(2) 477 | if op == 'POWER': 478 | x **= y 479 | elif op == 'MULTIPLY': 480 | x *= y 481 | elif op in ['DIVIDE', 'FLOOR_DIVIDE']: 482 | x //= y 483 | elif op == 'TRUE_DIVIDE': 484 | x /= y 485 | elif op == 'MODULO': 486 | x %= y 487 | elif op == 'ADD': 488 | x += y 489 | elif op == 'SUBTRACT': 490 | x -= y 491 | elif op == 'LSHIFT': 492 | x <<= y 493 | elif op == 'RSHIFT': 494 | x >>= y 495 | elif op == 'AND': 496 | x &= y 497 | elif op == 'XOR': 498 | x ^= y 499 | elif op == 'OR': 500 | x |= y 501 | else: # pragma: no cover 502 | raise VirtualMachineError("Unknown in-place operator: %r" % op) 503 | self.push(x) 504 | 505 | def sliceOperator(self, op): 506 | start = 0 507 | end = None # we will take this to mean end 508 | op, count = op[:-2], int(op[-1]) 509 | if count == 1: 510 | start = self.pop() 511 | elif count == 2: 512 | end = self.pop() 513 | elif count == 3: 514 | end = self.pop() 515 | start = self.pop() 516 | l = self.pop() 517 | if end is None: 518 | end = len(l) 519 | if op.startswith('STORE_'): 520 | l[start:end] = self.pop() 521 | elif op.startswith('DELETE_'): 522 | del l[start:end] 523 | else: 524 | self.push(l[start:end]) 525 | 526 | COMPARE_OPERATORS = [ 527 | operator.lt, 528 | operator.le, 529 | operator.eq, 530 | operator.ne, 531 | operator.gt, 532 | operator.ge, 533 | lambda x, y: x in y, 534 | lambda x, y: x not in y, 535 | lambda x, y: x is y, 536 | lambda x, y: x is not y, 537 | lambda x, y: issubclass(x, Exception) and issubclass(x, y), 538 | ] 539 | 540 | def byte_COMPARE_OP(self, opnum): 541 | x, y = self.popn(2) 542 | self.push(self.COMPARE_OPERATORS[opnum](x, y)) 543 | 544 | ## Attributes and indexing 545 | 546 | def byte_LOAD_ATTR(self, attr): 547 | obj = self.pop() 548 | val = getattr(obj, attr) 549 | self.push(val) 550 | 551 | def byte_STORE_ATTR(self, name): 552 | val, obj = self.popn(2) 553 | setattr(obj, name, val) 554 | 555 | def byte_DELETE_ATTR(self, name): 556 | obj = self.pop() 557 | delattr(obj, name) 558 | 559 | def byte_STORE_SUBSCR(self): 560 | val, obj, subscr = self.popn(3) 561 | obj[subscr] = val 562 | 563 | def byte_DELETE_SUBSCR(self): 564 | obj, subscr = self.popn(2) 565 | del obj[subscr] 566 | 567 | ## Building 568 | 569 | def byte_BUILD_TUPLE(self, count): 570 | elts = self.popn(count) 571 | self.push(tuple(elts)) 572 | 573 | def byte_BUILD_LIST(self, count): 574 | elts = self.popn(count) 575 | self.push(elts) 576 | 577 | def byte_BUILD_SET(self, count): 578 | # TODO: Not documented in Py2 docs. 579 | elts = self.popn(count) 580 | self.push(set(elts)) 581 | 582 | def byte_BUILD_MAP(self, size): 583 | # size is ignored. 584 | self.push({}) 585 | 586 | def byte_STORE_MAP(self): 587 | the_map, val, key = self.popn(3) 588 | the_map[key] = val 589 | self.push(the_map) 590 | 591 | def byte_UNPACK_SEQUENCE(self, count): 592 | seq = self.pop() 593 | for x in reversed(seq): 594 | self.push(x) 595 | 596 | def byte_BUILD_SLICE(self, count): 597 | if count == 2: 598 | x, y = self.popn(2) 599 | self.push(slice(x, y)) 600 | elif count == 3: 601 | x, y, z = self.popn(3) 602 | self.push(slice(x, y, z)) 603 | else: # pragma: no cover 604 | raise VirtualMachineError("Strange BUILD_SLICE count: %r" % count) 605 | 606 | def byte_LIST_APPEND(self, count): 607 | val = self.pop() 608 | the_list = self.peek(count) 609 | the_list.append(val) 610 | 611 | def byte_SET_ADD(self, count): 612 | val = self.pop() 613 | the_set = self.peek(count) 614 | the_set.add(val) 615 | 616 | def byte_MAP_ADD(self, count): 617 | val, key = self.popn(2) 618 | the_map = self.peek(count) 619 | the_map[key] = val 620 | 621 | ## Printing 622 | 623 | if 0: # Only used in the interactive interpreter, not in modules. 624 | def byte_PRINT_EXPR(self): 625 | print(self.pop()) 626 | 627 | def byte_PRINT_ITEM(self): 628 | item = self.pop() 629 | self.print_item(item) 630 | 631 | def byte_PRINT_ITEM_TO(self): 632 | to = self.pop() 633 | item = self.pop() 634 | self.print_item(item, to) 635 | 636 | def byte_PRINT_NEWLINE(self): 637 | self.print_newline() 638 | 639 | def byte_PRINT_NEWLINE_TO(self): 640 | to = self.pop() 641 | self.print_newline(to) 642 | 643 | def print_item(self, item, to=None): 644 | if to is None: 645 | to = sys.stdout 646 | if to.softspace: 647 | print(" ", end="", file=to) 648 | to.softspace = 0 649 | print(item, end="", file=to) 650 | if isinstance(item, str): 651 | if (not item) or (not item[-1].isspace()) or (item[-1] == " "): 652 | to.softspace = 1 653 | else: 654 | to.softspace = 1 655 | 656 | def print_newline(self, to=None): 657 | if to is None: 658 | to = sys.stdout 659 | print("", file=to) 660 | to.softspace = 0 661 | 662 | ## Jumps 663 | 664 | def byte_JUMP_FORWARD(self, jump): 665 | self.jump(jump) 666 | 667 | def byte_JUMP_ABSOLUTE(self, jump): 668 | self.jump(jump) 669 | 670 | if 0: # Not in py2.7 671 | def byte_JUMP_IF_TRUE(self, jump): 672 | val = self.top() 673 | if val: 674 | self.jump(jump) 675 | 676 | def byte_JUMP_IF_FALSE(self, jump): 677 | val = self.top() 678 | if not val: 679 | self.jump(jump) 680 | 681 | def byte_POP_JUMP_IF_TRUE(self, jump): 682 | val = self.pop() 683 | if val: 684 | self.jump(jump) 685 | 686 | def byte_POP_JUMP_IF_FALSE(self, jump): 687 | val = self.pop() 688 | if not val: 689 | self.jump(jump) 690 | 691 | def byte_JUMP_IF_TRUE_OR_POP(self, jump): 692 | val = self.top() 693 | if val: 694 | self.jump(jump) 695 | else: 696 | self.pop() 697 | 698 | def byte_JUMP_IF_FALSE_OR_POP(self, jump): 699 | val = self.top() 700 | if not val: 701 | self.jump(jump) 702 | else: 703 | self.pop() 704 | 705 | ## Blocks 706 | 707 | def byte_SETUP_LOOP(self, dest): 708 | self.push_block('loop', dest) 709 | 710 | def byte_GET_ITER(self): 711 | self.push(iter(self.pop())) 712 | 713 | def byte_FOR_ITER(self, jump): 714 | iterobj = self.top() 715 | try: 716 | v = next(iterobj) 717 | self.push(v) 718 | except StopIteration: 719 | self.pop() 720 | self.jump(jump) 721 | 722 | def byte_BREAK_LOOP(self): 723 | return 'break' 724 | 725 | def byte_CONTINUE_LOOP(self, dest): 726 | # This is a trick with the return value. 727 | # While unrolling blocks, continue and return both have to preserve 728 | # state as the finally blocks are executed. For continue, it's 729 | # where to jump to, for return, it's the value to return. It gets 730 | # pushed on the stack for both, so continue puts the jump destination 731 | # into return_value. 732 | self.return_value = dest 733 | return 'continue' 734 | 735 | def byte_SETUP_EXCEPT(self, dest): 736 | self.push_block('setup-except', dest) 737 | 738 | def byte_SETUP_FINALLY(self, dest): 739 | self.push_block('finally', dest) 740 | 741 | def byte_END_FINALLY(self): 742 | v = self.pop() 743 | if isinstance(v, str): 744 | why = v 745 | if why in ('return', 'continue'): 746 | self.return_value = self.pop() 747 | if why == 'silenced': # PY3 748 | block = self.pop_block() 749 | assert block.type == 'except-handler' 750 | self.unwind_block(block) 751 | why = None 752 | elif v is None: 753 | why = None 754 | elif issubclass(v, BaseException): 755 | exctype = v 756 | val = self.pop() 757 | tb = self.pop() 758 | self.last_exception = (exctype, val, tb) 759 | why = 'reraise' 760 | else: # pragma: no cover 761 | raise VirtualMachineError("Confused END_FINALLY") 762 | return why 763 | 764 | def byte_POP_BLOCK(self): 765 | self.pop_block() 766 | 767 | if PY2: 768 | def byte_RAISE_VARARGS(self, argc): 769 | # NOTE: the dis docs are completely wrong about the order of the 770 | # operands on the stack! 771 | exctype = val = tb = None 772 | if argc == 0: 773 | exctype, val, tb = self.last_exception 774 | elif argc == 1: 775 | exctype = self.pop() 776 | elif argc == 2: 777 | val = self.pop() 778 | exctype = self.pop() 779 | elif argc == 3: 780 | tb = self.pop() 781 | val = self.pop() 782 | exctype = self.pop() 783 | 784 | # There are a number of forms of "raise", normalize them somewhat. 785 | if isinstance(exctype, BaseException): 786 | val = exctype 787 | exctype = type(val) 788 | 789 | self.last_exception = (exctype, val, tb) 790 | 791 | if tb: 792 | return 'reraise' 793 | else: 794 | return 'exception' 795 | 796 | elif PY3: 797 | def byte_RAISE_VARARGS(self, argc): 798 | cause = exc = None 799 | if argc == 2: 800 | cause = self.pop() 801 | exc = self.pop() 802 | elif argc == 1: 803 | exc = self.pop() 804 | return self.do_raise(exc, cause) 805 | 806 | def do_raise(self, exc, cause): 807 | if exc is None: # reraise 808 | exc_type, val, tb = self.last_exception 809 | if exc_type is None: 810 | return 'exception' # error 811 | else: 812 | return 'reraise' 813 | 814 | elif type(exc) == type: 815 | # As in `raise ValueError` 816 | exc_type = exc 817 | val = exc() # Make an instance. 818 | elif isinstance(exc, BaseException): 819 | # As in `raise ValueError('foo')` 820 | exc_type = type(exc) 821 | val = exc 822 | else: 823 | return 'exception' # error 824 | 825 | # If you reach this point, you're guaranteed that 826 | # val is a valid exception instance and exc_type is its class. 827 | # Now do a similar thing for the cause, if present. 828 | if cause: 829 | if type(cause) == type: 830 | cause = cause() 831 | elif not isinstance(cause, BaseException): 832 | return 'exception' # error 833 | 834 | val.__cause__ = cause 835 | 836 | self.last_exception = exc_type, val, val.__traceback__ 837 | return 'exception' 838 | 839 | def byte_POP_EXCEPT(self): 840 | block = self.pop_block() 841 | if block.type != 'except-handler': 842 | raise Exception("popped block is not an except handler") 843 | self.unwind_block(block) 844 | 845 | def byte_SETUP_WITH(self, dest): 846 | ctxmgr = self.pop() 847 | self.push(ctxmgr.__exit__) 848 | ctxmgr_obj = ctxmgr.__enter__() 849 | if PY2: 850 | self.push_block('with', dest) 851 | elif PY3: 852 | self.push_block('finally', dest) 853 | self.push(ctxmgr_obj) 854 | 855 | def byte_WITH_CLEANUP(self): 856 | # The code here does some weird stack manipulation: the exit function 857 | # is buried in the stack, and where depends on what's on top of it. 858 | # Pull out the exit function, and leave the rest in place. 859 | v = w = None 860 | u = self.top() 861 | if u is None: 862 | exit_func = self.pop(1) 863 | elif isinstance(u, str): 864 | if u in ('return', 'continue'): 865 | exit_func = self.pop(2) 866 | else: 867 | exit_func = self.pop(1) 868 | u = None 869 | elif issubclass(u, BaseException): 870 | if PY2: 871 | w, v, u = self.popn(3) 872 | exit_func = self.pop() 873 | self.push(w, v, u) 874 | elif PY3: 875 | w, v, u = self.popn(3) 876 | tp, exc, tb = self.popn(3) 877 | exit_func = self.pop() 878 | self.push(tp, exc, tb) 879 | self.push(None) 880 | self.push(w, v, u) 881 | block = self.pop_block() 882 | assert block.type == 'except-handler' 883 | self.push_block(block.type, block.handler, block.level-1) 884 | else: # pragma: no cover 885 | raise VirtualMachineError("Confused WITH_CLEANUP") 886 | exit_ret = exit_func(u, v, w) 887 | err = (u is not None) and bool(exit_ret) 888 | if err: 889 | # An error occurred, and was suppressed 890 | if PY2: 891 | self.popn(3) 892 | self.push(None) 893 | elif PY3: 894 | self.push('silenced') 895 | 896 | ## Functions 897 | 898 | def byte_MAKE_FUNCTION(self, argc): 899 | if PY3: 900 | name = self.pop() 901 | else: 902 | name = None 903 | code = self.pop() 904 | defaults = self.popn(argc) 905 | globs = self.frame.f_globals 906 | fn = Function(name, code, globs, defaults, None, self) 907 | self.push(fn) 908 | 909 | def byte_LOAD_CLOSURE(self, name): 910 | self.push(self.frame.cells[name]) 911 | 912 | def byte_MAKE_CLOSURE(self, argc): 913 | if PY3: 914 | # TODO: the py3 docs don't mention this change. 915 | name = self.pop() 916 | else: 917 | name = None 918 | closure, code = self.popn(2) 919 | defaults = self.popn(argc) 920 | globs = self.frame.f_globals 921 | fn = Function(name, code, globs, defaults, closure, self) 922 | self.push(fn) 923 | 924 | def byte_CALL_FUNCTION(self, arg): 925 | return self.call_function(arg, [], {}) 926 | 927 | def byte_CALL_FUNCTION_VAR(self, arg): 928 | args = self.pop() 929 | return self.call_function(arg, args, {}) 930 | 931 | def byte_CALL_FUNCTION_KW(self, arg): 932 | kwargs = self.pop() 933 | return self.call_function(arg, [], kwargs) 934 | 935 | def byte_CALL_FUNCTION_VAR_KW(self, arg): 936 | args, kwargs = self.popn(2) 937 | return self.call_function(arg, args, kwargs) 938 | 939 | def call_function(self, arg, args, kwargs): 940 | lenKw, lenPos = divmod(arg, 256) 941 | namedargs = {} 942 | for i in range(lenKw): 943 | key, val = self.popn(2) 944 | namedargs[key] = val 945 | namedargs.update(kwargs) 946 | posargs = self.popn(lenPos) 947 | posargs.extend(args) 948 | 949 | func = self.pop() 950 | frame = self.frame 951 | if hasattr(func, 'im_func'): 952 | # Methods get self as an implicit first parameter. 953 | if func.im_self: 954 | posargs.insert(0, func.im_self) 955 | # The first parameter must be the correct type. 956 | if not isinstance(posargs[0], func.im_class): 957 | raise TypeError( 958 | 'unbound method %s() must be called with %s instance ' 959 | 'as first argument (got %s instance instead)' % ( 960 | func.im_func.func_name, 961 | func.im_class.__name__, 962 | type(posargs[0]).__name__, 963 | ) 964 | ) 965 | func = func.im_func 966 | retval = func(*posargs, **namedargs) 967 | self.push(retval) 968 | 969 | def byte_RETURN_VALUE(self): 970 | self.return_value = self.pop() 971 | if self.frame.generator: 972 | self.frame.generator.finished = True 973 | return "return" 974 | 975 | def byte_YIELD_VALUE(self): 976 | self.return_value = self.pop() 977 | return "yield" 978 | 979 | def byte_YIELD_FROM(self): 980 | u = self.pop() 981 | x = self.top() 982 | 983 | try: 984 | if not isinstance(x, Generator) or u is None: 985 | # Call next on iterators. 986 | retval = next(x) 987 | else: 988 | retval = x.send(u) 989 | self.return_value = retval 990 | except StopIteration as e: 991 | self.pop() 992 | self.push(e.value) 993 | else: 994 | # YIELD_FROM decrements f_lasti, so that it will be called 995 | # repeatedly until a StopIteration is raised. 996 | self.jump(self.frame.f_lasti - 1) 997 | # Returning "yield" prevents the block stack cleanup code 998 | # from executing, suspending the frame in its current state. 999 | return "yield" 1000 | 1001 | ## Importing 1002 | 1003 | def byte_IMPORT_NAME(self, name): 1004 | level, fromlist = self.popn(2) 1005 | frame = self.frame 1006 | self.push( 1007 | __import__(name, frame.f_globals, frame.f_locals, fromlist, level) 1008 | ) 1009 | 1010 | def byte_IMPORT_STAR(self): 1011 | # TODO: this doesn't use __all__ properly. 1012 | mod = self.pop() 1013 | for attr in dir(mod): 1014 | if attr[0] != '_': 1015 | self.frame.f_locals[attr] = getattr(mod, attr) 1016 | 1017 | def byte_IMPORT_FROM(self, name): 1018 | mod = self.top() 1019 | self.push(getattr(mod, name)) 1020 | 1021 | ## And the rest... 1022 | 1023 | def byte_EXEC_STMT(self): 1024 | stmt, globs, locs = self.popn(3) 1025 | six.exec_(stmt, globs, locs) 1026 | 1027 | if PY2: 1028 | def byte_BUILD_CLASS(self): 1029 | name, bases, methods = self.popn(3) 1030 | self.push(type(name, bases, methods)) 1031 | 1032 | 1033 | elif PY3: 1034 | def byte_LOAD_BUILD_CLASS(self): 1035 | # New in py3 1036 | self.push(__build_class__) 1037 | 1038 | def byte_STORE_LOCALS(self): 1039 | self.frame.f_locals = self.pop() 1040 | 1041 | if 0: # Not in py2.7 1042 | def byte_SET_LINENO(self, lineno): 1043 | self.frame.f_lineno = lineno 1044 | --------------------------------------------------------------------------------