Skip to content

Commit

Permalink
Merge pull request #103 from rocky/cross-version-types
Browse files Browse the repository at this point in the history
Add a "long" type for use in Python3 and ...
  • Loading branch information
rocky authored Apr 16, 2023
2 parents f9f965a + 3dce79b commit 30fe258
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 46 deletions.
85 changes: 52 additions & 33 deletions test_unit/test_dis27.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
# Minimal tests for dis module
from __future__ import print_function

from xdis import PYTHON3, IS_PYPY
import sys
import unittest

import six
import sys

import xdis.std as dis
from xdis import IS_PYPY, PYTHON3, PYTHON_VERSION_TRIPLE

if PYTHON3:
from io import StringIO
else:
from test.test_support import run_unittest
from StringIO import StringIO

import unittest
import xdis.std as dis
from StringIO import StringIO

# Turn off black formatting so we
# can use custom line numbering
# that we make use of in testing
# fmt: off
def bug708901():
for res in range(1,
for _ in range(1,
10):
pass

def bug1333982(x=[]):
assert 0, ([s for s in x] +
1)
pass
# fmt: on

if PYTHON_VERSION_TRIPLE[0:2] == (2, 7):

if sys.version_info[0:2] == (2, 7):
def _f(a):
print(a)
return 1
Expand All @@ -39,8 +46,10 @@ def _f(a):
%3d: 10 LOAD_CONST 1 (1)
13 RETURN_VALUE
"""%(_f.func_code.co_firstlineno + 1,
_f.func_code.co_firstlineno + 2)
""" % (
_f.func_code.co_firstlineno + 1,
_f.func_code.co_firstlineno + 2,
)

dis_bug708901 = """\
%3d: 0 SETUP_LOOP 23 (to 26)
Expand All @@ -51,17 +60,18 @@ def _f(a):
12 CALL_FUNCTION 2 (2 positional, 0 named)
15 GET_ITER
>> 16 FOR_ITER 6 (to 25)
19 STORE_FAST 0 (res)
19 STORE_FAST 0 (_)
%3d: 22 JUMP_ABSOLUTE 16 (to 16)
>> 25 POP_BLOCK
>> 26 LOAD_CONST 0 (None)
29 RETURN_VALUE
"""%(bug708901.func_code.co_firstlineno + 1,
bug708901.func_code.co_firstlineno + 2,
bug708901.func_code.co_firstlineno + 3)

""" % (
bug708901.func_code.co_firstlineno + 1,
bug708901.func_code.co_firstlineno + 2,
bug708901.func_code.co_firstlineno + 3,
)

dis_bug708901pypy = """\
%3d: 0 SETUP_LOOP 23 (to 26)
Expand All @@ -72,17 +82,18 @@ def _f(a):
12 CALL_FUNCTION 2 (2 positional, 0 named)
15 GET_ITER
>> 16 FOR_ITER 6 (to 25)
19 STORE_FAST 0 (res)
19 STORE_FAST 0 (_)
%3d: 22 JUMP_ABSOLUTE 16 (to 16)
>> 25 POP_BLOCK
>> 26 LOAD_CONST 0 (None)
29 RETURN_VALUE
"""%(bug708901.func_code.co_firstlineno + 1,
bug708901.func_code.co_firstlineno + 2,
bug708901.func_code.co_firstlineno + 3)

""" % (
bug708901.func_code.co_firstlineno + 1,
bug708901.func_code.co_firstlineno + 2,
bug708901.func_code.co_firstlineno + 3,
)

dis_bug1333982 = """\
%3d: 0 LOAD_CONST 1 (0)
Expand All @@ -105,9 +116,11 @@ def _f(a):
%3d: >> 41 LOAD_CONST 0 (None)
44 RETURN_VALUE
"""%(bug1333982.func_code.co_firstlineno + 1,
bug1333982.func_code.co_firstlineno + 2,
bug1333982.func_code.co_firstlineno + 3)
""" % (
bug1333982.func_code.co_firstlineno + 1,
bug1333982.func_code.co_firstlineno + 2,
bug1333982.func_code.co_firstlineno + 3,
)

_BIG_LINENO_FORMAT = """\
%3d: 0 LOAD_GLOBAL 0 (spam)
Expand All @@ -126,15 +139,16 @@ def do_disassembly_test(self, func, expected):
sys.stdout = save_stdout
got = s.getvalue()
# Trim trailing blanks (if any).
lines = got.split('\n')
lines = got.split("\n")
# lines = [line.rstrip() for line in lines]
expected = expected.split("\n")
import difflib

if expected != lines:
self.fail(
"events did not match expectation:\n" +
"\n".join(difflib.ndiff(expected,
lines)))
"events did not match expectation:\n"
+ "\n".join(difflib.ndiff(expected, lines))
)

def test_opmap(self):
self.assertEqual(dis.opmap["STOP_CODE"], 0)
Expand Down Expand Up @@ -166,16 +180,15 @@ def test_bug_1333982(self):
if False:
self.do_disassembly_test(bug1333982, dis_bug1333982)
else:
self.skipTest('need asserts, run without -O')
self.skipTest("need asserts, run without -O")

def test_big_linenos(self):
def func(count):
namespace = {}
func = "def foo():\n " + "".join(["\n "] * count + ["spam\n"]
)
exec_fn = six.__dict__['exec_']
func = "def foo():\n " + "".join(["\n "] * count + ["spam\n"])
exec_fn = six.__dict__["exec_"]
exec_fn(func, namespace)
return namespace['foo']
return namespace["foo"]

# Test all small ranges
for i in range(1, 300):
Expand All @@ -187,8 +200,14 @@ def func(count):
expected = _BIG_LINENO_FORMAT % (i + 2)
self.do_disassembly_test(func(i), expected)

def test_main():
run_unittest(DisTests)

if PYTHON_VERSION_TRIPLE >= (3, 0):
# Write a test for showing Python 2.x Long types in Python 3
pass


def test_main():
run_unittest(DisTests)


if __name__ == "__main__":
Expand Down
20 changes: 14 additions & 6 deletions xdis/bytecode.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@
allow running on Python 2.
"""

import sys, types
import sys
import types
from io import StringIO

from xdis.cross_dis import (
get_code_object,
format_code_info,
get_code_object,
instruction_size,
op_has_argument,
)
from xdis.instruction import Instruction
from xdis.util import code2num, num2code

from io import StringIO

_have_code = (types.MethodType, types.FunctionType, types.CodeType, type)


Expand Down Expand Up @@ -67,7 +67,7 @@ def offset2line(offset: int, linestarts):
return linestarts[high][1]


def _get_const_info(const_index, const_list):
def get_const_info(const_index, const_list):
"""Helper to get optional details about const references
Returns the dereferenced constant and its repr if the constant
Expand All @@ -88,7 +88,11 @@ def _get_const_info(const_index, const_list):
return argval, repr(argval)


def _get_name_info(name_index, name_list):
# For compatiablity
_get_const_info = get_const_info


def get_name_info(name_index, name_list):
"""Helper to get optional details about named references
Returns the dereferenced name as both value and repr if the name
Expand All @@ -109,6 +113,10 @@ def _get_name_info(name_index, name_list):
return argval, argrepr


# For compatiablity
_get_name_info = get_name_info


def get_instructions_bytes(
bytecode,
opc,
Expand Down
36 changes: 36 additions & 0 deletions xdis/cross_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright (c) 2023 by Rocky Bernstein
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""
Defines types from one set of Python versions that don't exist in
another set of Pythons
"""


class LongTypeForPython3(int):
"""
Define a Python3 long integer type which exists in
Python 2 but does not exist in Python 3.
"""

def __init__(self, value):
self.value = value

def __repr__(self):
"""
Replacement __str__ and str() for Python3.
This ensures we get the "L" suffix on long types.
"""
return f"""{self.value}L"""
12 changes: 7 additions & 5 deletions xdis/marsh.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# (C) Copyright 2018-2022 by Rocky Bernstein
# (C) Copyright 2018-2023 by Rocky Bernstein
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
Expand Down Expand Up @@ -27,10 +27,11 @@
# NOTE: This module is used in the Python3 interpreter, but also by
# the "sandboxed" process. It must work for Python2 as well.

import types, struct
import struct
import types

from xdis.codetype import Code2, Code3
from xdis.version_info import PYTHON_VERSION_TRIPLE, PYTHON3, version_tuple_to_str
from xdis.version_info import PYTHON3, PYTHON_VERSION_TRIPLE, version_tuple_to_str

try:
intern
Expand Down Expand Up @@ -807,8 +808,9 @@ def load(self):
self.bufpos += 1
return _load_dispatch[c](self)
except KeyError:
exception = ValueError("bad marshal code at position %d: %c"
% (self.bufpos - 1,c))
exception = ValueError(
"bad marshal code at position %d: %c" % (self.bufpos - 1, c)
)
except IndexError:
exception = EOFError
raise exception
Expand Down
10 changes: 8 additions & 2 deletions xdis/unmarshal.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@
from struct import unpack

from xdis.codetype import to_portable
from xdis.cross_types import LongTypeForPython3
from xdis.magics import magic_int2tuple
from xdis.version_info import IS_PYPY, PYTHON3, PYTHON_VERSION_TRIPLE

if PYTHON3:

def long(n):
return n
return LongTypeForPython3(n)

else:
import unicodedata
Expand Down Expand Up @@ -211,7 +212,8 @@ def r_object(self, bytes_for_s=False):
byte1 = byte1 & (FLAG_REF - 1)
marshal_type = chr(byte1)

# print(marshalType) # debug
# print(marshal_type) # debug

if marshal_type in UNMARSHAL_DISPATCH_TABLE:
func_suffix = UNMARSHAL_DISPATCH_TABLE[marshal_type]
unmarshal_func = getattr(self, "t_" + func_suffix)
Expand Down Expand Up @@ -260,9 +262,13 @@ def t_long(self, save_ref, bytes_for_s=False):
d = long(0)
for j in range(0, size):
md = int(unpack("<h", self.fp.read(2))[0])
# This operation and turn "d" from a long back
# into an int.
d += md << j * 15
d = long(d)
if n < 0:
d = long(d * -1)

return self.r_ref(d, save_ref)

# Python 3.4 removed this.
Expand Down

0 comments on commit 30fe258

Please sign in to comment.