You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+79-8Lines changed: 79 additions & 8 deletions
Original file line number
Diff line number
Diff line change
@@ -15,6 +15,8 @@ Otherwise you can clone this repository and import the `aexpr.py` file in the su
15
15
16
16
## Usage
17
17
18
+
If you want to see this tutorial visually you can watch the [screencast](https://github.com/active-expressions/active-expressions-static-python/tree/master/screencast).
19
+
18
20
First you have to import the library:
19
21
20
22
```
@@ -65,24 +67,93 @@ The on-change lambda expression gets three arguments.
65
67
**Be careful:** To get the old and the new value the expression will be executed twice. It should be side-effect free and fast.
66
68
67
69
You can find more examples in the [Example.ipynb](https://github.com/active-expressions/active-expressions-static-python/blob/master/Examples.ipynb)-notebook.
68
-
If you want to see this tutorial visually you can watch the [screencast](https://github.com/active-expressions/active-expressions-static-python/tree/master/screencast).
69
70
70
71
## Implementation
71
72
72
-
This library performs a static byte-code analysis of the expression and all nested methods.
73
-
Therefore it simulates an object-stack and an own variable mapping and processes all byte-code instruction itself.
73
+
This library analysis the given lambda expression and all nested methods to find all dependencies of the result of the lambda expression.
74
+
Dependencies of an expression are in this case all fields which are used in the expression or in nested methods.
75
+
**Example** (from the [presentation](https://github.com/active-expressions/active-expressions-static-python/blob/master/presentation/presentation.pdf)):
76
+
77
+
```
78
+
class Example:
79
+
def __init__(self):
80
+
self.f = 5
81
+
self.g = 10
82
+
83
+
def method(self):
84
+
t = self.f + 2
85
+
return t + self.get_g()
86
+
87
+
def get_g(self):
88
+
return self.g
89
+
90
+
tmp = Example()
91
+
aexpr(lambda: tmp.method())
92
+
```
93
+
94
+
The dependencies are `self.f` and `self.g` (from object `tmp`).
95
+
To be able to monitor the dependencies of the expression, we have to find them first.
96
+
97
+
## Step 1: Find the dependencies
98
+
99
+
Therefore this library performs a static byte-code analysis of the expression and all nested methods.
100
+
It converts the binary byte-code of the expression and all nested methods with the library [`dis`](https://docs.python.org/2/library/dis.html).
101
+
Afterwards it simulates an object-stack and an own variable mapping and processes all byte-code instruction itself.
102
+
103
+
**Short Example** (the complete one is in the [presentation](https://github.com/active-expressions/active-expressions-static-python/blob/master/presentation/presentation.pdf)):
104
+
```
105
+
...
106
+
LOAD_FAST (self)
107
+
LOAD_ATTR (f)
108
+
LOAD_CONST (2)
109
+
...
110
+
```
111
+
112
+
`LOAD_FAST` pushes an object (`self` in this case which is equals to `tmp`) to the object stack.
113
+
Afterwards `LOAD_ATTR` pulls the top of stack object and gets the attribute `f` from this attribute.
114
+
Now we found a dependency of the expression.
115
+
More general: **Always when we process a `LOAD_ATTR` instruction we find a dependency.**
116
+
The attribute will be pushed on the stack and the simulation continues with `LOAD_CONST`.
117
+
The [`aexpr`-method](https://github.com/active-expressions/active-expressions-static-python/blob/master/aexpr/aexpr.py#L72) performs this static byte-code analysis.
118
+
119
+
This implementation performs not all byte-codes in all details.
120
+
It abstracts some of the instructions.
121
+
An addition for example only takes two elements from the object stack and pushes a placeholder on this since we do not really care about the result.
122
+
A multiplication does the same.
123
+
Thats the reason why all elements on our own object stack are wrapped in an [ObjectWrapper](https://github.com/active-expressions/active-expressions-static-python/blob/master/aexpr/aexpr.py#L29), which can be an real object or a placeholder.
124
+
125
+
## Step 2: Monitor the dependencies
126
+
127
+
When we found the dependencies we have to monitor them to be able to trigger if something changes.
128
+
For all dependencies (attribute of a object) we modify the `__setattr__`-method of the object, which will be called when setting a attribute of that object.
129
+
We install a hook in that `__setattr__`-method which checks if we monitor that specific attribute and calls all triggers if so.
130
+
The [method `placeaexpr`](https://github.com/active-expressions/active-expressions-static-python/blob/master/aexpr/aexpr.py#L46) installs these hooks.
131
+
74
132
Currently around 54% of all byte-code instructions are supported.
75
133
See the contribution section if you want to increase this number ;)
76
134
77
-
An example for the analysis is shown in the [presentation](https://github.com/active-expressions/active-expressions-static-python/blob/master/presentation/presentation.pdf).
135
+
The full example for this analysis is shown in the [presentation](https://github.com/active-expressions/active-expressions-static-python/blob/master/presentation/presentation.pdf).
78
136
79
137
## Limitations
80
138
81
-
This library does not support data structures like lists, sets, maps, ...
82
-
It also can not monitor local variables.
83
-
See some more limitations in the [presentation](https://github.com/active-expressions/active-expressions-static-python/blob/master/presentation/presentation.pdf).
139
+
This library has the few following limitations. Feel free to contribute and fix these limitations:
140
+
141
+
-**Lists, Sets, Maps:** Datastructures are not supported so far. Means if you store a dependency in a list and access the an attribute later, you can not monitor on that attribute.
142
+
-**Local Variables:** Local Variables are not instrumentable since they do not have a `__setattr__` or something else. Only fields of objects are instrumentable.
143
+
-**External Resources:** Monitoring if a server is available or a file exists would require to poll this information repeatedly. This is not supported.
144
+
-**Transactions:** Each time a dependency changes all triggers are triggered. Its not possible to pause this to change more attributes at once.
145
+
-**Other language features:** Not supported are for examples *exceptions* and *closures**; *concurreny*, *asynchrony* and *meta-programming* can cause issues as well.
146
+
147
+
You can find some code examples for some of them in the [presentation](https://github.com/active-expressions/active-expressions-static-python/blob/master/presentation/presentation.pdf).
84
148
85
149
## Contribution
86
150
87
151
If you have some complex expression to monitor it can happen that you get an `UnimplementedInstructionException`.
88
-
Afterwards you see the unsupported byte-code-instruction. Feel free to create pull request to this repository to support that instruction.
152
+
This means that you try to perform an instruction which is not so far supported.
153
+
Afterwards you see the unsupported byte-code-instruction.
154
+
Feel free to create pull request to this repository to support that instruction.
155
+
156
+
To support a new instruction you have to modify the content of the `aexpr`-method.
157
+
Call the method `opcode` once with the new supported op-codes of the instruction and call the result with a method which performs the required actions.
158
+
Therefore you get the instruction, the rest of the instruction queue, the object stack and the variable mapping as parameters.
159
+
There are already a lot of examples for this in this method.
0 commit comments