[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:
guido 2007-01-31 16:29:18 +01:00
parent 65f51efa55
commit 58eace43f9
5 changed files with 204 additions and 1 deletions

View File

@ -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]

View File

@ -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):

View File

@ -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

View File

@ -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')

141
py/doc/code.txt Normal file
View File

@ -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"