-
Notifications
You must be signed in to change notification settings - Fork 52
/
virtual_namespace.py
123 lines (95 loc) · 3.77 KB
/
virtual_namespace.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
"""
<Author>
Armon Dadgar
<Start Date>
October 21st, 2009
<Description>
This module provides the VirtualNamespace object. This object allows
arbitrary code to be checked for safety, and evaluated within a
specified global context.
"""
import encoding_header # Subtract len(ENCODING_HEADER) from error line numbers.
import safe # Used for safety checking
from exception_hierarchy import *
# This is to work around safe...
safe_compile = compile
# Functional constructor for VirtualNamespace
def createvirtualnamespace(code, name):
return VirtualNamespace(code,name)
# This class is used to represent a namespace
class VirtualNamespace(object):
"""
The VirtualNamespace class is used as a wrapper around an arbitrary
code string that has been verified for safety. The namespace provides
a method of evaluating the code with an arbitrary global context.
"""
# Constructor
def __init__(self, code, name):
"""
<Purpose>
Initializes the VirtualNamespace class.
<Arguments>
code:
(String) The code to run in the namespace
name:
(String, optional) The name to use for the code. When the module is
being executed, if there is an exception, this name will appear in
the traceback.
<Exceptions>
A safety check is performed on the code, and a CodeUnsafeError exception will be raised
if the code fails the safety check.
If code or name are not string types, a RepyArgumentError exception will be raised.
"""
# Check for the code
# Do a type check
if type(code) is not str:
raise RepyArgumentError, "Code must be a string!"
if type(name) is not str:
raise RepyArgumentError, "Name must be a string!"
# Remove any windows carriage returns
code = code.replace('\r\n','\n')
# Prepend an encoding string to protect against bugs in that code,
# see SeattleTestbed/repy_v1#120.
# This causes tracebacks to have an inaccurate line number, so we adjust
# them in multiple modules. See SeattleTestbed/repy_v2#95.
code = encoding_header.ENCODING_DECLARATION + code
# Do a safety check
try:
safe.serial_safe_check(code)
except Exception, e:
raise CodeUnsafeError, "Code failed safety check! Error: "+str(e)
# All good, store the compiled byte code
self.code = safe_compile(code,name,"exec")
# Evaluates the virtual namespace
def evaluate(self,context):
"""
<Purpose>
Evaluates the wrapped code within a context.
<Arguments>
context: A global context to use when executing the code.
This should be a SafeDict object, but if a dict object is provided
it will automatically be converted to a SafeDict object.
<Exceptions>
Any that may be raised by the code that is being evaluated.
A RepyArgumentError exception will be raised if the provided context is not
a safe dictionary object or a ContextUnsafeError if the
context is a dict but cannot be converted into a SafeDict.
<Returns>
The context dictionary that was used during evaluation.
If the context was a dict object, this will be a new
SafeDict object. If the context was a SafeDict object,
then this will return the same context object.
"""
# Try to convert a normal dict into a SafeDict
if type(context) is dict:
try:
context = safe.SafeDict(context)
except Exception, e:
raise ContextUnsafeError, "Provided context is not safe! Exception: "+str(e)
# Type check
if not isinstance(context, safe.SafeDict):
raise RepyArgumentError, "Provided context is not a safe dictionary!"
# Call safe_run with the underlying dictionary
safe.safe_run(self.code, context.__under__)
# Return the dictionary we used
return context