Commit a8add0ec authored by Guido van Rossum's avatar Guido van Rossum

Merged revisions 55270-55324 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/branches/p3yk

........
  r55271 | fred.drake | 2007-05-11 10:14:47 -0700 (Fri, 11 May 2007) | 3 lines

  remove jpeg, panel libraries for SGI; there is more IRIX stuff left over,
  I guess that should be removed too, but will leave for someone who is sure
........
  r55280 | fred.drake | 2007-05-11 19:11:37 -0700 (Fri, 11 May 2007) | 1 line

  remove mention of file that has been removed
........
  r55301 | brett.cannon | 2007-05-13 17:38:05 -0700 (Sun, 13 May 2007) | 4 lines

  Remove rexec and Bastion from the stdlib.  This also eliminates the need for
  f_restricted on frames.  This in turn negates the need for
  PyEval_GetRestricted() and PyFrame_IsRestricted().
........
  r55303 | brett.cannon | 2007-05-13 19:22:22 -0700 (Sun, 13 May 2007) | 2 lines

  Remove the md5 and sha modules.
........
  r55305 | george.yoshida | 2007-05-13 19:45:55 -0700 (Sun, 13 May 2007) | 2 lines

  fix markup
........
  r55306 | neal.norwitz | 2007-05-13 19:47:57 -0700 (Sun, 13 May 2007) | 1 line

  Get the doc building again after some removals.
........
  r55307 | neal.norwitz | 2007-05-13 19:50:45 -0700 (Sun, 13 May 2007) | 1 line

  Get test_pyclbr passing again after getstatus was removed from commands.  This "test case" was weird since it was just importing a seemingly random module.  Remove the import
........
  r55322 | brett.cannon | 2007-05-14 14:09:20 -0700 (Mon, 14 May 2007) | 3 lines

  Remove the compiler package.  Will eventually need a mechanism to byte compile
  an AST.
........
parent 827b055f
......@@ -88,7 +88,6 @@ LIBFILES= $(MANSTYLES) $(INDEXSTYLES) $(COMMONTEX) \
commontex/reportingbugs.tex \
lib/lib.tex \
lib/asttable.tex \
lib/compiler.tex \
lib/distutils.tex \
lib/email.tex \
lib/emailencoders.tex \
......@@ -200,21 +199,15 @@ LIBFILES= $(MANSTYLES) $(INDEXSTYLES) $(COMMONTEX) \
lib/libaudioop.tex \
lib/libimageop.tex \
lib/libaifc.tex \
lib/libjpeg.tex \
lib/librgbimg.tex \
lib/libossaudiodev.tex \
lib/libcrypto.tex \
lib/libhashlib.tex \
lib/libmd5.tex \
lib/libsha.tex \
lib/libhmac.tex \
lib/libstdwin.tex \
lib/libsun.tex \
lib/libxdrlib.tex \
lib/libimghdr.tex \
lib/librestricted.tex \
lib/librexec.tex \
lib/libbastion.tex \
lib/libformatter.tex \
lib/liboperator.tex \
lib/libresource.tex \
......
This diff is collapsed.
......@@ -182,8 +182,6 @@ and how to embed it in other applications.
\input{libcrypto} % Cryptographic Services
\input{libhashlib}
\input{libhmac}
\input{libmd5}
\input{libsha}
% =============
% FILE & DATABASE STORAGE
......@@ -388,9 +386,6 @@ and how to embed it in other applications.
\input{custominterp} % Custom interpreter
\input{libcode}
\input{libcodeop}
\input{librestricted} % Restricted Execution
\input{librexec}
\input{libbastion}
\input{modules} % Importing Modules
......@@ -419,7 +414,6 @@ and how to embed it in other applications.
\input{libpickletools}
\input{distutils}
\input{compiler} % compiler package
\input{libast}
\input{libmisc} % Miscellaneous Services
......@@ -434,9 +428,6 @@ and how to embed it in other applications.
%\input{libstdwin} % STDWIN ONLY
\input{libjpeg}
%\input{libpanel}
\input{libsun} % SUNOS ONLY
\input{libsunaudio}
% XXX(nnorwitz): the modules below this comment should be kept.
......
\section{\module{Bastion} ---
Restricting access to objects}
\declaremodule{standard}{Bastion}
\modulesynopsis{Providing restricted access to objects.}
\moduleauthor{Barry Warsaw}{bwarsaw@python.org}
\versionchanged[Disabled module]{2.3}
\begin{notice}[warning]
The documentation has been left in place to help in reading old code
that uses the module.
\end{notice}
% I'm concerned that the word 'bastion' won't be understood by people
% for whom English is a second language, making the module name
% somewhat mysterious. Thus, the brief definition... --amk
According to the dictionary, a bastion is ``a fortified area or
position'', or ``something that is considered a stronghold.'' It's a
suitable name for this module, which provides a way to forbid access
to certain attributes of an object. It must always be used with the
\refmodule{rexec} module, in order to allow restricted-mode programs
access to certain safe attributes of an object, while denying access
to other, unsafe attributes.
% I've punted on the issue of documenting keyword arguments for now.
\begin{funcdesc}{Bastion}{object\optional{, filter\optional{,
name\optional{, class}}}}
Protect the object \var{object}, returning a bastion for the
object. Any attempt to access one of the object's attributes will
have to be approved by the \var{filter} function; if the access is
denied an \exception{AttributeError} exception will be raised.
If present, \var{filter} must be a function that accepts a string
containing an attribute name, and returns true if access to that
attribute will be permitted; if \var{filter} returns false, the access
is denied. The default filter denies access to any function beginning
with an underscore (\character{_}). The bastion's string representation
will be \samp{<Bastion for \var{name}>} if a value for
\var{name} is provided; otherwise, \samp{repr(\var{object})} will be
used.
\var{class}, if present, should be a subclass of \class{BastionClass};
see the code in \file{bastion.py} for the details. Overriding the
default \class{BastionClass} will rarely be required.
\end{funcdesc}
\begin{classdesc}{BastionClass}{getfunc, name}
Class which actually implements bastion objects. This is the default
class used by \function{Bastion()}. The \var{getfunc} parameter is a
function which returns the value of an attribute which should be
exposed to the restricted execution environment when called with the
name of the attribute as the only parameter. \var{name} is used to
construct the \function{repr()} of the \class{BastionClass} instance.
\end{classdesc}
\section{\module{jpeg} ---
Read and write JPEG files}
\declaremodule{builtin}{jpeg}
\platform{IRIX}
\modulesynopsis{Read and write image files in compressed JPEG format.}
The module \module{jpeg} provides access to the jpeg compressor and
decompressor written by the Independent JPEG Group
\index{Independent JPEG Group}(IJG). JPEG is a standard for
compressing pictures; it is defined in ISO 10918. For details on JPEG
or the Independent JPEG Group software refer to the JPEG standard or
the documentation provided with the software.
A portable interface to JPEG image files is available with the Python
Imaging Library (PIL) by Fredrik Lundh. Information on PIL is
available at \url{http://www.pythonware.com/products/pil/}.
\index{Python Imaging Library}
\index{PIL (the Python Imaging Library)}
\index{Lundh, Fredrik}
The \module{jpeg} module defines an exception and some functions.
\begin{excdesc}{error}
Exception raised by \function{compress()} and \function{decompress()}
in case of errors.
\end{excdesc}
\begin{funcdesc}{compress}{data, w, h, b}
Treat data as a pixmap of width \var{w} and height \var{h}, with
\var{b} bytes per pixel. The data is in SGI GL order, so the first
pixel is in the lower-left corner. This means that \function{gl.lrectread()}
return data can immediately be passed to \function{compress()}.
Currently only 1 byte and 4 byte pixels are allowed, the former being
treated as greyscale and the latter as RGB color.
\function{compress()} returns a string that contains the compressed
picture, in JFIF\index{JFIF} format.
\end{funcdesc}
\begin{funcdesc}{decompress}{data}
Data is a string containing a picture in JFIF\index{JFIF} format. It
returns a tuple \code{(\var{data}, \var{width}, \var{height},
\var{bytesperpixel})}. Again, the data is suitable to pass to
\function{gl.lrectwrite()}.
\end{funcdesc}
\begin{funcdesc}{setoption}{name, value}
Set various options. Subsequent \function{compress()} and
\function{decompress()} calls will use these options. The following
options are available:
\begin{tableii}{l|p{3in}}{code}{Option}{Effect}
\lineii{'forcegray'}{%
Force output to be grayscale, even if input is RGB.}
\lineii{'quality'}{%
Set the quality of the compressed image to a value between
\code{0} and \code{100} (default is \code{75}). This only affects
compression.}
\lineii{'optimize'}{%
Perform Huffman table optimization. Takes longer, but results in
smaller compressed image. This only affects compression.}
\lineii{'smooth'}{%
Perform inter-block smoothing on uncompressed image. Only useful
for low-quality images. This only affects decompression.}
\end{tableii}
\end{funcdesc}
\begin{seealso}
\seetitle{JPEG Still Image Data Compression Standard}{The
canonical reference for the JPEG image format, by
Pennebaker and Mitchell.}
\seetitle[http://www.w3.org/Graphics/JPEG/itu-t81.pdf]{Information
Technology - Digital Compression and Coding of
Continuous-tone Still Images - Requirements and
Guidelines}{The ISO standard for JPEG is also published as
ITU T.81. This is available online in PDF form.}
\end{seealso}
\section{\module{md5} ---
MD5 message digest algorithm}
\declaremodule{builtin}{md5}
\modulesynopsis{RSA's MD5 message digest algorithm.}
\deprecated{2.5}{Use the \refmodule{hashlib} module instead.}
This module implements the interface to RSA's MD5 message digest
\index{message digest, MD5}
algorithm (see also Internet \rfc{1321}). Its use is quite
straightforward:\ use \function{new()} to create an md5 object.
You can now feed this object with arbitrary strings using the
\method{update()} method, and at any point you can ask it for the
\dfn{digest} (a strong kind of 128-bit checksum,
a.k.a. ``fingerprint'') of the concatenation of the strings fed to it
so far using the \method{digest()} method.
\index{checksum!MD5}
For example, to obtain the digest of the string \code{'Nobody inspects
the spammish repetition'}:
\begin{verbatim}
>>> import md5
>>> m = md5.new()
>>> m.update("Nobody inspects")
>>> m.update(" the spammish repetition")
>>> m.digest()
'\xbbd\x9c\x83\xdd\x1e\xa5\xc9\xd9\xde\xc9\xa1\x8d\xf0\xff\xe9'
\end{verbatim}
More condensed:
\begin{verbatim}
>>> md5.new("Nobody inspects the spammish repetition").digest()
'\xbbd\x9c\x83\xdd\x1e\xa5\xc9\xd9\xde\xc9\xa1\x8d\xf0\xff\xe9'
\end{verbatim}
The following values are provided as constants in the module and as
attributes of the md5 objects returned by \function{new()}:
\begin{datadesc}{digest_size}
The size of the resulting digest in bytes. This is always
\code{16}.
\end{datadesc}
The md5 module provides the following functions:
\begin{funcdesc}{new}{\optional{arg}}
Return a new md5 object. If \var{arg} is present, the method call
\code{update(\var{arg})} is made.
\end{funcdesc}
\begin{funcdesc}{md5}{\optional{arg}}
For backward compatibility reasons, this is an alternative name for the
\function{new()} function.
\end{funcdesc}
An md5 object has the following methods:
\begin{methoddesc}[md5]{update}{arg}
Update the md5 object with the string \var{arg}. Repeated calls are
equivalent to a single call with the concatenation of all the
arguments: \code{m.update(a); m.update(b)} is equivalent to
\code{m.update(a+b)}.
\end{methoddesc}
\begin{methoddesc}[md5]{digest}{}
Return the digest of the strings passed to the \method{update()}
method so far. This is a 16-byte string which may contain
non-\ASCII{} characters, including null bytes.
\end{methoddesc}
\begin{methoddesc}[md5]{hexdigest}{}
Like \method{digest()} except the digest is returned as a string of
length 32, containing only hexadecimal digits. This may
be used to exchange the value safely in email or other non-binary
environments.
\end{methoddesc}
\begin{methoddesc}[md5]{copy}{}
Return a copy (``clone'') of the md5 object. This can be used to
efficiently compute the digests of strings that share a common initial
substring.
\end{methoddesc}
\begin{seealso}
\seemodule{sha}{Similar module implementing the Secure Hash
Algorithm (SHA). The SHA algorithm is considered a
more secure hash.}
\end{seealso}
\section{\module{panel} ---
None}
\declaremodule{standard}{panel}
\modulesynopsis{None}
\strong{Please note:} The FORMS library, to which the
\code{fl}\refbimodindex{fl} module described above interfaces, is a
simpler and more accessible user interface library for use with GL
than the \code{panel} module (besides also being by a Dutch author).
This module should be used instead of the built-in module
\code{pnl}\refbimodindex{pnl}
to interface with the
\emph{Panel Library}.
The module is too large to document here in its entirety.
One interesting function:
\begin{funcdesc}{defpanellist}{filename}
Parses a panel description file containing S-expressions written by the
\emph{Panel Editor}
that accompanies the Panel Library and creates the described panels.
It returns a list of panel objects.
\end{funcdesc}
\warning{The Python interpreter will dump core if you don't create a
GL window before calling
\code{panel.mkpanel()}
or
\code{panel.defpanellist()}.}
\section{\module{panelparser} ---
None}
\declaremodule{standard}{panelparser}
\modulesynopsis{None}
This module defines a self-contained parser for S-expressions as output
by the Panel Editor (which is written in Scheme so it can't help writing
S-expressions).
The relevant function is
\code{panelparser.parse_file(\var{file})}
which has a file object (not a filename!) as argument and returns a list
of parsed S-expressions.
Each S-expression is converted into a Python list, with atoms converted
to Python strings and sub-expressions (recursively) to Python lists.
For more details, read the module file.
% XXXXJH should be funcdesc, I think
\section{\module{pnl} ---
None}
\declaremodule{builtin}{pnl}
\modulesynopsis{None}
This module provides access to the
\emph{Panel Library}
built by NASA Ames\index{NASA} (to get it, send email to
\code{panel-request@nas.nasa.gov}).
All access to it should be done through the standard module
\code{panel}\refstmodindex{panel},
which transparently exports most functions from
\code{pnl}
but redefines
\code{pnl.dopanel()}.
\warning{The Python interpreter will dump core if you don't create a
GL window before calling \code{pnl.mkpanel()}.}
The module is too large to document here in its entirety.
\chapter{Restricted Execution \label{restricted}}
\begin{notice}[warning]
In Python 2.3 these modules have been disabled due to various known
and not readily fixable security holes. The modules are still
documented here to help in reading old code that uses the
\module{rexec} and \module{Bastion} modules.
\end{notice}
\emph{Restricted execution} is the basic framework in Python that allows
for the segregation of trusted and untrusted code. The framework is based on the
notion that trusted Python code (a \emph{supervisor}) can create a
``padded cell' (or environment) with limited permissions, and run the
untrusted code within this cell. The untrusted code cannot break out
of its cell, and can only interact with sensitive system resources
through interfaces defined and managed by the trusted code. The term
``restricted execution'' is favored over ``safe-Python''
since true safety is hard to define, and is determined by the way the
restricted environment is created. Note that the restricted
environments can be nested, with inner cells creating subcells of
lesser, but never greater, privilege.
An interesting aspect of Python's restricted execution model is that
the interfaces presented to untrusted code usually have the same names
as those presented to trusted code. Therefore no special interfaces
need to be learned to write code designed to run in a restricted
environment. And because the exact nature of the padded cell is
determined by the supervisor, different restrictions can be imposed,
depending on the application. For example, it might be deemed
``safe'' for untrusted code to read any file within a specified
directory, but never to write a file. In this case, the supervisor
may redefine the built-in \function{open()} function so that it raises
an exception whenever the \var{mode} parameter is \code{'w'}. It
might also perform a \cfunction{chroot()}-like operation on the
\var{filename} parameter, such that root is always relative to some
safe ``sandbox'' area of the filesystem. In this case, the untrusted
code would still see an built-in \function{open()} function in its
environment, with the same calling interface. The semantics would be
identical too, with \exception{IOError}s being raised when the
supervisor determined that an unallowable parameter is being used.
The Python run-time determines whether a particular code block is
executing in restricted execution mode based on the identity of the
\code{__builtins__} object in its global variables: if this is (the
dictionary of) the standard \refmodule[builtin]{__builtin__} module,
the code is deemed to be unrestricted, else it is deemed to be
restricted.
Python code executing in restricted mode faces a number of limitations
that are designed to prevent it from escaping from the padded cell.
For instance, the function object attribute \member{func_globals} and
the class and instance object attribute \member{__dict__} are
unavailable.
Two modules provide the framework for setting up restricted execution
environments:
\localmoduletable
\begin{seealso}
\seetitle[http://grail.sourceforge.net/]{Grail Home Page}
{Grail, an Internet browser written in Python, uses these
modules to support Python applets. More
information on the use of Python's restricted execution
mode in Grail is available on the Web site.}
\end{seealso}
This diff is collapsed.
\section{\module{sha} ---
SHA-1 message digest algorithm}
\declaremodule{builtin}{sha}
\modulesynopsis{NIST's secure hash algorithm, SHA.}
\sectionauthor{Fred L. Drake, Jr.}{fdrake@acm.org}
\deprecated{2.5}{Use the \refmodule{hashlib} module instead.}
This module implements the interface to NIST's\index{NIST} secure hash
algorithm,\index{Secure Hash Algorithm} known as SHA-1. SHA-1 is an
improved version of the original SHA hash algorithm. It is used in
the same way as the \refmodule{md5} module:\ use \function{new()}
to create an sha object, then feed this object with arbitrary strings
using the \method{update()} method, and at any point you can ask it
for the \dfn{digest} of the concatenation of the strings fed to it
so far.\index{checksum!SHA} SHA-1 digests are 160 bits instead of
MD5's 128 bits.
\begin{funcdesc}{new}{\optional{string}}
Return a new sha object. If \var{string} is present, the method
call \code{update(\var{string})} is made.
\end{funcdesc}
The following values are provided as constants in the module and as
attributes of the sha objects returned by \function{new()}:
\begin{datadesc}{blocksize}
Size of the blocks fed into the hash function; this is always
\code{1}. This size is used to allow an arbitrary string to be
hashed.
\end{datadesc}
\begin{datadesc}{digest_size}
The size of the resulting digest in bytes. This is always
\code{20}.
\end{datadesc}
An sha object has the same methods as md5 objects:
\begin{methoddesc}[sha]{update}{arg}
Update the sha object with the string \var{arg}. Repeated calls are
equivalent to a single call with the concatenation of all the
arguments: \code{m.update(a); m.update(b)} is equivalent to
\code{m.update(a+b)}.
\end{methoddesc}
\begin{methoddesc}[sha]{digest}{}
Return the digest of the strings passed to the \method{update()}
method so far. This is a 20-byte string which may contain
non-\ASCII{} characters, including null bytes.
\end{methoddesc}
\begin{methoddesc}[sha]{hexdigest}{}
Like \method{digest()} except the digest is returned as a string of
length 40, containing only hexadecimal digits. This may
be used to exchange the value safely in email or other non-binary
environments.
\end{methoddesc}
\begin{methoddesc}[sha]{copy}{}
Return a copy (``clone'') of the sha object. This can be used to
efficiently compute the digests of strings that share a common initial
substring.
\end{methoddesc}
\begin{seealso}
\seetitle[http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf]
{Secure Hash Standard}
{The Secure Hash Algorithm is defined by NIST document FIPS
PUB 180-2:
\citetitle[http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf]
{Secure Hash Standard}, published in August 2002.}
\seetitle[http://csrc.nist.gov/encryption/tkhash.html]
{Cryptographic Toolkit (Secure Hashing)}
{Links from NIST to various information on secure hashing.}
\end{seealso}
......@@ -941,18 +941,15 @@ stack frame; \member{f_code} is the code object being executed in this
frame; \member{f_locals} is the dictionary used to look up local
variables; \member{f_globals} is used for global variables;
\member{f_builtins} is used for built-in (intrinsic) names;
\member{f_restricted} is a flag indicating whether the function is
executing in restricted execution mode; \member{f_lasti} gives the
precise instruction (this is an index into the bytecode string of
the code object).
\member{f_lasti} gives the precise instruction (this is an index into
the bytecode string of the code object).
\withsubitem{(frame attribute)}{
\ttindex{f_back}
\ttindex{f_code}
\ttindex{f_globals}
\ttindex{f_locals}
\ttindex{f_lasti}
\ttindex{f_builtins}
\ttindex{f_restricted}}
\ttindex{f_builtins}}
Special writable attributes: \member{f_trace}, if not \code{None}, is
a function called at the start of each source code line (this is used
......
......@@ -33,7 +33,6 @@ PyAPI_FUNC(PyObject *) PyEval_GetBuiltins(void);
PyAPI_FUNC(PyObject *) PyEval_GetGlobals(void);
PyAPI_FUNC(PyObject *) PyEval_GetLocals(void);
PyAPI_FUNC(struct _frame *) PyEval_GetFrame(void);
PyAPI_FUNC(int) PyEval_GetRestricted(void);
/* Look at the current frame's (if any) code's co_flags, and turn on
the corresponding compiler flags in cf->cf_flags. Return 1 if any
......
......@@ -52,8 +52,6 @@ typedef struct _frame {
PyAPI_DATA(PyTypeObject) PyFrame_Type;
#define PyFrame_Check(op) ((op)->ob_type == &PyFrame_Type)
#define PyFrame_IsRestricted(f) \
((f)->f_builtins != (f)->f_tstate->interp->builtins)
PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *,
PyObject *, PyObject *);
......
"""Bastionification utility.
A bastion (for another object -- the 'original') is an object that has
the same methods as the original but does not give access to its
instance variables. Bastions have a number of uses, but the most
obvious one is to provide code executing in restricted mode with a
safe interface to an object implemented in unrestricted mode.
The bastionification routine has an optional second argument which is
a filter function. Only those methods for which the filter method
(called with the method name as argument) returns true are accessible.
The default filter method returns true unless the method name begins
with an underscore.
There are a number of possible implementations of bastions. We use a
'lazy' approach where the bastion's __getattr__() discipline does all
the work for a particular method the first time it is used. This is
usually fastest, especially if the user doesn't call all available
methods. The retrieved methods are stored as instance variables of
the bastion, so the overhead is only occurred on the first use of each
method.
Detail: the bastion class has a __repr__() discipline which includes
the repr() of the original object. This is precomputed when the
bastion is created.
"""
__all__ = ["BastionClass", "Bastion"]
from types import MethodType
class BastionClass:
"""Helper class used by the Bastion() function.
You could subclass this and pass the subclass as the bastionclass
argument to the Bastion() function, as long as the constructor has
the same signature (a get() function and a name for the object).
"""
def __init__(self, get, name):
"""Constructor.
Arguments:
get - a function that gets the attribute value (by name)
name - a human-readable name for the original object
(suggestion: use repr(object))
"""
self._get_ = get
self._name_ = name
def __repr__(self):
"""Return a representation string.
This includes the name passed in to the constructor, so that
if you print the bastion during debugging, at least you have
some idea of what it is.
"""
return "<Bastion for %s>" % self._name_
def __getattr__(self, name):
"""Get an as-yet undefined attribute value.
This calls the get() function that was passed to the
constructor. The result is stored as an instance variable so
that the next time the same attribute is requested,
__getattr__() won't be invoked.
If the get() function raises an exception, this is simply
passed on -- exceptions are not cached.
"""
attribute = self._get_(name)
self.__dict__[name] = attribute
return attribute
def Bastion(object, filter = lambda name: name[:1] != '_',
name=None, bastionclass=BastionClass):
"""Create a bastion for an object, using an optional filter.
See the Bastion module's documentation for background.
Arguments:
object - the original object
filter - a predicate that decides whether a function name is OK;
by default all names are OK that don't start with '_'
name - the name of the object; default repr(object)
bastionclass - class used to create the bastion; default BastionClass
"""
raise RuntimeError, "This code is not secure in Python 2.2 and later"
# Note: we define *two* ad-hoc functions here, get1 and get2.
# Both are intended to be called in the same way: get(name).
# It is clear that the real work (getting the attribute
# from the object and calling the filter) is done in get1.
# Why can't we pass get1 to the bastion? Because the user
# would be able to override the filter argument! With get2,
# overriding the default argument is no security loophole:
# all it does is call it.
# Also notice that we can't place the object and filter as
# instance variables on the bastion object itself, since
# the user has full access to all instance variables!
def get1(name, object=object, filter=filter):
"""Internal function for Bastion(). See source comments."""
if filter(name):
attribute = getattr(object, name)
if type(attribute) == MethodType:
return attribute
raise AttributeError, name
def get2(name, get1=get1):
"""Internal function for Bastion(). See source comments."""
return get1(name)
if name is None:
name = repr(object)
return bastionclass(get2, name)
def _test():
"""Test the Bastion() function."""
class Original:
def __init__(self):
self.sum = 0
def add(self, n):
self._add(n)
def _add(self, n):
self.sum = self.sum + n
def total(self):
return self.sum
o = Original()
b = Bastion(o)
testcode = """if 1:
b.add(81)
b.add(18)
print "b.total() =", b.total()
try:
print "b.sum =", b.sum,
except:
print "inaccessible"
else:
print "accessible"
try:
print "b._add =", b._add,
except:
print "inaccessible"
else:
print "accessible"
try:
print "b._get_.__defaults__ =", map(type, b._get_.__defaults__),
except:
print "inaccessible"
else:
print "accessible"
\n"""
exec(testcode)
print('='*20, "Using rexec:", '='*20)
import rexec
r = rexec.RExec()
m = r.add_module('__main__')
m.b = b
r.r_exec(testcode)
if __name__ == '__main__':
_test()
"""Package for parsing and compiling Python source code
There are several functions defined at the top level that are imported
from modules contained in the package.
parse(buf, mode="exec") -> AST
Converts a string containing Python source code to an abstract
syntax tree (AST). The AST is defined in compiler.ast.
parseFile(path) -> AST
The same as parse(open(path))
walk(ast, visitor, verbose=None)
Does a pre-order walk over the ast using the visitor instance.
See compiler.visitor for details.
compile(source, filename, mode, flags=None, dont_inherit=None)
Returns a code object. A replacement for the builtin compile() function.
compileFile(filename)
Generates a .pyc file by compiling filename.
"""
from compiler.transformer import parse, parseFile
from compiler.visitor import walk
from compiler.pycodegen import compile, compileFile
This diff is collapsed.
# operation flags
OP_ASSIGN = 'OP_ASSIGN'
OP_DELETE = 'OP_DELETE'
OP_APPLY = 'OP_APPLY'
SC_LOCAL = 1
SC_GLOBAL = 2
SC_FREE = 3
SC_CELL = 4
SC_UNKNOWN = 5
CO_OPTIMIZED = 0x0001
CO_NEWLOCALS = 0x0002
CO_VARARGS = 0x0004
CO_VARKEYWORDS = 0x0008
CO_NESTED = 0x0010
CO_GENERATOR = 0x0020
CO_GENERATOR_ALLOWED = 0
CO_FUTURE_DIVISION = 0x2000
CO_FUTURE_ABSIMPORT = 0x4000
CO_FUTURE_WITH_STATEMENT = 0x8000
"""Parser for future statements
"""
from compiler import ast, walk
def is_future(stmt):
"""Return true if statement is a well-formed future statement"""
if not isinstance(stmt, ast.From):
return 0
if stmt.modname == "__future__":
return 1
else:
return 0
class FutureParser:
features = ("nested_scopes", "generators", "division",
"absolute_import", "with_statement")
def __init__(self):
self.found = {} # set
def visitModule(self, node):
stmt = node.node
for s in stmt.nodes:
if not self.check_stmt(s):
break
def check_stmt(self, stmt):
if is_future(stmt):
for name, asname in stmt.names:
if name in self.features:
self.found[name] = 1
else:
raise SyntaxError, \
"future feature %s is not defined" % name
stmt.valid_future = 1
return 1
return 0
def get_features(self):
"""Return list of features enabled by future statements"""
return self.found.keys()
class BadFutureParser:
"""Check for invalid future statements"""
def visitFrom(self, node):
if hasattr(node, 'valid_future'):
return
if node.modname != "__future__":
return
raise SyntaxError, "invalid future statement " + repr(node)
def find_futures(node):
p1 = FutureParser()
p2 = BadFutureParser()
walk(node, p1)
walk(node, p2)
return p1.get_features()
if __name__ == "__main__":
import sys
from compiler import parseFile, walk
for file in sys.argv[1:]:
print(file)
tree = parseFile(file)
v = FutureParser()
walk(tree, v)
print(v.found)
print()
def flatten(tup):
elts = []
for elt in tup:
if isinstance(elt, tuple):
elts = elts + flatten(elt)
else:
elts.append(elt)
return elts
class Set:
def __init__(self):
self.elts = {}
def __len__(self):
return len(self.elts)
def __contains__(self, elt):
return elt in self.elts
def add(self, elt):
self.elts[elt] = elt
def elements(self):
return list(self.elts.keys())
def has_elt(self, elt):
return elt in self.elts
def remove(self, elt):
del self.elts[elt]
def copy(self):
c = Set()
c.elts.update(self.elts)
return c
class Stack:
def __init__(self):
self.stack = []
self.pop = self.stack.pop
def __len__(self):
return len(self.stack)
def push(self, elt):
self.stack.append(elt)
def top(self):
return self.stack[-1]
def __getitem__(self, index): # needed by visitContinue()
return self.stack[index]
MANGLE_LEN = 256 # magic constant from compile.c
def mangle(name, klass):
if not name.startswith('__'):
return name
if len(name) + 2 >= MANGLE_LEN:
return name
if name.endswith('__'):
return name
try:
i = 0
while klass[i] == '_':
i = i + 1
except IndexError:
return name
klass = klass[i:]
tlen = len(klass) + len(name)
if tlen > MANGLE_LEN:
klass = klass[:MANGLE_LEN-tlen]
return "_%s%s" % (klass, name)
def set_filename(filename, tree):
"""Set the filename attribute to filename on every node in tree"""
worklist = [tree]
while worklist:
node = worklist.pop(0)
node.filename = filename
worklist.extend(node.getChildNodes())
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
"""Check for errs in the AST.
The Python parser does not catch all syntax errors. Others, like
assignments with invalid targets, are caught in the code generation
phase.
The compiler package catches some errors in the transformer module.
But it seems clearer to write checkers that use the AST to detect
errors.
"""
from compiler import ast, walk
def check(tree, multi=None):
v = SyntaxErrorChecker(multi)
walk(tree, v)
return v.errors
class SyntaxErrorChecker:
"""A visitor to find syntax errors in the AST."""
def __init__(self, multi=None):
"""Create new visitor object.
If optional argument multi is not None, then print messages
for each error rather than raising a SyntaxError for the
first.
"""
self.multi = multi
self.errors = 0
def error(self, node, msg):
self.errors = self.errors + 1
if self.multi is not None:
print("%s:%s: %s" % (node.filename, node.lineno, msg))
else:
raise SyntaxError, "%s (%s:%s)" % (msg, node.filename, node.lineno)
def visitAssign(self, node):
# the transformer module handles many of these
pass
## for target in node.nodes:
## if isinstance(target, ast.AssList):
## if target.lineno is None:
## target.lineno = node.lineno
## self.error(target, "can't assign to list comprehension")
This diff is collapsed.
from compiler import ast
# XXX should probably rename ASTVisitor to ASTWalker
# XXX can it be made even more generic?
class ASTVisitor:
"""Performs a depth-first walk of the AST
The ASTVisitor will walk the AST, performing either a preorder or
postorder traversal depending on which method is called.
methods:
preorder(tree, visitor)
postorder(tree, visitor)
tree: an instance of ast.Node
visitor: an instance with visitXXX methods
The ASTVisitor is responsible for walking over the tree in the
correct order. For each node, it checks the visitor argument for
a method named 'visitNodeType' where NodeType is the name of the
node's class, e.g. Class. If the method exists, it is called
with the node as its sole argument.
The visitor method for a particular node type can control how
child nodes are visited during a preorder walk. (It can't control
the order during a postorder walk, because it is called _after_
the walk has occurred.) The ASTVisitor modifies the visitor
argument by adding a visit method to the visitor; this method can
be used to visit a child node of arbitrary type.
"""
VERBOSE = 0
def __init__(self):
self.node = None
self._cache = {}
def default(self, node, *args):
for child in node.getChildNodes():
self.dispatch(child, *args)
def dispatch(self, node, *args):
self.node = node
klass = node.__class__
meth = self._cache.get(klass, None)
if meth is None:
className = klass.__name__
meth = getattr(self.visitor, 'visit' + className, self.default)
self._cache[klass] = meth
## if self.VERBOSE > 0:
## className = klass.__name__
## if self.VERBOSE == 1:
## if meth == 0:
## print "dispatch", className
## else:
## print "dispatch", className, (meth and meth.__name__ or '')
return meth(node, *args)
def preorder(self, tree, visitor, *args):
"""Do preorder walk of tree using visitor"""
self.visitor = visitor
visitor.visit = self.dispatch
self.dispatch(tree, *args) # XXX *args make sense?
class ExampleASTVisitor(ASTVisitor):
"""Prints examples of the nodes that aren't visited
This visitor-driver is only useful for development, when it's
helpful to develop a visitor incrementally, and get feedback on what
you still have to do.
"""
examples = {}
def dispatch(self, node, *args):
self.node = node
meth = self._cache.get(node.__class__, None)
className = node.__class__.__name__
if meth is None:
meth = getattr(self.visitor, 'visit' + className, 0)
self._cache[node.__class__] = meth
if self.VERBOSE > 1:
print("dispatch", className, (meth and meth.__name__ or ''))
if meth:
meth(node, *args)
elif self.VERBOSE > 0:
klass = node.__class__
if klass not in self.examples:
self.examples[klass] = klass
print()
print(self.visitor)
print(klass)
for attr in dir(node):
if attr[0] != '_':
print("\t", "%-12.12s" % attr, getattr(node, attr))
print()
return self.default(node, *args)
# XXX this is an API change
_walker = ASTVisitor
def walk(tree, visitor, walker=None, verbose=None):
if walker is None:
walker = _walker()
if verbose is not None:
walker.VERBOSE = verbose
walker.preorder(tree, visitor)
return walker.visitor
def dumpNode(node):
print(node.__class__)
for attr in dir(node):
if attr[0] != '_':
print("\t", "%-10.10s" % attr, getattr(node, attr))
......@@ -8,7 +8,7 @@ module searching and loading algorithm, and it is possible to replace
the built-in function __import__ in order to change the semantics of
the import statement, until now it has been difficult to combine the
effect of different __import__ hacks, like loading modules from URLs
by rimport.py, or restricted execution by rexec.py.
by rimport.py.
This module defines three new concepts:
......
......@@ -674,7 +674,6 @@ def _test_revamp():
# push MAL's mapper into sys.path[0] as a cache (hard-coded for apps)
#
# from Guido:
# need to change sys.* references for rexec environs
# need hook for MAL's walk-me-up import strategy, or Tim's absolute strategy
# watch out for sys.modules[...] is None
# flag to force absolute imports? (speeds _determine_import_context and
......@@ -714,7 +713,7 @@ def _test_revamp():
# > However, we still have a tension occurring here:
# >
# > 1) implementing policy in ImportManager assists in single-point policy
# > changes for app/rexec situations
# > changes for app situations
# > 2) implementing policy in Importer assists in package-private policy
# > changes for normal, operating conditions
# >
......
......@@ -161,7 +161,6 @@ def isframe(object):
f_lasti index of last attempted instruction in bytecode
f_lineno current line number in Python source code
f_locals local namespace seen by this frame
f_restricted 0 or 1 if frame is in restricted execution mode
f_trace tracing function for this frame, or None"""
return isinstance(object, types.FrameType)
......@@ -674,7 +673,7 @@ def getargs(co):
"""Get information about the arguments accepted by a code object.
Three things are returned: (args, varargs, varkw), where
'args' is the list of argument names, possibly containing nested
'args' is the list of argument names, possibly containing nested
lists. Keyword-only arguments are appended. 'varargs' and 'varkw'
are the names of the * and ** arguments or None."""
args, varargs, kwonlyargs, varkw = _getfullargs(co)
......@@ -751,7 +750,7 @@ def getargspec(func):
'args' will include keyword-only argument names.
'varargs' and 'varkw' are the names of the * and ** arguments or None.
'defaults' is an n-tuple of the default values of the last n arguments.
Use the getfullargspec() API for Python-3000 code, as annotations
and keyword arguments are supported. getargspec() will raise ValueError
if the func has either annotations or keyword arguments.
......@@ -767,7 +766,7 @@ def getargspec(func):
def getfullargspec(func):
"""Get the names and default values of a function's arguments.
A tuple of seven things is returned: (args, varargs, kwonlyargs,
A tuple of seven things is returned: (args, varargs, kwonlyargs,
kwonlydefaults, varkw, defaults, annotations).
'args' is a list of the argument names (it may contain nested lists).
'varargs' and 'varkw' are the names of the * and ** arguments or None.
......@@ -775,7 +774,7 @@ def getfullargspec(func):
'kwonlyargs' is a list of keyword-only argument names.
'kwonlydefaults' is a dictionary mapping names from kwonlyargs to defaults.
'annotations' is a dictionary mapping argument names to annotations.
The first four items in the tuple correspond to getargspec().
"""
......@@ -784,7 +783,7 @@ def getfullargspec(func):
if not isfunction(func):
raise TypeError('arg is not a Python function')
args, varargs, kwonlyargs, varkw = _getfullargs(func.__code__)
return (args, varargs, varkw, func.__defaults__,
return (args, varargs, varkw, func.__defaults__,
kwonlyargs, func.__kwdefaults__, func.__annotations__)
def getargvalues(frame):
......@@ -816,12 +815,12 @@ def formatannotation(annotation, base_module=None):
return annotation.__name__
return annotation.__module__+'.'+annotation.__name__
return repr(annotation)
def formatannotationrelativeto(object):
module = getattr(object, '__module__', None)
def _formatannotation(annotation):
return formatannotation(annotation, module)
return _formatannotation
module = getattr(object, '__module__', None)
def _formatannotation(annotation):
return formatannotation(annotation, module)
return _formatannotation
def formatargspec(args, varargs=None, varkw=None, defaults=None,
kwonlyargs=(), kwonlydefaults={}, annotations={},
......@@ -832,7 +831,7 @@ def formatargspec(args, varargs=None, varkw=None, defaults=None,
formatreturns=lambda text: ' -> ' + text,
formatannotation=formatannotation,
join=joinseq):
"""Format an argument spec from the values returned by getargspec
"""Format an argument spec from the values returned by getargspec
or getfullargspec.
The first seven arguments are (args, varargs, varkw, defaults,
......
# $Id$
#
# Copyright (C) 2005 Gregory P. Smith (greg@electricrain.com)
# Licensed to PSF under a Contributor Agreement.
from hashlib import md5
new = md5
blocksize = 1 # legacy value (wrong in any useful sense)
digest_size = 16
......@@ -8,9 +8,4 @@ from types import ClassType as classobj
from types import FunctionType as function
from types import MethodType as instancemethod
from types import ModuleType as module
# CodeType is not accessible in restricted execution mode
try:
from types import CodeType as code
except ImportError:
pass
from types import CodeType as code
......@@ -1009,14 +1009,9 @@ class Unpickler:
if (not args and
type(klass) is ClassType and
not hasattr(klass, "__getinitargs__")):
try:
value = _EmptyClass()
value.__class__ = klass
instantiated = 1
except RuntimeError:
# In restricted execution, assignment to inst.__class__ is
# prohibited
pass
value = _EmptyClass()
value.__class__ = klass
instantiated = 1
if not instantiated:
try:
value = klass(*args)
......@@ -1184,20 +1179,7 @@ class Unpickler:
if isinstance(state, tuple) and len(state) == 2:
state, slotstate = state
if state:
try:
inst.__dict__.update(state)
except RuntimeError:
# XXX In restricted execution, the instance's __dict__
# is not accessible. Use the old way of unpickling
# the instance variables. This is a semantic
# difference when unpickling in restricted
# vs. unrestricted modes.
# Note, however, that cPickle has never tried to do the
# .update() business, and always uses
# PyObject_SetItem(inst.__dict__, key, value) in a
# loop over state.items().
for k, v in state.items():
setattr(inst, k, v)
inst.__dict__.update(state)
if slotstate:
for k, v in slotstate.items():
setattr(inst, k, v)
......
......@@ -1562,13 +1562,6 @@ opcodes = [
the object is updated via
anyobject.__dict__.update(argument)
This may raise RuntimeError in restricted execution mode (which
disallows access to __dict__ directly); in that case, the object
is updated instead via
for k, v in argument.items():
anyobject[k] = v
"""),
I(name='INST',
......@@ -1604,11 +1597,7 @@ opcodes = [
calling __init__() is current wisdom). In this case, an instance of
an old-style dummy class is created, and then we try to rebind its
__class__ attribute to the desired class object. If this succeeds,
the new instance object is pushed on the stack, and we're done. In
restricted execution mode it can fail (assignment to __class__ is
disallowed), and I'm not really sure what happens then -- it looks
like the code ends up calling the class object's __init__ anyway,
via falling into the next case.
the new instance object is pushed on the stack, and we're done.
Else (the argtuple is not empty, it's not an old-style class object,
or the class object does have a __getinitargs__ attribute), the code
......
# Implement 'jpeg' interface using SGI's compression library
# XXX Options 'smooth' and 'optimize' are ignored.
# XXX It appears that compressing grayscale images doesn't work right;
# XXX the resulting file causes weirdness.
class error(Exception):
pass
options = {'quality': 75, 'optimize': 0, 'smooth': 0, 'forcegray': 0}
comp = None
decomp = None
def compress(imgdata, width, height, bytesperpixel):
global comp
import cl
if comp is None: comp = cl.OpenCompressor(cl.JPEG)
if bytesperpixel == 1:
format = cl.GRAYSCALE
elif bytesperpixel == 4:
format = cl.RGBX
if options['forcegray']:
iformat = cl.GRAYSCALE
else:
iformat = cl.YUV
# XXX How to support 'optimize'?
params = [cl.IMAGE_WIDTH, width, cl.IMAGE_HEIGHT, height,
cl.ORIGINAL_FORMAT, format,
cl.ORIENTATION, cl.BOTTOM_UP,
cl.QUALITY_FACTOR, options['quality'],
cl.INTERNAL_FORMAT, iformat,
]
comp.SetParams(params)
jpegdata = comp.Compress(1, imgdata)
return jpegdata
def decompress(jpegdata):
global decomp
import cl
if decomp is None: decomp = cl.OpenDecompressor(cl.JPEG)
headersize = decomp.ReadHeader(jpegdata)
params = [cl.IMAGE_WIDTH, 0, cl.IMAGE_HEIGHT, 0, cl.INTERNAL_FORMAT, 0]
decomp.GetParams(params)
width, height, format = params[1], params[3], params[5]
if format == cl.GRAYSCALE or options['forcegray']:
format = cl.GRAYSCALE
bytesperpixel = 1
else:
format = cl.RGBX
bytesperpixel = 4
# XXX How to support 'smooth'?
params = [cl.ORIGINAL_FORMAT, format,
cl.ORIENTATION, cl.BOTTOM_UP,
cl.FRAME_BUFFER_SIZE, width*height*bytesperpixel]
decomp.SetParams(params)
imgdata = decomp.Decompress(1, jpegdata)
return imgdata, width, height, bytesperpixel
def setoption(name, value):
if type(value) is not type(0):
raise TypeError, 'jpeg.setoption: numeric options only'
if name == 'forcegrey':
name = 'forcegray'
if not options.has_key(name):
raise KeyError, 'jpeg.setoption: unknown option name'
options[name] = int(value)
def test():
import sys
if sys.argv[1:2] == ['-g']:
del sys.argv[1]
setoption('forcegray', 1)
if not sys.argv[1:]:
sys.argv.append('/usr/local/images/data/jpg/asterix.jpg')
for file in sys.argv[1:]:
show(file)
def show(file):
import gl, GL, DEVICE
jpegdata = open(file, 'r').read()
imgdata, width, height, bytesperpixel = decompress(jpegdata)
gl.foreground()
gl.prefsize(width, height)
win = gl.winopen(file)
if bytesperpixel == 1:
gl.cmode()
gl.pixmode(GL.PM_SIZE, 8)
gl.gconfig()
for i in range(256):
gl.mapcolor(i, i, i, i)
else:
gl.RGBmode()
gl.pixmode(GL.PM_SIZE, 32)
gl.gconfig()
gl.qdevice(DEVICE.REDRAW)
gl.qdevice(DEVICE.ESCKEY)
gl.qdevice(DEVICE.WINQUIT)
gl.qdevice(DEVICE.WINSHUT)
gl.lrectwrite(0, 0, width-1, height-1, imgdata)
while 1:
dev, val = gl.qread()
if dev in (DEVICE.ESCKEY, DEVICE.WINSHUT, DEVICE.WINQUIT):
break
if dev == DEVICE.REDRAW:
gl.lrectwrite(0, 0, width-1, height-1, imgdata)
gl.winclose(win)
# Now test the compression and write the result to a fixed filename
newjpegdata = compress(imgdata, width, height, bytesperpixel)
open('/tmp/j.jpg', 'w').write(newjpegdata)
# Module 'panel'
#
# Support for the Panel library.
# Uses built-in module 'pnl'.
# Applications should use 'panel.function' instead of 'pnl.function';
# most 'pnl' functions are transparently exported by 'panel',
# but dopanel() is overridden and you have to use this version
# if you want to use callbacks.
import pnl
debug = 0
# Test if an object is a list.
#
def is_list(x):
return type(x) == type([])
# Reverse a list.
#
def reverse(list):
res = []
for item in list:
res.insert(0, item)
return res
# Get an attribute of a list, which may itself be another list.
# Don't use 'prop' for name.
#
def getattrlist(list, name):
for item in list:
if item and is_list(item) and item[0] == name:
return item[1:]
return []
# Get a property of a list, which may itself be another list.
#
def getproplist(list, name):
for item in list:
if item and is_list(item) and item[0] == 'prop':
if len(item) > 1 and item[1] == name:
return item[2:]
return []
# Test if an actuator description contains the property 'end-of-group'
#
def is_endgroup(list):
x = getproplist(list, 'end-of-group')
return (x and x[0] == '#t')
# Neatly display an actuator definition given as S-expression
# the prefix string is printed before each line.
#
def show_actuator(prefix, a):
for item in a:
if not is_list(item):
print(prefix, item)
elif item and item[0] == 'al':
print(prefix, 'Subactuator list:')
for a in item[1:]:
show_actuator(prefix + ' ', a)
elif len(item) == 2:
print(prefix, item[0], '=>', item[1])
elif len(item) == 3 and item[0] == 'prop':
print(prefix, 'Prop', item[1], '=>', end=' ')
print(item[2])
else:
print(prefix, '?', item)
# Neatly display a panel.
#
def show_panel(prefix, p):
for item in p:
if not is_list(item):
print(prefix, item)
elif item and item[0] == 'al':
print(prefix, 'Actuator list:')
for a in item[1:]:
show_actuator(prefix + ' ', a)
elif len(item) == 2:
print(prefix, item[0], '=>', item[1])
elif len(item) == 3 and item[0] == 'prop':
print(prefix, 'Prop', item[1], '=>', end=' ')
print(item[2])
else:
print(prefix, '?', item)
# Exception raised by build_actuator or build_panel.
#
panel_error = 'panel error'
# Dummy callback used to initialize the callbacks.
#
def dummy_callback(arg):
pass
# Assign attributes to members of the target.
# Attribute names in exclist are ignored.
# The member name is the attribute name prefixed with the prefix.
#
def assign_members(target, attrlist, exclist, prefix):
for item in attrlist:
if is_list(item) and len(item) == 2 and item[0] not in exclist:
name, value = item[0], item[1]
ok = 1
if value[0] in '-0123456789':
value = eval(value)
elif value[0] == '"':
value = value[1:-1]
elif value == 'move-then-resize':
# Strange default set by Panel Editor...
ok = 0
else:
print('unknown value', value, 'for', name)
ok = 0
if ok:
lhs = 'target.' + prefix + name
stmt = lhs + '=' + repr(value)
if debug: print('exec', stmt)
try:
exec(stmt + '\n')
except KeyboardInterrupt: # Don't catch this!
raise KeyboardInterrupt
except:
print('assign failed:', stmt)
# Build a real actuator from an actuator description.
# Return a pair (actuator, name).
#
def build_actuator(descr):
namelist = getattrlist(descr, 'name')
if namelist:
# Assume it is a string
actuatorname = namelist[0][1:-1]
else:
actuatorname = ''
type = descr[0]
if type[:4] == 'pnl_': type = type[4:]
act = pnl.mkact(type)
act.downfunc = act.activefunc = act.upfunc = dummy_callback
#
assign_members(act, descr[1:], ['al', 'data', 'name'], '')
#
# Treat actuator-specific data
#
datalist = getattrlist(descr, 'data')
prefix = ''
if type[-4:] == 'puck':
prefix = 'puck_'
elif type == 'mouse':
prefix = 'mouse_'
assign_members(act, datalist, [], prefix)
#
return act, actuatorname
# Build all sub-actuators and add them to the super-actuator.
# The super-actuator must already have been added to the panel.
# Sub-actuators with defined names are added as members to the panel
# so they can be referenced as p.name.
#
# Note: I have no idea how panel.endgroup() works when applied
# to a sub-actuator.
#
def build_subactuators(panel, super_act, al):
#
# This is nearly the same loop as below in build_panel(),
# except a call is made to addsubact() instead of addact().
#
for a in al:
act, name = build_actuator(a)
act.addsubact(super_act)
if name:
stmt = 'panel.' + name + ' = act'
if debug: print('exec', stmt)
exec(stmt + '\n')
if is_endgroup(a):
panel.endgroup()
sub_al = getattrlist(a, 'al')
if sub_al:
build_subactuators(panel, act, sub_al)
#
# Fix the actuator to which whe just added subactuators.
# This can't hurt (I hope) and is needed for the scroll actuator.
#
super_act.fixact()
# Build a real panel from a panel definition.
# Return a panel object p, where for each named actuator a, p.name is a
# reference to a.
#
def build_panel(descr):
#
# Sanity check
#
if (not descr) or descr[0] != 'panel':
raise panel_error, 'panel description must start with "panel"'
#
if debug: show_panel('', descr)
#
# Create an empty panel
#
panel = pnl.mkpanel()
#
# Assign panel attributes
#
assign_members(panel, descr[1:], ['al'], '')
#
# Look for actuator list
#
al = getattrlist(descr, 'al')
#
# The order in which actuators are created is important
# because of the endgroup() operator.
# Unfortunately the Panel Editor outputs the actuator list
# in reverse order, so we reverse it here.
#
al = reverse(al)
#
for a in al:
act, name = build_actuator(a)
act.addact(panel)
if name:
stmt = 'panel.' + name + ' = act'
exec(stmt + '\n')
if is_endgroup(a):
panel.endgroup()
sub_al = getattrlist(a, 'al')
if sub_al:
build_subactuators(panel, act, sub_al)
#
return panel
# Wrapper around pnl.dopanel() which calls call-back functions.
#
def my_dopanel():
# Extract only the first 4 elements to allow for future expansion
a, down, active, up = pnl.dopanel()[:4]
if down:
down.downfunc(down)
if active:
active.activefunc(active)
if up:
up.upfunc(up)
return a
# Create one or more panels from a description file (S-expressions)
# generated by the Panel Editor.
#
def defpanellist(file):
import panelparser
descrlist = panelparser.parse_file(open(file, 'r'))
panellist = []
for descr in descrlist:
panellist.append(build_panel(descr))
return panellist
# Import everything from built-in method pnl, so the user can always
# use panel.foo() instead of pnl.foo().
# This gives *no* performance penalty once this module is imported.
#
from pnl import * # for export
dopanel = my_dopanel # override pnl.dopanel
This diff is collapsed.
This diff is collapsed.
......@@ -35,7 +35,6 @@ class AllTest(unittest.TestCase):
import _socket
self.check_all("BaseHTTPServer")
self.check_all("Bastion")
self.check_all("CGIHTTPServer")
self.check_all("ConfigParser")
self.check_all("Cookie")
......@@ -124,7 +123,6 @@ class AllTest(unittest.TestCase):
self.check_all("random")
self.check_all("re")
self.check_all("repr")
self.check_all("rexec")
self.check_all("rfc822")
self.check_all("rlcompleter")
self.check_all("robotparser")
......
##import Bastion
##
##Bastion._test()
This diff is collapsed.
This diff is collapsed.
......@@ -251,7 +251,7 @@ class ImportHooksTestCase(ImportHooksBaseTestCase):
i = ImpWrapper()
sys.meta_path.append(i)
sys.path_hooks.append(ImpWrapper)
mnames = ("colorsys", "urlparse", "distutils.core", "compiler.misc")
mnames = ("colorsys", "urlparse", "distutils.core")
for mname in mnames:
parent = mname.split(".")[0]
for n in list(sys.modules.keys()):
......
This diff is collapsed.
This diff is collapsed.
......@@ -11,10 +11,6 @@ from unittest import TestCase
StaticMethodType = type(staticmethod(lambda: None))
ClassMethodType = type(classmethod(lambda c: None))
# This next line triggers an error on old versions of pyclbr.
from commands import getstatus
# Here we test the python class browser code.
#
# The main function in this suite, 'testModule', compares the output
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment