]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | """Restricted execution facilities.\r |
2 | \r | |
3 | The class RExec exports methods r_exec(), r_eval(), r_execfile(), and\r | |
4 | r_import(), which correspond roughly to the built-in operations\r | |
5 | exec, eval(), execfile() and import, but executing the code in an\r | |
6 | environment that only exposes those built-in operations that are\r | |
7 | deemed safe. To this end, a modest collection of 'fake' modules is\r | |
8 | created which mimics the standard modules by the same names. It is a\r | |
9 | policy decision which built-in modules and operations are made\r | |
10 | available; this module provides a reasonable default, but derived\r | |
11 | classes can change the policies e.g. by overriding or extending class\r | |
12 | variables like ok_builtin_modules or methods like make_sys().\r | |
13 | \r | |
14 | XXX To do:\r | |
15 | - r_open should allow writing tmp dir\r | |
16 | - r_exec etc. with explicit globals/locals? (Use rexec("exec ... in ...")?)\r | |
17 | \r | |
18 | """\r | |
19 | from warnings import warnpy3k\r | |
20 | warnpy3k("the rexec module has been removed in Python 3.0", stacklevel=2)\r | |
21 | del warnpy3k\r | |
22 | \r | |
23 | \r | |
24 | import sys\r | |
25 | import __builtin__\r | |
26 | import os\r | |
27 | import ihooks\r | |
28 | import imp\r | |
29 | \r | |
30 | __all__ = ["RExec"]\r | |
31 | \r | |
32 | class FileBase:\r | |
33 | \r | |
34 | ok_file_methods = ('fileno', 'flush', 'isatty', 'read', 'readline',\r | |
35 | 'readlines', 'seek', 'tell', 'write', 'writelines', 'xreadlines',\r | |
36 | '__iter__')\r | |
37 | \r | |
38 | \r | |
39 | class FileWrapper(FileBase):\r | |
40 | \r | |
41 | # XXX This is just like a Bastion -- should use that!\r | |
42 | \r | |
43 | def __init__(self, f):\r | |
44 | for m in self.ok_file_methods:\r | |
45 | if not hasattr(self, m) and hasattr(f, m):\r | |
46 | setattr(self, m, getattr(f, m))\r | |
47 | \r | |
48 | def close(self):\r | |
49 | self.flush()\r | |
50 | \r | |
51 | \r | |
52 | TEMPLATE = """\r | |
53 | def %s(self, *args):\r | |
54 | return getattr(self.mod, self.name).%s(*args)\r | |
55 | """\r | |
56 | \r | |
57 | class FileDelegate(FileBase):\r | |
58 | \r | |
59 | def __init__(self, mod, name):\r | |
60 | self.mod = mod\r | |
61 | self.name = name\r | |
62 | \r | |
63 | for m in FileBase.ok_file_methods + ('close',):\r | |
64 | exec TEMPLATE % (m, m)\r | |
65 | \r | |
66 | \r | |
67 | class RHooks(ihooks.Hooks):\r | |
68 | \r | |
69 | def __init__(self, *args):\r | |
70 | # Hacks to support both old and new interfaces:\r | |
71 | # old interface was RHooks(rexec[, verbose])\r | |
72 | # new interface is RHooks([verbose])\r | |
73 | verbose = 0\r | |
74 | rexec = None\r | |
75 | if args and type(args[-1]) == type(0):\r | |
76 | verbose = args[-1]\r | |
77 | args = args[:-1]\r | |
78 | if args and hasattr(args[0], '__class__'):\r | |
79 | rexec = args[0]\r | |
80 | args = args[1:]\r | |
81 | if args:\r | |
82 | raise TypeError, "too many arguments"\r | |
83 | ihooks.Hooks.__init__(self, verbose)\r | |
84 | self.rexec = rexec\r | |
85 | \r | |
86 | def set_rexec(self, rexec):\r | |
87 | # Called by RExec instance to complete initialization\r | |
88 | self.rexec = rexec\r | |
89 | \r | |
90 | def get_suffixes(self):\r | |
91 | return self.rexec.get_suffixes()\r | |
92 | \r | |
93 | def is_builtin(self, name):\r | |
94 | return self.rexec.is_builtin(name)\r | |
95 | \r | |
96 | def init_builtin(self, name):\r | |
97 | m = __import__(name)\r | |
98 | return self.rexec.copy_except(m, ())\r | |
99 | \r | |
100 | def init_frozen(self, name): raise SystemError, "don't use this"\r | |
101 | def load_source(self, *args): raise SystemError, "don't use this"\r | |
102 | def load_compiled(self, *args): raise SystemError, "don't use this"\r | |
103 | def load_package(self, *args): raise SystemError, "don't use this"\r | |
104 | \r | |
105 | def load_dynamic(self, name, filename, file):\r | |
106 | return self.rexec.load_dynamic(name, filename, file)\r | |
107 | \r | |
108 | def add_module(self, name):\r | |
109 | return self.rexec.add_module(name)\r | |
110 | \r | |
111 | def modules_dict(self):\r | |
112 | return self.rexec.modules\r | |
113 | \r | |
114 | def default_path(self):\r | |
115 | return self.rexec.modules['sys'].path\r | |
116 | \r | |
117 | \r | |
118 | # XXX Backwards compatibility\r | |
119 | RModuleLoader = ihooks.FancyModuleLoader\r | |
120 | RModuleImporter = ihooks.ModuleImporter\r | |
121 | \r | |
122 | \r | |
123 | class RExec(ihooks._Verbose):\r | |
124 | """Basic restricted execution framework.\r | |
125 | \r | |
126 | Code executed in this restricted environment will only have access to\r | |
127 | modules and functions that are deemed safe; you can subclass RExec to\r | |
128 | add or remove capabilities as desired.\r | |
129 | \r | |
130 | The RExec class can prevent code from performing unsafe operations like\r | |
131 | reading or writing disk files, or using TCP/IP sockets. However, it does\r | |
132 | not protect against code using extremely large amounts of memory or\r | |
133 | processor time.\r | |
134 | \r | |
135 | """\r | |
136 | \r | |
137 | ok_path = tuple(sys.path) # That's a policy decision\r | |
138 | \r | |
139 | ok_builtin_modules = ('audioop', 'array', 'binascii',\r | |
140 | 'cmath', 'errno', 'imageop',\r | |
141 | 'marshal', 'math', 'md5', 'operator',\r | |
142 | 'parser', 'select',\r | |
143 | 'sha', '_sre', 'strop', 'struct', 'time',\r | |
144 | '_weakref')\r | |
145 | \r | |
146 | ok_posix_names = ('error', 'fstat', 'listdir', 'lstat', 'readlink',\r | |
147 | 'stat', 'times', 'uname', 'getpid', 'getppid',\r | |
148 | 'getcwd', 'getuid', 'getgid', 'geteuid', 'getegid')\r | |
149 | \r | |
150 | ok_sys_names = ('byteorder', 'copyright', 'exit', 'getdefaultencoding',\r | |
151 | 'getrefcount', 'hexversion', 'maxint', 'maxunicode',\r | |
152 | 'platform', 'ps1', 'ps2', 'version', 'version_info')\r | |
153 | \r | |
154 | nok_builtin_names = ('open', 'file', 'reload', '__import__')\r | |
155 | \r | |
156 | ok_file_types = (imp.C_EXTENSION, imp.PY_SOURCE)\r | |
157 | \r | |
158 | def __init__(self, hooks = None, verbose = 0):\r | |
159 | """Returns an instance of the RExec class.\r | |
160 | \r | |
161 | The hooks parameter is an instance of the RHooks class or a subclass\r | |
162 | of it. If it is omitted or None, the default RHooks class is\r | |
163 | instantiated.\r | |
164 | \r | |
165 | Whenever the RExec module searches for a module (even a built-in one)\r | |
166 | or reads a module's code, it doesn't actually go out to the file\r | |
167 | system itself. Rather, it calls methods of an RHooks instance that\r | |
168 | was passed to or created by its constructor. (Actually, the RExec\r | |
169 | object doesn't make these calls --- they are made by a module loader\r | |
170 | object that's part of the RExec object. This allows another level of\r | |
171 | flexibility, which can be useful when changing the mechanics of\r | |
172 | import within the restricted environment.)\r | |
173 | \r | |
174 | By providing an alternate RHooks object, we can control the file\r | |
175 | system accesses made to import a module, without changing the\r | |
176 | actual algorithm that controls the order in which those accesses are\r | |
177 | made. For instance, we could substitute an RHooks object that\r | |
178 | passes all filesystem requests to a file server elsewhere, via some\r | |
179 | RPC mechanism such as ILU. Grail's applet loader uses this to support\r | |
180 | importing applets from a URL for a directory.\r | |
181 | \r | |
182 | If the verbose parameter is true, additional debugging output may be\r | |
183 | sent to standard output.\r | |
184 | \r | |
185 | """\r | |
186 | \r | |
187 | raise RuntimeError, "This code is not secure in Python 2.2 and later"\r | |
188 | \r | |
189 | ihooks._Verbose.__init__(self, verbose)\r | |
190 | # XXX There's a circular reference here:\r | |
191 | self.hooks = hooks or RHooks(verbose)\r | |
192 | self.hooks.set_rexec(self)\r | |
193 | self.modules = {}\r | |
194 | self.ok_dynamic_modules = self.ok_builtin_modules\r | |
195 | list = []\r | |
196 | for mname in self.ok_builtin_modules:\r | |
197 | if mname in sys.builtin_module_names:\r | |
198 | list.append(mname)\r | |
199 | self.ok_builtin_modules = tuple(list)\r | |
200 | self.set_trusted_path()\r | |
201 | self.make_builtin()\r | |
202 | self.make_initial_modules()\r | |
203 | # make_sys must be last because it adds the already created\r | |
204 | # modules to its builtin_module_names\r | |
205 | self.make_sys()\r | |
206 | self.loader = RModuleLoader(self.hooks, verbose)\r | |
207 | self.importer = RModuleImporter(self.loader, verbose)\r | |
208 | \r | |
209 | def set_trusted_path(self):\r | |
210 | # Set the path from which dynamic modules may be loaded.\r | |
211 | # Those dynamic modules must also occur in ok_builtin_modules\r | |
212 | self.trusted_path = filter(os.path.isabs, sys.path)\r | |
213 | \r | |
214 | def load_dynamic(self, name, filename, file):\r | |
215 | if name not in self.ok_dynamic_modules:\r | |
216 | raise ImportError, "untrusted dynamic module: %s" % name\r | |
217 | if name in sys.modules:\r | |
218 | src = sys.modules[name]\r | |
219 | else:\r | |
220 | src = imp.load_dynamic(name, filename, file)\r | |
221 | dst = self.copy_except(src, [])\r | |
222 | return dst\r | |
223 | \r | |
224 | def make_initial_modules(self):\r | |
225 | self.make_main()\r | |
226 | self.make_osname()\r | |
227 | \r | |
228 | # Helpers for RHooks\r | |
229 | \r | |
230 | def get_suffixes(self):\r | |
231 | return [item # (suff, mode, type)\r | |
232 | for item in imp.get_suffixes()\r | |
233 | if item[2] in self.ok_file_types]\r | |
234 | \r | |
235 | def is_builtin(self, mname):\r | |
236 | return mname in self.ok_builtin_modules\r | |
237 | \r | |
238 | # The make_* methods create specific built-in modules\r | |
239 | \r | |
240 | def make_builtin(self):\r | |
241 | m = self.copy_except(__builtin__, self.nok_builtin_names)\r | |
242 | m.__import__ = self.r_import\r | |
243 | m.reload = self.r_reload\r | |
244 | m.open = m.file = self.r_open\r | |
245 | \r | |
246 | def make_main(self):\r | |
247 | self.add_module('__main__')\r | |
248 | \r | |
249 | def make_osname(self):\r | |
250 | osname = os.name\r | |
251 | src = __import__(osname)\r | |
252 | dst = self.copy_only(src, self.ok_posix_names)\r | |
253 | dst.environ = e = {}\r | |
254 | for key, value in os.environ.items():\r | |
255 | e[key] = value\r | |
256 | \r | |
257 | def make_sys(self):\r | |
258 | m = self.copy_only(sys, self.ok_sys_names)\r | |
259 | m.modules = self.modules\r | |
260 | m.argv = ['RESTRICTED']\r | |
261 | m.path = map(None, self.ok_path)\r | |
262 | m.exc_info = self.r_exc_info\r | |
263 | m = self.modules['sys']\r | |
264 | l = self.modules.keys() + list(self.ok_builtin_modules)\r | |
265 | l.sort()\r | |
266 | m.builtin_module_names = tuple(l)\r | |
267 | \r | |
268 | # The copy_* methods copy existing modules with some changes\r | |
269 | \r | |
270 | def copy_except(self, src, exceptions):\r | |
271 | dst = self.copy_none(src)\r | |
272 | for name in dir(src):\r | |
273 | setattr(dst, name, getattr(src, name))\r | |
274 | for name in exceptions:\r | |
275 | try:\r | |
276 | delattr(dst, name)\r | |
277 | except AttributeError:\r | |
278 | pass\r | |
279 | return dst\r | |
280 | \r | |
281 | def copy_only(self, src, names):\r | |
282 | dst = self.copy_none(src)\r | |
283 | for name in names:\r | |
284 | try:\r | |
285 | value = getattr(src, name)\r | |
286 | except AttributeError:\r | |
287 | continue\r | |
288 | setattr(dst, name, value)\r | |
289 | return dst\r | |
290 | \r | |
291 | def copy_none(self, src):\r | |
292 | m = self.add_module(src.__name__)\r | |
293 | m.__doc__ = src.__doc__\r | |
294 | return m\r | |
295 | \r | |
296 | # Add a module -- return an existing module or create one\r | |
297 | \r | |
298 | def add_module(self, mname):\r | |
299 | m = self.modules.get(mname)\r | |
300 | if m is None:\r | |
301 | self.modules[mname] = m = self.hooks.new_module(mname)\r | |
302 | m.__builtins__ = self.modules['__builtin__']\r | |
303 | return m\r | |
304 | \r | |
305 | # The r* methods are public interfaces\r | |
306 | \r | |
307 | def r_exec(self, code):\r | |
308 | """Execute code within a restricted environment.\r | |
309 | \r | |
310 | The code parameter must either be a string containing one or more\r | |
311 | lines of Python code, or a compiled code object, which will be\r | |
312 | executed in the restricted environment's __main__ module.\r | |
313 | \r | |
314 | """\r | |
315 | m = self.add_module('__main__')\r | |
316 | exec code in m.__dict__\r | |
317 | \r | |
318 | def r_eval(self, code):\r | |
319 | """Evaluate code within a restricted environment.\r | |
320 | \r | |
321 | The code parameter must either be a string containing a Python\r | |
322 | expression, or a compiled code object, which will be evaluated in\r | |
323 | the restricted environment's __main__ module. The value of the\r | |
324 | expression or code object will be returned.\r | |
325 | \r | |
326 | """\r | |
327 | m = self.add_module('__main__')\r | |
328 | return eval(code, m.__dict__)\r | |
329 | \r | |
330 | def r_execfile(self, file):\r | |
331 | """Execute the Python code in the file in the restricted\r | |
332 | environment's __main__ module.\r | |
333 | \r | |
334 | """\r | |
335 | m = self.add_module('__main__')\r | |
336 | execfile(file, m.__dict__)\r | |
337 | \r | |
338 | def r_import(self, mname, globals={}, locals={}, fromlist=[]):\r | |
339 | """Import a module, raising an ImportError exception if the module\r | |
340 | is considered unsafe.\r | |
341 | \r | |
342 | This method is implicitly called by code executing in the\r | |
343 | restricted environment. Overriding this method in a subclass is\r | |
344 | used to change the policies enforced by a restricted environment.\r | |
345 | \r | |
346 | """\r | |
347 | return self.importer.import_module(mname, globals, locals, fromlist)\r | |
348 | \r | |
349 | def r_reload(self, m):\r | |
350 | """Reload the module object, re-parsing and re-initializing it.\r | |
351 | \r | |
352 | This method is implicitly called by code executing in the\r | |
353 | restricted environment. Overriding this method in a subclass is\r | |
354 | used to change the policies enforced by a restricted environment.\r | |
355 | \r | |
356 | """\r | |
357 | return self.importer.reload(m)\r | |
358 | \r | |
359 | def r_unload(self, m):\r | |
360 | """Unload the module.\r | |
361 | \r | |
362 | Removes it from the restricted environment's sys.modules dictionary.\r | |
363 | \r | |
364 | This method is implicitly called by code executing in the\r | |
365 | restricted environment. Overriding this method in a subclass is\r | |
366 | used to change the policies enforced by a restricted environment.\r | |
367 | \r | |
368 | """\r | |
369 | return self.importer.unload(m)\r | |
370 | \r | |
371 | # The s_* methods are similar but also swap std{in,out,err}\r | |
372 | \r | |
373 | def make_delegate_files(self):\r | |
374 | s = self.modules['sys']\r | |
375 | self.delegate_stdin = FileDelegate(s, 'stdin')\r | |
376 | self.delegate_stdout = FileDelegate(s, 'stdout')\r | |
377 | self.delegate_stderr = FileDelegate(s, 'stderr')\r | |
378 | self.restricted_stdin = FileWrapper(sys.stdin)\r | |
379 | self.restricted_stdout = FileWrapper(sys.stdout)\r | |
380 | self.restricted_stderr = FileWrapper(sys.stderr)\r | |
381 | \r | |
382 | def set_files(self):\r | |
383 | if not hasattr(self, 'save_stdin'):\r | |
384 | self.save_files()\r | |
385 | if not hasattr(self, 'delegate_stdin'):\r | |
386 | self.make_delegate_files()\r | |
387 | s = self.modules['sys']\r | |
388 | s.stdin = self.restricted_stdin\r | |
389 | s.stdout = self.restricted_stdout\r | |
390 | s.stderr = self.restricted_stderr\r | |
391 | sys.stdin = self.delegate_stdin\r | |
392 | sys.stdout = self.delegate_stdout\r | |
393 | sys.stderr = self.delegate_stderr\r | |
394 | \r | |
395 | def reset_files(self):\r | |
396 | self.restore_files()\r | |
397 | s = self.modules['sys']\r | |
398 | self.restricted_stdin = s.stdin\r | |
399 | self.restricted_stdout = s.stdout\r | |
400 | self.restricted_stderr = s.stderr\r | |
401 | \r | |
402 | \r | |
403 | def save_files(self):\r | |
404 | self.save_stdin = sys.stdin\r | |
405 | self.save_stdout = sys.stdout\r | |
406 | self.save_stderr = sys.stderr\r | |
407 | \r | |
408 | def restore_files(self):\r | |
409 | sys.stdin = self.save_stdin\r | |
410 | sys.stdout = self.save_stdout\r | |
411 | sys.stderr = self.save_stderr\r | |
412 | \r | |
413 | def s_apply(self, func, args=(), kw={}):\r | |
414 | self.save_files()\r | |
415 | try:\r | |
416 | self.set_files()\r | |
417 | r = func(*args, **kw)\r | |
418 | finally:\r | |
419 | self.restore_files()\r | |
420 | return r\r | |
421 | \r | |
422 | def s_exec(self, *args):\r | |
423 | """Execute code within a restricted environment.\r | |
424 | \r | |
425 | Similar to the r_exec() method, but the code will be granted access\r | |
426 | to restricted versions of the standard I/O streams sys.stdin,\r | |
427 | sys.stderr, and sys.stdout.\r | |
428 | \r | |
429 | The code parameter must either be a string containing one or more\r | |
430 | lines of Python code, or a compiled code object, which will be\r | |
431 | executed in the restricted environment's __main__ module.\r | |
432 | \r | |
433 | """\r | |
434 | return self.s_apply(self.r_exec, args)\r | |
435 | \r | |
436 | def s_eval(self, *args):\r | |
437 | """Evaluate code within a restricted environment.\r | |
438 | \r | |
439 | Similar to the r_eval() method, but the code will be granted access\r | |
440 | to restricted versions of the standard I/O streams sys.stdin,\r | |
441 | sys.stderr, and sys.stdout.\r | |
442 | \r | |
443 | The code parameter must either be a string containing a Python\r | |
444 | expression, or a compiled code object, which will be evaluated in\r | |
445 | the restricted environment's __main__ module. The value of the\r | |
446 | expression or code object will be returned.\r | |
447 | \r | |
448 | """\r | |
449 | return self.s_apply(self.r_eval, args)\r | |
450 | \r | |
451 | def s_execfile(self, *args):\r | |
452 | """Execute the Python code in the file in the restricted\r | |
453 | environment's __main__ module.\r | |
454 | \r | |
455 | Similar to the r_execfile() method, but the code will be granted\r | |
456 | access to restricted versions of the standard I/O streams sys.stdin,\r | |
457 | sys.stderr, and sys.stdout.\r | |
458 | \r | |
459 | """\r | |
460 | return self.s_apply(self.r_execfile, args)\r | |
461 | \r | |
462 | def s_import(self, *args):\r | |
463 | """Import a module, raising an ImportError exception if the module\r | |
464 | is considered unsafe.\r | |
465 | \r | |
466 | This method is implicitly called by code executing in the\r | |
467 | restricted environment. Overriding this method in a subclass is\r | |
468 | used to change the policies enforced by a restricted environment.\r | |
469 | \r | |
470 | Similar to the r_import() method, but has access to restricted\r | |
471 | versions of the standard I/O streams sys.stdin, sys.stderr, and\r | |
472 | sys.stdout.\r | |
473 | \r | |
474 | """\r | |
475 | return self.s_apply(self.r_import, args)\r | |
476 | \r | |
477 | def s_reload(self, *args):\r | |
478 | """Reload the module object, re-parsing and re-initializing it.\r | |
479 | \r | |
480 | This method is implicitly called by code executing in the\r | |
481 | restricted environment. Overriding this method in a subclass is\r | |
482 | used to change the policies enforced by a restricted environment.\r | |
483 | \r | |
484 | Similar to the r_reload() method, but has access to restricted\r | |
485 | versions of the standard I/O streams sys.stdin, sys.stderr, and\r | |
486 | sys.stdout.\r | |
487 | \r | |
488 | """\r | |
489 | return self.s_apply(self.r_reload, args)\r | |
490 | \r | |
491 | def s_unload(self, *args):\r | |
492 | """Unload the module.\r | |
493 | \r | |
494 | Removes it from the restricted environment's sys.modules dictionary.\r | |
495 | \r | |
496 | This method is implicitly called by code executing in the\r | |
497 | restricted environment. Overriding this method in a subclass is\r | |
498 | used to change the policies enforced by a restricted environment.\r | |
499 | \r | |
500 | Similar to the r_unload() method, but has access to restricted\r | |
501 | versions of the standard I/O streams sys.stdin, sys.stderr, and\r | |
502 | sys.stdout.\r | |
503 | \r | |
504 | """\r | |
505 | return self.s_apply(self.r_unload, args)\r | |
506 | \r | |
507 | # Restricted open(...)\r | |
508 | \r | |
509 | def r_open(self, file, mode='r', buf=-1):\r | |
510 | """Method called when open() is called in the restricted environment.\r | |
511 | \r | |
512 | The arguments are identical to those of the open() function, and a\r | |
513 | file object (or a class instance compatible with file objects)\r | |
514 | should be returned. RExec's default behaviour is allow opening\r | |
515 | any file for reading, but forbidding any attempt to write a file.\r | |
516 | \r | |
517 | This method is implicitly called by code executing in the\r | |
518 | restricted environment. Overriding this method in a subclass is\r | |
519 | used to change the policies enforced by a restricted environment.\r | |
520 | \r | |
521 | """\r | |
522 | mode = str(mode)\r | |
523 | if mode not in ('r', 'rb'):\r | |
524 | raise IOError, "can't open files for writing in restricted mode"\r | |
525 | return open(file, mode, buf)\r | |
526 | \r | |
527 | # Restricted version of sys.exc_info()\r | |
528 | \r | |
529 | def r_exc_info(self):\r | |
530 | ty, va, tr = sys.exc_info()\r | |
531 | tr = None\r | |
532 | return ty, va, tr\r | |
533 | \r | |
534 | \r | |
535 | def test():\r | |
536 | import getopt, traceback\r | |
537 | opts, args = getopt.getopt(sys.argv[1:], 'vt:')\r | |
538 | verbose = 0\r | |
539 | trusted = []\r | |
540 | for o, a in opts:\r | |
541 | if o == '-v':\r | |
542 | verbose = verbose+1\r | |
543 | if o == '-t':\r | |
544 | trusted.append(a)\r | |
545 | r = RExec(verbose=verbose)\r | |
546 | if trusted:\r | |
547 | r.ok_builtin_modules = r.ok_builtin_modules + tuple(trusted)\r | |
548 | if args:\r | |
549 | r.modules['sys'].argv = args\r | |
550 | r.modules['sys'].path.insert(0, os.path.dirname(args[0]))\r | |
551 | else:\r | |
552 | r.modules['sys'].path.insert(0, "")\r | |
553 | fp = sys.stdin\r | |
554 | if args and args[0] != '-':\r | |
555 | try:\r | |
556 | fp = open(args[0])\r | |
557 | except IOError, msg:\r | |
558 | print "%s: can't open file %r" % (sys.argv[0], args[0])\r | |
559 | return 1\r | |
560 | if fp.isatty():\r | |
561 | try:\r | |
562 | import readline\r | |
563 | except ImportError:\r | |
564 | pass\r | |
565 | import code\r | |
566 | class RestrictedConsole(code.InteractiveConsole):\r | |
567 | def runcode(self, co):\r | |
568 | self.locals['__builtins__'] = r.modules['__builtin__']\r | |
569 | r.s_apply(code.InteractiveConsole.runcode, (self, co))\r | |
570 | try:\r | |
571 | RestrictedConsole(r.modules['__main__'].__dict__).interact()\r | |
572 | except SystemExit, n:\r | |
573 | return n\r | |
574 | else:\r | |
575 | text = fp.read()\r | |
576 | fp.close()\r | |
577 | c = compile(text, fp.name, 'exec')\r | |
578 | try:\r | |
579 | r.s_exec(c)\r | |
580 | except SystemExit, n:\r | |
581 | return n\r | |
582 | except:\r | |
583 | traceback.print_exc()\r | |
584 | return 1\r | |
585 | \r | |
586 | \r | |
587 | if __name__ == '__main__':\r | |
588 | sys.exit(test())\r |