[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 | ||||
| 
 | ||||
| class Code(object): | ||||
|     """ wrapper around Python code objects """ | ||||
|     def __init__(self, rawcode): | ||||
|         rawcode = getattr(rawcode, 'im_func', rawcode) | ||||
|         rawcode = getattr(rawcode, 'func_code', rawcode) | ||||
|  | @ -57,6 +58,7 @@ class Code(object): | |||
|         ) | ||||
| 
 | ||||
|     def path(self): | ||||
|         """ return a py.path.local object wrapping the source of the code """ | ||||
|         try: | ||||
|             return self.raw.co_filename.__path__ | ||||
|         except AttributeError: | ||||
|  | @ -64,6 +66,8 @@ class Code(object): | |||
|     path = property(path, None, None, "path of this code object") | ||||
| 
 | ||||
|     def fullsource(self): | ||||
|         """ return a py.code.Source object for the full source file of the code | ||||
|         """ | ||||
|         fn = self.raw.co_filename | ||||
|         try: | ||||
|             return fn.__source__ | ||||
|  | @ -73,11 +77,16 @@ class Code(object): | |||
|                           "full source containing this code object") | ||||
|      | ||||
|     def source(self): | ||||
|         """ return a py.code.Source object for the code object's source only | ||||
|         """ | ||||
|         # return source only for that part of code | ||||
|         import inspect | ||||
|         return py.code.Source(inspect.getsource(self.raw)) | ||||
| 
 | ||||
|     def getargs(self): | ||||
|         """ return a tuple with the argument names for the code object | ||||
|         """ | ||||
|         # handfull shortcut for getting args | ||||
|         raw = self.raw | ||||
|         return raw.co_varnames[:raw.co_argcount] | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,6 +22,13 @@ class ExceptionInfo(object): | |||
|         self.traceback = py.code.Traceback(tb)  | ||||
| 
 | ||||
|     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) | ||||
|         text = ''.join(lines) | ||||
|         if text.endswith('\n'): | ||||
|  | @ -32,6 +39,7 @@ class ExceptionInfo(object): | |||
|         return text | ||||
| 
 | ||||
|     def errisinstance(self, exc):  | ||||
|         """ return True if the exception is an instance of exc """ | ||||
|         return isinstance(self.value, exc)  | ||||
| 
 | ||||
|     def __str__(self): | ||||
|  |  | |||
|  | @ -18,23 +18,38 @@ class Frame(object): | |||
|                          "statement this frame is at") | ||||
| 
 | ||||
|     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.update(vars) | ||||
|         return eval(code, self.f_globals, f_locals) | ||||
| 
 | ||||
|     def exec_(self, code, **vars): | ||||
|         """ exec 'code' in the frame | ||||
| 
 | ||||
|             'vars' are optiona; additional local variables | ||||
|         """ | ||||
|         f_locals = self.f_locals.copy()  | ||||
|         f_locals.update(vars) | ||||
|         exec code in self.f_globals, f_locals  | ||||
| 
 | ||||
|     def repr(self, object): | ||||
|         """ return a 'safe' (non-recursive, one-line) string repr for 'object' | ||||
|         """ | ||||
|         return py.__.code.safe_repr._repr(object) | ||||
| 
 | ||||
|     def is_true(self, object): | ||||
|         return object | ||||
| 
 | ||||
|     def getargs(self): | ||||
|         """ return a list of tuples (name, value) for all arguments | ||||
|         """ | ||||
|         retval = [] | ||||
|         for arg in self.code.getargs(): | ||||
|             retval.append((arg, self.f_locals[arg])) | ||||
|         return retval | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ from __future__ import generators | |||
| import py  | ||||
| 
 | ||||
| class TracebackEntry(object): | ||||
|     """ a single entry in a traceback """ | ||||
|      | ||||
|     exprinfo = None  | ||||
| 
 | ||||
|     def __init__(self, rawentry): | ||||
|  | @ -14,6 +16,7 @@ class TracebackEntry(object): | |||
|         return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1) | ||||
| 
 | ||||
|     def statement(self): | ||||
|         """ return a py.code.Source object for the current statement """ | ||||
|         source = self.frame.code.fullsource | ||||
|         return source.getstatement(self.lineno) | ||||
|     statement = property(statement, None, None, | ||||
|  | @ -63,6 +66,11 @@ class TracebackEntry(object): | |||
|     source = property(getsource) | ||||
| 
 | ||||
|     def ishidden(self): | ||||
|         """ return True if the current frame has a var __tracebackhide__  | ||||
|             resolving to True | ||||
|              | ||||
|             mostly for internal use | ||||
|         """ | ||||
|         try:  | ||||
|             return self.frame.eval("__tracebackhide__")  | ||||
|         except (SystemExit, KeyboardInterrupt):  | ||||
|  | @ -103,6 +111,15 @@ class Traceback(list): | |||
|             list.__init__(self, tb) | ||||
| 
 | ||||
|     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: | ||||
|             if ((path is None or x.frame.code.path == path) and | ||||
|                 (lineno is None or x.lineno == lineno) and | ||||
|  | @ -117,6 +134,15 @@ class Traceback(list): | |||
|         return val | ||||
| 
 | ||||
|     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)) | ||||
| 
 | ||||
|     def getcrashentry(self): | ||||
|  | @ -129,6 +155,9 @@ class Traceback(list): | |||
|         return tb[-1] | ||||
| 
 | ||||
|     def recursionindex(self): | ||||
|         """ return the index of the frame/TracebackItem where recursion | ||||
|             originates if appropriate, None if no recursion occurred | ||||
|         """ | ||||
|         cache = {} | ||||
|         for i, entry in py.builtin.enumerate(self): | ||||
|             key = entry.frame.code.path, entry.lineno  | ||||
|  | @ -155,3 +184,4 @@ class Traceback(list): | |||
| 
 | ||||
| co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2', | ||||
|                    '?', '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