[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