Skip to content

Commit 00b650f

Browse files
authored
Merge pull request #1 from active-expressions/python27support
Add support for python 2.7
2 parents edb42ac + aff0f54 commit 00b650f

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed

Examples.ipynb

+5-5
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
}
4444
],
4545
"source": [
46-
"class BankAccount:\n",
46+
"class BankAccount(object):\n",
4747
" def __init__(self):\n",
4848
" self.balance = 0\n",
4949
" \n",
@@ -92,12 +92,12 @@
9292
}
9393
],
9494
"source": [
95-
"class Point:\n",
95+
"class Point(object):\n",
9696
" def __init__(self, x, y):\n",
9797
" self.x = x\n",
9898
" self.y = y\n",
9999
"\n",
100-
"class Rect:\n",
100+
"class Rect(object):\n",
101101
" def __init__(self, point1, point2):\n",
102102
" self.point1 = point1\n",
103103
" self.point2 = point2\n",
@@ -139,7 +139,7 @@
139139
}
140140
],
141141
"source": [
142-
"class Example:\n",
142+
"class Example(object):\n",
143143
" def __init__(self):\n",
144144
" self.f = 5\n",
145145
" self.g = 10\n",
@@ -225,7 +225,7 @@
225225
}
226226
],
227227
"source": [
228-
"class MoreExamples:\n",
228+
"class MoreExamples(object):\n",
229229
" def __init__(self):\n",
230230
" self.f = 5\n",
231231
" self.g = 6\n",

README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ Active Expression Implementation for Python using static-byte-code analysis.
44

55
## Installation
66

7-
To use Active Expression you need at least Python 3.4.
7+
To use Active Expression you need at least Python 2.7 (earlier version may be supported but not tested) or Python 3.4.
88
To install the package, you can run the following command:
99

1010
```
11+
pip install git+https://github.com/active-expressions/active-expressions-static-python
1112
pip3 install git+https://github.com/active-expressions/active-expressions-static-python
1213
```
1314

@@ -28,7 +29,7 @@ Afterwards you can use the method `aexpr` in your program.
2829
For the following example lets assume we have the following class:
2930

3031
```
31-
class Example:
32+
class Example(object):
3233
def __init__(self):
3334
self.f = 5
3435
```
@@ -75,7 +76,7 @@ Dependencies of an expression are in this case all fields which are used in the
7576
**Example** (from the [presentation](https://github.com/active-expressions/active-expressions-static-python/blob/master/presentation/presentation.pdf)):
7677

7778
```
78-
class Example:
79+
class Example(object):
7980
def __init__(self):
8081
self.f = 5
8182
self.g = 10

aexpr/aexpr.py

+47-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import dis
22
from collections import deque
33
import inspect
4+
import re
5+
import sys
6+
if sys.version_info < (3, .0):
7+
from StringIO import StringIO
8+
49

510
class ExpressionReaction:
611
"""An Expression Reaction object will be called when a change is detected.
@@ -43,10 +48,19 @@ def is_placeholder(self):
4348
def is_build_in(self):
4449
return self.buildin
4550

51+
def is_new_style(cls):
52+
"""Checks if a class is a new style class.
53+
See: https://stackoverflow.com/questions/2654622/identifying-that-a-variable-is-a-new-style-class-in-python"""
54+
return hasattr(cls, '__class__') and ('__dict__' in dir(cls) or hasattr(cls, '__slots__'))
55+
56+
4657
def placeaexpr(obj, attr_name, expression_reaction_object):
4758
"""placeaexpr will be called by the byte-code analysis
4859
when an instrumentable attribute is found."""
4960

61+
if not is_new_style(obj):
62+
raise ValueError('You have to use new style classes')
63+
5064
if not hasattr(obj, '__listenon__'):
5165
if not hasattr(type(obj), "__aexprhandler__"):
5266
original = type(obj).__setattr__
@@ -201,7 +215,7 @@ def call_function_handler(inst, iq, os, vm):
201215
for i in range(inst.argval, 0, -1):
202216
localvars[params.args[i]] = func_args[i-1]
203217

204-
os.append(process_function(func.__code__, wrapper.base_obj, localvars))
218+
os.append(process_function(func, wrapper.base_obj, localvars))
205219
else:
206220
os.append(ObjectWrapper(placeholder=True))
207221

@@ -220,7 +234,7 @@ def get_obj_from_wrapper(wrapper, inst):
220234
print("Warning: Access to Placeholder isn't supported (Instruction: " + str(inst) + ")")
221235
return wrapper.obj
222236

223-
def process_function(function_code, self, localvars=None):
237+
def process_function(function, self, localvars=None):
224238
instruction_queue = deque()
225239
object_stack = []
226240
variable_mapping = {}
@@ -233,7 +247,16 @@ def process_function(function_code, self, localvars=None):
233247
if "self" not in variable_mapping:
234248
variable_mapping["self"] = ObjectWrapper(obj=self)
235249

236-
instruction_queue.extend(dis.get_instructions(function_code))
250+
if sys.version_info > (3, 0):
251+
instruction_queue.extend(dis.get_instructions(function.__code__))
252+
else:
253+
old_stdout = sys.stdout
254+
result = StringIO()
255+
sys.stdout = result
256+
dis.dis(function)
257+
sys.stdout = old_stdout
258+
result_string = result.getvalue()
259+
instruction_queue.extend([Instruction.get_instruction_from_string(s) for s in result_string.splitlines()])
237260

238261
while instruction_queue:
239262
inst = instruction_queue.popleft()
@@ -244,6 +267,26 @@ def process_function(function_code, self, localvars=None):
244267
raise UnimplementedInstructionException("Unimplemented Instruction: " + str(inst))
245268
return object_stack.pop()
246269

247-
process_function(lambda_expression.__code__, None, localvars)
270+
process_function(lambda_expression, None, localvars)
248271

249272
return expression_reaction_object
273+
274+
class Instruction:
275+
def __init__(self, opcode, argval):
276+
self.opcode = opcode
277+
self.argval = argval
278+
279+
def __str__(self):
280+
return str(self.opcode) + " " + str(self.argval)
281+
282+
@staticmethod
283+
def get_instruction_from_string(line):
284+
splits = line.strip().split("(")
285+
value = ""
286+
if len(splits) == 2: # Has a value
287+
value = splits[1].replace(")", "")
288+
instruction = re.sub(r"\d", "", splits[0]).strip()
289+
if instruction not in dis.opmap:
290+
return None
291+
opcode = dis.opmap[instruction]
292+
return Instruction(opcode, value)

0 commit comments

Comments
 (0)