[svn r37674] Added document 'code.txt' that describes py.code, added docstrings to py.code
public items. --HG-- branch : trunk
This commit is contained in:
		
							parent
							
								
									65f51efa55
								
							
						
					
					
						commit
						58eace43f9
					
				|  | @ -1,6 +1,7 @@ | ||||||
| import py | import py | ||||||
| 
 | 
 | ||||||
| class Code(object): | class Code(object): | ||||||
|  |     """ wrapper around Python code objects """ | ||||||
|     def __init__(self, rawcode): |     def __init__(self, rawcode): | ||||||
|         rawcode = getattr(rawcode, 'im_func', rawcode) |         rawcode = getattr(rawcode, 'im_func', rawcode) | ||||||
|         rawcode = getattr(rawcode, 'func_code', rawcode) |         rawcode = getattr(rawcode, 'func_code', rawcode) | ||||||
|  | @ -57,6 +58,7 @@ class Code(object): | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     def path(self): |     def path(self): | ||||||
|  |         """ return a py.path.local object wrapping the source of the code """ | ||||||
|         try: |         try: | ||||||
|             return self.raw.co_filename.__path__ |             return self.raw.co_filename.__path__ | ||||||
|         except AttributeError: |         except AttributeError: | ||||||
|  | @ -64,6 +66,8 @@ class Code(object): | ||||||
|     path = property(path, None, None, "path of this code object") |     path = property(path, None, None, "path of this code object") | ||||||
| 
 | 
 | ||||||
|     def fullsource(self): |     def fullsource(self): | ||||||
|  |         """ return a py.code.Source object for the full source file of the code | ||||||
|  |         """ | ||||||
|         fn = self.raw.co_filename |         fn = self.raw.co_filename | ||||||
|         try: |         try: | ||||||
|             return fn.__source__ |             return fn.__source__ | ||||||
|  | @ -73,11 +77,16 @@ class Code(object): | ||||||
|                           "full source containing this code object") |                           "full source containing this code object") | ||||||
|      |      | ||||||
|     def source(self): |     def source(self): | ||||||
|  |         """ return a py.code.Source object for the code object's source only | ||||||
|  |         """ | ||||||
|         # return source only for that part of code |         # return source only for that part of code | ||||||
|         import inspect |         import inspect | ||||||
|         return py.code.Source(inspect.getsource(self.raw)) |         return py.code.Source(inspect.getsource(self.raw)) | ||||||
| 
 | 
 | ||||||
|     def getargs(self): |     def getargs(self): | ||||||
|  |         """ return a tuple with the argument names for the code object | ||||||
|  |         """ | ||||||
|         # handfull shortcut for getting args |         # handfull shortcut for getting args | ||||||
|         raw = self.raw |         raw = self.raw | ||||||
|         return raw.co_varnames[:raw.co_argcount] |         return raw.co_varnames[:raw.co_argcount] | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -22,6 +22,13 @@ class ExceptionInfo(object): | ||||||
|         self.traceback = py.code.Traceback(tb)  |         self.traceback = py.code.Traceback(tb)  | ||||||
| 
 | 
 | ||||||
|     def exconly(self, tryshort=False):  |     def exconly(self, tryshort=False):  | ||||||
|  |         """ return the exception as a string | ||||||
|  |          | ||||||
|  |             when 'tryshort' resolves to True, and the exception is a | ||||||
|  |             py.magic.AssertionError, only the actual exception part of | ||||||
|  |             the exception representation is returned (so 'AssertionError: ' is | ||||||
|  |             removed from the beginning) | ||||||
|  |         """ | ||||||
|         lines = py.std.traceback.format_exception_only(self.type, self.value) |         lines = py.std.traceback.format_exception_only(self.type, self.value) | ||||||
|         text = ''.join(lines) |         text = ''.join(lines) | ||||||
|         if text.endswith('\n'): |         if text.endswith('\n'): | ||||||
|  | @ -32,6 +39,7 @@ class ExceptionInfo(object): | ||||||
|         return text |         return text | ||||||
| 
 | 
 | ||||||
|     def errisinstance(self, exc):  |     def errisinstance(self, exc):  | ||||||
|  |         """ return True if the exception is an instance of exc """ | ||||||
|         return isinstance(self.value, exc)  |         return isinstance(self.value, exc)  | ||||||
| 
 | 
 | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|  |  | ||||||
|  | @ -18,23 +18,38 @@ class Frame(object): | ||||||
|                          "statement this frame is at") |                          "statement this frame is at") | ||||||
| 
 | 
 | ||||||
|     def eval(self, code, **vars): |     def eval(self, code, **vars): | ||||||
|  |         """ evaluate 'code' in the frame | ||||||
|  | 
 | ||||||
|  |             'vars' are optional additional local variables | ||||||
|  | 
 | ||||||
|  |             returns the result of the evaluation | ||||||
|  |         """ | ||||||
|         f_locals = self.f_locals.copy()  |         f_locals = self.f_locals.copy()  | ||||||
|         f_locals.update(vars) |         f_locals.update(vars) | ||||||
|         return eval(code, self.f_globals, f_locals) |         return eval(code, self.f_globals, f_locals) | ||||||
| 
 | 
 | ||||||
|     def exec_(self, code, **vars): |     def exec_(self, code, **vars): | ||||||
|  |         """ exec 'code' in the frame | ||||||
|  | 
 | ||||||
|  |             'vars' are optiona; additional local variables | ||||||
|  |         """ | ||||||
|         f_locals = self.f_locals.copy()  |         f_locals = self.f_locals.copy()  | ||||||
|         f_locals.update(vars) |         f_locals.update(vars) | ||||||
|         exec code in self.f_globals, f_locals  |         exec code in self.f_globals, f_locals  | ||||||
| 
 | 
 | ||||||
|     def repr(self, object): |     def repr(self, object): | ||||||
|  |         """ return a 'safe' (non-recursive, one-line) string repr for 'object' | ||||||
|  |         """ | ||||||
|         return py.__.code.safe_repr._repr(object) |         return py.__.code.safe_repr._repr(object) | ||||||
| 
 | 
 | ||||||
|     def is_true(self, object): |     def is_true(self, object): | ||||||
|         return object |         return object | ||||||
| 
 | 
 | ||||||
|     def getargs(self): |     def getargs(self): | ||||||
|  |         """ return a list of tuples (name, value) for all arguments | ||||||
|  |         """ | ||||||
|         retval = [] |         retval = [] | ||||||
|         for arg in self.code.getargs(): |         for arg in self.code.getargs(): | ||||||
|             retval.append((arg, self.f_locals[arg])) |             retval.append((arg, self.f_locals[arg])) | ||||||
|         return retval |         return retval | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -2,6 +2,8 @@ from __future__ import generators | ||||||
| import py  | import py  | ||||||
| 
 | 
 | ||||||
| class TracebackEntry(object): | class TracebackEntry(object): | ||||||
|  |     """ a single entry in a traceback """ | ||||||
|  |      | ||||||
|     exprinfo = None  |     exprinfo = None  | ||||||
| 
 | 
 | ||||||
|     def __init__(self, rawentry): |     def __init__(self, rawentry): | ||||||
|  | @ -14,6 +16,7 @@ class TracebackEntry(object): | ||||||
|         return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1) |         return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1) | ||||||
| 
 | 
 | ||||||
|     def statement(self): |     def statement(self): | ||||||
|  |         """ return a py.code.Source object for the current statement """ | ||||||
|         source = self.frame.code.fullsource |         source = self.frame.code.fullsource | ||||||
|         return source.getstatement(self.lineno) |         return source.getstatement(self.lineno) | ||||||
|     statement = property(statement, None, None, |     statement = property(statement, None, None, | ||||||
|  | @ -63,6 +66,11 @@ class TracebackEntry(object): | ||||||
|     source = property(getsource) |     source = property(getsource) | ||||||
| 
 | 
 | ||||||
|     def ishidden(self): |     def ishidden(self): | ||||||
|  |         """ return True if the current frame has a var __tracebackhide__  | ||||||
|  |             resolving to True | ||||||
|  |              | ||||||
|  |             mostly for internal use | ||||||
|  |         """ | ||||||
|         try:  |         try:  | ||||||
|             return self.frame.eval("__tracebackhide__")  |             return self.frame.eval("__tracebackhide__")  | ||||||
|         except (SystemExit, KeyboardInterrupt):  |         except (SystemExit, KeyboardInterrupt):  | ||||||
|  | @ -103,6 +111,15 @@ class Traceback(list): | ||||||
|             list.__init__(self, tb) |             list.__init__(self, tb) | ||||||
| 
 | 
 | ||||||
|     def cut(self, path=None, lineno=None, firstlineno=None): |     def cut(self, path=None, lineno=None, firstlineno=None): | ||||||
|  |         """ return a Traceback instance wrapping part of this Traceback | ||||||
|  | 
 | ||||||
|  |             by provding any combination of path, lineno and firstlineno, the | ||||||
|  |             first frame to start the to-be-returned traceback is determined | ||||||
|  | 
 | ||||||
|  |             this allows cutting the first part of a Traceback instance e.g. | ||||||
|  |             for formatting reasons (removing some uninteresting bits that deal | ||||||
|  |             with handling of the exception/traceback) | ||||||
|  |         """ | ||||||
|         for x in self: |         for x in self: | ||||||
|             if ((path is None or x.frame.code.path == path) and |             if ((path is None or x.frame.code.path == path) and | ||||||
|                 (lineno is None or x.lineno == lineno) and |                 (lineno is None or x.lineno == lineno) and | ||||||
|  | @ -117,6 +134,15 @@ class Traceback(list): | ||||||
|         return val |         return val | ||||||
| 
 | 
 | ||||||
|     def filter(self, fn=lambda x: not x.ishidden()): |     def filter(self, fn=lambda x: not x.ishidden()): | ||||||
|  |         """ return a Traceback instance with certain items removed | ||||||
|  | 
 | ||||||
|  |             fn is a function that gets a single argument, a TracebackItem | ||||||
|  |             instance, and should return True when the item should be added | ||||||
|  |             to the Traceback, False when not | ||||||
|  | 
 | ||||||
|  |             by default this removes all the TracebackItems which are hidden | ||||||
|  |             (see ishidden() above) | ||||||
|  |         """ | ||||||
|         return Traceback(filter(fn, self)) |         return Traceback(filter(fn, self)) | ||||||
| 
 | 
 | ||||||
|     def getcrashentry(self): |     def getcrashentry(self): | ||||||
|  | @ -129,6 +155,9 @@ class Traceback(list): | ||||||
|         return tb[-1] |         return tb[-1] | ||||||
| 
 | 
 | ||||||
|     def recursionindex(self): |     def recursionindex(self): | ||||||
|  |         """ return the index of the frame/TracebackItem where recursion | ||||||
|  |             originates if appropriate, None if no recursion occurred | ||||||
|  |         """ | ||||||
|         cache = {} |         cache = {} | ||||||
|         for i, entry in py.builtin.enumerate(self): |         for i, entry in py.builtin.enumerate(self): | ||||||
|             key = entry.frame.code.path, entry.lineno  |             key = entry.frame.code.path, entry.lineno  | ||||||
|  | @ -155,3 +184,4 @@ class Traceback(list): | ||||||
| 
 | 
 | ||||||
| co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2', | co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2', | ||||||
|                    '?', 'eval') |                    '?', 'eval') | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,141 @@ | ||||||
|  | ============== | ||||||
|  | :api:`py.code` | ||||||
|  | ============== | ||||||
|  | 
 | ||||||
|  | The :api:`py.code` part of the 'py lib' contains some functionality to help | ||||||
|  | dealing with Python code objects. Even though working with Python's internal | ||||||
|  | code objects (as found on frames and callables) can be very powerful, it's | ||||||
|  | usually also quite cumbersome, because the API provided by core Python is | ||||||
|  | relatively low level and not very accessible. | ||||||
|  | 
 | ||||||
|  | The :api:`py.code` library tries to simplify accessing the code objects as well | ||||||
|  | as creating them. There is a small set of interfaces a user needs to deal with, | ||||||
|  | all nicely bundled together, and with a rich set of 'Pythonic' functionality. | ||||||
|  | 
 | ||||||
|  | source: :source:`py/code/` | ||||||
|  | 
 | ||||||
|  | Contents of the library | ||||||
|  | ======================= | ||||||
|  | 
 | ||||||
|  | Every object in the :api:`py.code` library wraps a code Python object related | ||||||
|  | to code objects, source code, frames and tracebacks: the :api:`py.code.Code` | ||||||
|  | class wraps code objects, :api:`py.code.Source` source snippets, | ||||||
|  | :api:`py.code.Traceback` exception tracebacks, :api:`py.code.Frame` frame  | ||||||
|  | objects (as found in e.g. tracebacks) and :api:`py.code.ExceptionInfo` the | ||||||
|  | tuple provided by sys.exc_info() (containing exception and traceback | ||||||
|  | information when an exception occurs). Also in the library is a helper function | ||||||
|  | :api:`py.code.compile()` that provides the same functionality as Python's | ||||||
|  | built-in 'compile()' function, but returns a wrapped code object. | ||||||
|  | 
 | ||||||
|  | The wrappers | ||||||
|  | ============ | ||||||
|  | 
 | ||||||
|  | :api:`py.code.Code` | ||||||
|  | ------------------- | ||||||
|  | 
 | ||||||
|  | Code objects are instantiated with a code object or a callable as argument, | ||||||
|  | and provide functionality to compare themselves with other Code objects, get to | ||||||
|  | the source file or its contents, create new Code objects from scratch, etc. | ||||||
|  | 
 | ||||||
|  | A quick example:: | ||||||
|  | 
 | ||||||
|  |   >>> import py | ||||||
|  |   >>> c = py.code.Code(py.path.local.read) | ||||||
|  |   >>> c.path.basename | ||||||
|  |   'common.py' | ||||||
|  |   >>> isinstance(c.source(), py.code.Source) | ||||||
|  |   True | ||||||
|  |   >>> str(c.source()).split('\n')[0] | ||||||
|  |   "def read(self, mode='rb'):" | ||||||
|  | 
 | ||||||
|  | source: :source:`py/code/code.py` | ||||||
|  | 
 | ||||||
|  | :api:`py.code.Source` | ||||||
|  | --------------------- | ||||||
|  | 
 | ||||||
|  | Source objects wrap snippets of Python source code, providing a simple yet | ||||||
|  | powerful interface to read, deindent, slice, compare, compile and manipulate | ||||||
|  | them, things that are not so easy in core Python. | ||||||
|  | 
 | ||||||
|  | Example:: | ||||||
|  | 
 | ||||||
|  |   >>> s = py.code.Source("""\ | ||||||
|  |   ...   def foo(): | ||||||
|  |   ...     print "foo" | ||||||
|  |   ... """) | ||||||
|  |   >>> str(s).startswith('def') # automatic de-indentation! | ||||||
|  |   True | ||||||
|  |   >>> s.isparseable() | ||||||
|  |   True | ||||||
|  |   >>> sub = s.getstatement(1) # get the statement starting at line 1 | ||||||
|  |   >>> str(sub).strip() # XXX why is the strip() required?!? | ||||||
|  |   'print "foo"' | ||||||
|  | 
 | ||||||
|  | source: :source:`py/code/source.py` | ||||||
|  | 
 | ||||||
|  | :api:`py.code.Traceback` | ||||||
|  | ------------------------ | ||||||
|  | 
 | ||||||
|  | Tracebacks are usually not very easy to examine, you need to access certain | ||||||
|  | somewhat hidden attributes of the traceback's items (resulting in expressions | ||||||
|  | such as 'fname = tb.tb_next.tb_frame.f_code.co_filename'). The Traceback | ||||||
|  | interface (and its TracebackItem children) tries to improve this. | ||||||
|  | 
 | ||||||
|  | Example:: | ||||||
|  | 
 | ||||||
|  |   >>> import sys | ||||||
|  |   >>> try: | ||||||
|  |   ...   py.path.local(100) # illegal argument | ||||||
|  |   ... except: | ||||||
|  |   ...   exc, e, tb = sys.exc_info() | ||||||
|  |   >>> t = py.code.Traceback(tb) | ||||||
|  |   >>> first = t[1] # get the second entry (first is in this doc) | ||||||
|  |   >>> first.path.basename # second is in py/path/local.py | ||||||
|  |   'local.py' | ||||||
|  |   >>> isinstance(first.statement, py.code.Source) | ||||||
|  |   True | ||||||
|  |   >>> str(first.statement).strip().startswith('raise ValueError') | ||||||
|  |   True | ||||||
|  | 
 | ||||||
|  | source: :source:`py/code/traceback2.py` | ||||||
|  | 
 | ||||||
|  | :api:`py.code.Frame` | ||||||
|  | -------------------- | ||||||
|  | 
 | ||||||
|  | Frame wrappers are used in :api:`py.code.Traceback` items, and will usually not | ||||||
|  | directly be instantiated. They provide some nice methods to evaluate code | ||||||
|  | 'inside' the frame (using the frame's local variables), get to the underlying | ||||||
|  | code (frames have a code attribute that points to a :api:`py.code.Code` object) | ||||||
|  | and examine the arguments. | ||||||
|  | 
 | ||||||
|  | Example (using the 'first' TracebackItem instance created above):: | ||||||
|  | 
 | ||||||
|  |   >>> frame = first.frame | ||||||
|  |   >>> isinstance(frame.code, py.code.Code) | ||||||
|  |   True | ||||||
|  |   >>> isinstance(frame.eval('self'), py.__.path.local.local.LocalPath) | ||||||
|  |   True | ||||||
|  |   >>> [namevalue[0] for namevalue in frame.getargs()] | ||||||
|  |   ['cls', 'path'] | ||||||
|  | 
 | ||||||
|  | :api:`py.code.ExceptionInfo` | ||||||
|  | ---------------------------- | ||||||
|  | 
 | ||||||
|  | A wrapper around the tuple returned by sys.exc_info() (will call sys.exc_info() | ||||||
|  | itself if the tuple is not provided as an argument), provides some handy | ||||||
|  | attributes to easily access the traceback and exception string. | ||||||
|  | 
 | ||||||
|  | Example:: | ||||||
|  | 
 | ||||||
|  |   >>> import sys | ||||||
|  |   >>> try: | ||||||
|  |   ...   foobar() | ||||||
|  |   ... except: | ||||||
|  |   ...   excinfo = py.code.ExceptionInfo() | ||||||
|  |   >>> excinfo.typename | ||||||
|  |   'exceptions.NameError' | ||||||
|  |   >>> isinstance(excinfo.traceback, py.code.Traceback) | ||||||
|  |   True | ||||||
|  |   >>> excinfo.exconly() | ||||||
|  |   "NameError: name 'foobar' is not defined" | ||||||
|  | 
 | ||||||
		Loading…
	
		Reference in New Issue