I've received some enthusiastic emails from someone who wants to
revive restricted mode. He started out with a bunch of patches to the
CPython runtime using ctypes, which he attached to an App Engine bug:
http://code.google.com/p/googleappengine/issues/detail?id=671
Based on his code (the file secure.py is all you need, included in
secure.tar.gz) it seems he believes the only security leaks are
__subclasses__, gi_frame and gi_code. (I have since convinced him that
if we add "restricted" guards to these attributes, he doesn't need the
functions added to sys.)
I don't recall the exploits that Samuele once posted that caused the
death of rexec.py -- does anyone recall, or have a pointer to the
threads?
--
--Guido van Rossum (home page: http://www.python.org/~guido/)
Alright, I will re-submit with the contents pasted. I never use double
backquotes as I think them rather ugly; that is the work of an editor
or some automated program in the chain. Plus, it also messed up my
line formatting and now I have lines with one word on them... Anyway,
the contents of PEP 3145:
PEP: 3145
Title: Asynchronous I/O For subprocess.Popen
Author: (James) Eric Pruitt, Charles R. McCreary, Josiah Carlson
Type: Standards Track
Content-Type: text/plain
Created: 04-Aug-2009
Python-Version: 3.2
Abstract:
In its present form, the subprocess.Popen implementation is prone to
dead-locking and blocking of the parent Python script while waiting on data
from the child process.
Motivation:
A search for "python asynchronous subprocess" will turn up numerous
accounts of people wanting to execute a child process and communicate with
it from time to time reading only the data that is available instead of
blocking to wait for the program to produce data [1] [2] [3]. The current
behavior of the subprocess module is that when a user sends or receives
data via the stdin, stderr and stdout file objects, dead locks are common
and documented [4] [5]. While communicate can be used to alleviate some of
the buffering issues, it will still cause the parent process to block while
attempting to read data when none is available to be read from the child
process.
Rationale:
There is a documented need for asynchronous, non-blocking functionality in
subprocess.Popen [6] [7] [2] [3]. Inclusion of the code would improve the
utility of the Python standard library that can be used on Unix based and
Windows builds of Python. Practically every I/O object in Python has a
file-like wrapper of some sort. Sockets already act as such and for
strings there is StringIO. Popen can be made to act like a file by simply
using the methods attached the the subprocess.Popen.stderr, stdout and
stdin file-like objects. But when using the read and write methods of
those options, you do not have the benefit of asynchronous I/O. In the
proposed solution the wrapper wraps the asynchronous methods to mimic a
file object.
Reference Implementation:
I have been maintaining a Google Code repository that contains all of my
changes including tests and documentation [9] as well as blog detailing
the problems I have come across in the development process [10].
I have been working on implementing non-blocking asynchronous I/O in the
subprocess.Popen module as well as a wrapper class for subprocess.Popen
that makes it so that an executed process can take the place of a file by
duplicating all of the methods and attributes that file objects have.
There are two base functions that have been added to the subprocess.Popen
class: Popen.send and Popen._recv, each with two separate implementations,
one for Windows and one for Unix based systems. The Windows
implementation uses ctypes to access the functions needed to control pipes
in the kernel 32 DLL in an asynchronous manner. On Unix based systems,
the Python interface for file control serves the same purpose. The
different implementations of Popen.send and Popen._recv have identical
arguments to make code that uses these functions work across multiple
platforms.
When calling the Popen._recv function, it requires the pipe name be
passed as an argument so there exists the Popen.recv function that passes
selects stdout as the pipe for Popen._recv by default. Popen.recv_err
selects stderr as the pipe by default. "Popen.recv" and "Popen.recv_err"
are much easier to read and understand than "Popen._recv('stdout' ..." and
"Popen._recv('stderr' ..." respectively.
Since the Popen._recv function does not wait on data to be produced
before returning a value, it may return empty bytes. Popen.asyncread
handles this issue by returning all data read over a given time
interval.
The ProcessIOWrapper class uses the asyncread and asyncwrite functions to
allow a process to act like a file so that there are no blocking issues
that can arise from using the stdout and stdin file objects produced from
a subprocess.Popen call.
References:
[1] [ python-Feature Requests-1191964 ] asynchronous Subprocess
http://mail.python.org/pipermail/python-bugs-list/2006-December/
036524.html
[2] Daily Life in an Ivory Basement : /feb-07/problems-with-subprocess
http://ivory.idyll.org/blog/feb-07/problems-with-subprocess
[3] How can I run an external command asynchronously from Python? - Stack
Overflow
http://stackoverflow.com/questions/636561/how-can-i-run-an-external-
command-asynchronously-from-python
[4] 18.1. subprocess - Subprocess management - Python v2.6.2 documentation
http://docs.python.org/library/subprocess.html#subprocess.Popen.wait
[5] 18.1. subprocess - Subprocess management - Python v2.6.2 documentation
http://docs.python.org/library/subprocess.html#subprocess.Popen.kill
[6] Issue 1191964: asynchronous Subprocess - Python tracker
http://bugs.python.org/issue1191964
[7] Module to allow Asynchronous subprocess use on Windows and Posix
platforms - ActiveState Code
http://code.activestate.com/recipes/440554/
[8] subprocess.rst - subprocdev - Project Hosting on Google Code
http://code.google.com/p/subprocdev/source/browse/doc/subprocess.rst?spec...
[9] subprocdev - Project Hosting on Google Code
http://code.google.com/p/subprocdev
[10] Python Subprocess Dev
http://subdev.blogspot.com/
Copyright:
This P.E.P. is licensed under the Open Publication License;
http://www.opencontent.org/openpub/.
On Tue, Sep 8, 2009 at 22:56, Benjamin Peterson <benjamin(a)python.org> wrote:
> 2009/9/7 Eric Pruitt <eric.pruitt(a)gmail.com>:
>> Hello all,
>>
>> I have been working on adding asynchronous I/O to the Python
>> subprocess module as part of my Google Summer of Code project. Now
>> that I have finished documenting and pruning the code, I present PEP
>> 3145 for its inclusion into the Python core code. Any and all feedback
>> on the PEP (http://www.python.org/dev/peps/pep-3145/) is appreciated.
>
> Hi Eric,
> One of the reasons you're not getting many response is that you've not
> pasted the contents of the PEP in this message. That makes it really
> easy for people to comment on various sections.
>
> BTW, it seems like you were trying to use reST formatting with the
> text PEP layout. Double backquotes only mean something in reST.
>
>
> --
> Regards,
> Benjamin
>
Which I noticed since it's cited in the BeOpen license we still refer
to in LICENSE. Since pythonlabs.com itself is still up, it probably
isn't much work to make the logos.html URI work again, but I don't know
who maintains that page.
cheer,
Georg
--
Thus spake the Lord: Thou shalt indent with four spaces. No more, no less.
Four shall be the number of spaces thou shalt indent, and the number of thy
indenting shall be four. Eight shalt thou not indent, nor either indent thou
two, excepting that thou then proceed to four. Tabs are right out.
Hello everyone.
I see several problems with the two hex-conversion function pairs that
Python offers:
1. binascii.hexlify and binascii.unhexlify
2. bytes.fromhex and bytes.hex
Problem #1:
bytes.hex is not implemented, although it was specified in PEP 358.
This means there is no symmetrical function to accompany bytes.fromhex.
Problem #2:
Both pairs perform the same function, although The Zen Of Python suggests
that
"There should be one-- and preferably only one --obvious way to do it."
I do not understand why PEP 358 specified the bytes function pair although
it mentioned the binascii pair...
Problem #3:
bytes.fromhex may receive spaces in the input string, although
binascii.unhexlify may not.
I see no good reason for these two functions to have different features.
Problem #4:
binascii.unhexlify may receive both input types: strings or bytes, whereas
bytes.fromhex raises an exception when given a bytes parameter.
Again there is no reason for these functions to be different.
Problem #5:
binascii.hexlify returns a bytes type - although ideally, converting to hex
should
always return string types and converting from hex should always return
bytes.
IMO there is no meaning of bytes as an output of hexlify, since the output
is a
representation of other bytes.
This is also the suggested behavior of bytes.hex in PEP 358
Problems #4 and #5 call for a decision about the input and output of the
functions being discussed:
Option A : Strict input and output
unhexlify (and bytes.fromhex) may only receives string and may only return
bytes
hexlify (and bytes.hex) may only receives bytes and may only return strings
Option B : Robust input and strict output
unhexlify (and bytes.fromhex) may receive bytes and strings and may only
return bytes
hexlify (and bytes.hex) may receive bytes or strings and may only return
strings
Of course we may also consider a third option, which will allow the return
type of
all functions to be robust (perhaps specified in a keyword argument), but as
I wrote in
the description of problem #5, I see no sense in that.
Note that PEP 3137 describes: "... the more strict definitions of encoding
and decoding in
Python 3000: encoding always takes a Unicode string and returns a bytes
sequence, and decoding
always takes a bytes sequence and returns a Unicode string." - suggesting
option A.
To repeat problems #4 and #5, the current behavior does not match any
option:
* The return type of binascii.hexlify should be string, and this is not the
current behavior.
As for the input:
* Option A is not the current behavior because binascii.unhexlify may
receive both input types.
* Option B is not the current behavior because bytes.fromhex does not allow
bytes as input.
To fix these issues, three changes should be applied:
1. Deprecate bytes.fromhex. This fixes the following problems:
#4 (go with option B and remove the function that does not allow bytes
input)
#2 (the binascii functions will be the only way to "do it")
#1 (bytes.hex should not be implemented)
2. In order to keep the functionality that bytes.fromhex has over unhexlify,
the latter function should be able to handle spaces in its input (fix #3)
3. binascii.hexlify should return string as its return type (fix #5)
This is a follow up to PEP 3147. That PEP, already implemented in Python 3.2,
allows for Python source files from different Python versions to live together
in the same directory. It does this by putting a magic tag in the .pyc file
name and placing the .pyc file in a __pycache__ directory.
Distros such as Debian and Ubuntu will use this to greatly simplifying
deploying Python, and Python applications and libraries. Debian and Ubuntu
usually ship more than one version of Python, and currently have to play
complex games with symlinks to make this work. PEP 3147 will go a long way to
eliminating the need for extra directories and symlinks.
One more thing I've found we need though, is a way to handled shared libraries
for extension modules. Just as we can get name collisions on foo.pyc, we can
get collisions on foo.so. We obviously cannot install foo.so built for Python
3.2 and foo.so built for Python 3.3 in the same location. So symlink
nightmare's mini-me is back.
I have a fairly simple fix for this. I'd actually be surprised if this hasn't
been discussed before, but teh Googles hasn't turned up anything.
The idea is to put the Python version number in the shared library file name,
and extend .so lookup to find these extended file names. So for example, we'd
see foo.3.2.so instead, and Python would know how to dynload both that and the
traditional foo.so file too (for backward compatibility).
(On file naming: the original patch used foo.so.3.2 and that works just as
well, but I thought there might be tools that expect exactly a '.so' suffix,
so I changed it to put the Major.Minor version number to the left of the
extension. The exact naming scheme is of course open to debate.)
This is a much simpler patch than PEP 3147, though I'm not 100% sure it's the
right approach. The way this works is by modifying the configure and
Makefile.pre.in to put the version number in the $SO make variable. Python
parses its (generated) Makefile to find $SO and it uses this deep in the
bowels of distutils to decide what suffix to use when writing shared libraries
built by 'python setup.py build_ext'.
This means the patched Python only writes versioned .so files by default. I
personally don't see that as a problem, and it does not affect the test suite,
with the exception of one easily tweaked test. I don't know if third party
tools will care. The fact that traditional foo.so shared libraries will still
satisfy the import should be enough, I think.
The patch is currently Linux only, since I need this for Debian and Ubuntu and
wanted to keep the change narrow.
Other possible approaches:
* Extend the distutils API so that the .so file extension can be passed in,
instead of being essentially hardcoded to what Python's Makefile contains.
* Keep the dynload_shlib.c change, but modify the Debian/Ubuntu build
environment to pass in $SO to make (though the configure.in warning and
sleep is a little annoying).
* Add a ./configure option to enable this, which Debuntu's build would use.
The patch is available here:
http://pastebin.ubuntu.com/454512/
and my working branch is here:
https://code.edge.launchpad.net/~barry/python/sovers
Please let me know what you think. I'm happy to just commit this to the py3k
branch if there are no objections <wink>. I don't think a new PEP is in
order, but an update to PEP 3147 might make sense.
Cheers,
-Barry
While the EuroPython sprints are still going on, I am back home, and
after a somewhat restful night of sleep, I have some thoughts I'd like
to share before I get distracted. Note, I am jumping wildly between
topics.
- Commit privileges: Maybe we've been too careful with only giving
commit privileges to to experienced and trusted new developers. I
spoke to Ezio Melotti and from his experience with getting commit
privileges, it seems to be a case of "the lion is much more afraid of
you than you are afraid of the lion". I.e. having got privileges he
was very concerned about doing something wrong, worried about the
complexity of SVN, and so on. Since we've got lots of people watching
the commit stream, I think that there really shouldn't need to be a
worry at all about a new committer doing something malicious, and
there shouldn't be much worry about honest beginners' mistakes either
-- the main worry remains that new committers don't use their
privileges enough. So, my recommendation (which surely is a
turn-around of my *own* attitude in the past) is to give out more
commit privileges sooner.
- Concurrency and parallelism: Russel Winder and Sarah Mount pushed
the idea of CSP
(http://en.wikipedia.org/wiki/Communicating_sequential_processes) in
several talks at the conference. They (at least Russell) emphasized
the difference between concurrency (interleaved event streams) and
parallelism (using many processors to speed things up). Their
prediction is that as machines with many processing cores become more
prevalent, the relevant architecture will change from cores sharing a
single coherent memory (the model on which threads are based) to one
where each core has a limited amount of private memory, and
communication is done via message passing between the cores. This
gives them (and me :-) hope that the GIL won't be a problem as long as
we adopt a parallel processing model. Two competing models are the
Actor model, which is based on asynchronous communication, and CSP,
which is synchronous (when a writer writes to a channel, it blocks
until a reader reads that value -- a rendezvous). At least Sarah
suggested that both models are important. She also mentioned that a
merger is under consideration between the two major CSP-for-Python
packages, Py-CSP and Python-CSP. I also believe that the merger will
be based on the stdlib multiprocessing package, but I'm not sure. I do
expect that we may get some suggestions from that corner to make some
minor changes to details of multiprocessing (and perhaps threading),
and I think we should be open to those (I expect these will be good
suggestions for small tweaks, not major overhauls).
- After seeing Raymond's talk about monocle (search for it on PyPI) I
am getting excited again about PEP 380 (yield from, return values from
generators). Having read the PEP on the plane back home I didn't see
anything wrong with it, so it could just be accepted in its current
form. Implementation will still have to wait for Python 3.3 because of
the moratorium. (Although I wouldn't mind making an exception to get
it into 3.2.)
- This made me think of how the PEP process should evolve so as to not
require my personal approval for every PEP. I think the model for
future PEPs should be the one we used for PEP 3148 (futures, which was
just approved by Jesse): the discussion is led and moderated by one
designated "PEP handler" (a different one for each PEP) and the PEP
handler, after reviewing the discussion, decides when the PEP is
approved. A PEP handler should be selected for each PEP as soon as
possible; without a PEP handler, discussing a PEP is not all that
useful. The PEP handler should be someone respected by the community
with an interest in the subject of the PEP but at an arms' length (at
least) from the PEP author. The PEP handler will have to moderate
feedback, separating useful comments from (too much) bikeshedding,
repetitious lines of questioning, and other forms of obstruction. The
PEP handler should also set and try to maintain a schedule for the
discussion. Note that a schedule should not be used to break a tie --
it should be used to stop bikeshedding and repeat discussions, while
giving all interested parties a chance to comment. (I should say that
this is probably similar to the role of an IETF working group director
with respect to RFCs.)
- Specifically, if Raymond is interested, I wouldn't mind seeing him
as the PEP handler for PEP 380. For some of Martin von Löwis's PEPs
(382, 384) I think a PEP handler is sorely lacking -- from the
language summit it appeared as if nobody besides Martin understands
these PEPs.
- A lot of things seem to be happening to make PyPI better. Is this
being summarized somewhere? Based on some questions I received during
my keynote Q&A (http://bit.ly/bdflqa) I think not enough people are
aware of what we are already doing in this area. Frankly, I'm not sure
I do, either: I think I've heard of a GSOC student and of plans to
take over pypi.appspot.com (with the original developer's permission)
to become a full and up-to-date mirror. Mirroring apparently also
requires some client changes. Oh, and there's a proposed solution for
the "register user" problem where apparently the clients had been
broken by a unilateral change to the server to require a certain "yes
I agree" checkbox.
For a hopefully eventually exhaustive overview of what was
accomplished at EuroPython, go to http://wiki.europython.eu/After --
and if you know some blog about EuroPython not yet listed, please add
it there.
--
--Guido van Rossum (python.org/~guido)
I have two somewhat unrelated thoughts about PEPs.
* Accepted: header
When PEP 3147 was accepted, I had a few folks ask that this be recorded in the
PEP by including a link to the BDFL pronouncement email. I realized that
there's no formal way to express this in a PEP, and many PEPs in fact don't
record more than the fact that it was accepted. I'd like to propose
officially adding an Accepted: header which should include a URL to the email
or other web resource where the PEP is accepted. I've come as close as
possible to this (without modifying the supporting scripts or PEP 1) in PEP
3147:
http://www.python.org/dev/peps/pep-3147/
I'd be willing to update things if there are no objections.
* EOL schedule for older releases.
We have semi-formal policies for the lifetimes of Python releases, though I'm
not sure this policy is written down in any of the existing informational
PEPs. However, we have release schedule PEPs going back to Python 1.6. It
seems reasonable to me that we include end-of-life information in those PEPs.
For example, we could state that Python 2.4 is no longer even being maintained
for security, and we could state the projected date that Python 2.6 will go
into security-only maintenance mode.
I would not mandate that we go back and update all previous PEPs for either of
these ideas. We'd adopt them moving forward and allow anyone who's motivated
to backfill information opportunistically.
Thoughts?
-Barry
Hi All,
The documentation for the logging package doesn't include any mention of
unicode.
What's the recommended usage in terms of passing unicode vs encoded strings?
How does this differ from Python 2 to Python 3?
cheers,
Chris
--
Simplistix - Content Management, Batch Processing & Python Consulting
- http://www.simplistix.co.uk
Hello all,
My apologies in advance if email mangles whitespace in the code
examples. I can reformulate as a PEP if that is deemed useful and this
document can be found online at:
http://hg.python.org/unittest2/file/tip/description.txt
(Please excuse errors and omissions - but do feel free to point them out.)
This is a description, and request for feedback, of the unittest plugin
system that I am currently prototyping in the plugins branch of
unittest2_. My goal is to merge the plugin system back into unittest
itself in Python 3.2.
.. _unittest2: http://hg.python.org/unittest2
As part of the prototype I have been implementing some example plugins
(in unittest2/plugins/) so I can develop the mechanism against real
rather than imagined use cases. Jason Pellerin, creator of nose, has
been providing me with feedback and has been trying it out by porting
some of the nose plugins to unittest [#]_. He comments on the system "it
looks very flexible and clean". ;-)
Example plugins available and included:
* a pep8 and pyflakes checker
* a debugger plugin that drops you into pdb on test fail / error
* a doctest loader (looks for doctests in all text files in the
project)
* use a regex for matching files in test discovery instead of a glob
* growl notifications on test run start and stop
* filter individual *test methods* using a regex
* load test functions from modules as well as TestCases
* integration with the coverage module for coverage reporting
In addition I intend to create a plugin that outputs junit compatible
xml from a test run (for integration with tools like the hudson
continuous integration server) and a test runner that runs tests in
parallel using multiprocessing.
Not all of these will be included in the merge to unittest. Which ones
will is still an open question.
I'd like feedback on the proposal, and hopefully approval to port it
into unittest after discussion / amendment / completion. In particular
I'd like feedback on the basic system, plus which events should be
available and what information should be available in them. Note that
the system is *not* complete in the prototype. Enough is implemented to
get "the general idea" and to formalise the full system. It still needs
extensive tests and the extra work in TestProgram makes it abundantly
clear that refactoring there is well overdue...
In the details below open questions and todos are noted. I *really*
value feedback (but will ignore bikeshedding ;-)
.. note::
Throughout this document I refer to the prototype implementation
using names like ``unittest2.events.hooks``. Should this proposal be
accepted then the names will live in the unittest package instead of
unittest2.
The core classes for the event system live in the current
implementation in the ``unittest2.events`` namespace.
Abstract
========
unittest lacks a standard way of extending it to provide commonly
requested functionality, other than subclassing and overriding (and
reimplementing) parts of its behaviour. This document describes a plugin
system already partially prototyped in unittest2.
Aspects of the plugin system include:
* an events mechanism where handlers can be registered and called during
a test run
* a Plugin class built over the top of this for easy creation of plugins
* a configuration system for specifying which plugins should be loaded
and for configuring individual plugins
* command line integration
* the specific set of events and the information / actions available to them
As the plugin system essentially just adds event calls to key places it
has few backwards compatibility issues. Unfortunately existing
extensions that override the parts of unittest that call these events
will not be compatible with plugins that use them. Framework authors who
re-implement parts of unittest, for example custom test runners, may
want to add calling these events in appropriate places.
Rationale
=========
Why a plugin system for unittest?
unittest is the standard library test framework for Python but in recent
years has been eclipsed in functionality by frameworks like nose and
py.test. Among the reasons for this is that these frameworks are easier
to extend with plugins than unittest. unittest makes itself particularly
difficult to extend by using subclassing as its basic extension
mechanism. You subclass and override behaviour in its core classes like
the loader, runner and result classes.
This means that where you have more than one "extension" working in the
same area it is very hard for them to work together. Whilst various
extensions to unittest do exist (e.g. testtools, zope.testrunner etc)
they don't tend to work well together. In contrast the plugin system
makes creating extensions to unittest much simpler and less likely that
extensions will clash with each other.
nose itself exists as a large system built over the top of unittest.
Extending unittest in this way was very painful for the creators of
nose, and every release of Python breaks nose in some way due to changes
in unittest. One of the goals of the extension mechanism is to allow
nose2 to be a much thinner set of plugins over unittest(2) that is much
simpler to maintain [#]_. The early indications are that the proposed
system is a good fit for this goal.
Low Level Mechanism
====================
The basic mechanism is having events fired at various points during a
test run. Plugins can register event handler functions that will be
called with an event object. Multiple functions may be registered to
handle an event and event handlers can also be removed.
Over the top of this is a ``Plugin`` class that simplifies building
plugins on top of this mechanism. This is described in a separate section.
The events live on the ``unittest2.events.hooks`` class. Handlers are
added using ``+=`` and removed using ``-=``, a syntax borrowed from the
.NET system.
For example adding a handler for the ``startTestRun`` event::
from unittest2.events import hooks
def startTestRun(event):
print 'test run started at %s' % event.startTime
hooks.startTestRun += startTestRun
Handlers are called with an Event object specific to the event. Each
event provides different information on its event objects as attributes.
For example the attributes available on ``StartTestRunEvent`` objects are:
* ``suite`` - the test suite for the full test run
* ``runner`` - the test runner
* ``result`` - the test result
* ``startTime``
The name of events, whether any should be added or removed, and what
information is available on the event objects are all valid topics for
discussion. Specific events and the information available to them is
covered in a section below.
An example plugin using events directly is the ``doctestloader`` plugin.
Framework authors who re-implement parts of unittest, for example custom
test runners, may want to add calling these events in appropriate
places. This is very simple. For example the ``pluginsLoaded`` event is
fired with a ``PluginsLoadedEvent`` object that is instantiated without
parameters::
from unittest2.events import hooks, PluginsLoadedEvent
hooks.pluginsLoaded(PluginsLoadedEvent())
Why use event objects and not function parameters?
--------------------------------------------------
There are several reasons to use event objects instead of function
parameters. The *disadvantage* of this is that the information available
to an event is not obvious from the signature of a handler. There are
several compelling advantages however:
* the signature of all handler functions is identical and therefore easy
to remember
* backwards compatibility - new attributes can be added to event objects
(and parameters deprecated) without breaking existing plugins. Changing
the way a function is called (unless all handlers have a ``**kw``
signature) is much harder.
* several of the events have a lot of information available. This would
make the signature of handlers huge. With an event object handlers only
need to be aware of attributes they are interested in and ignore
information they aren't interested in ("only pay for what you eat").
* some of the attributes are mutable - the event object is shared
between all handlers, this would be less obvious if function parameters
were used
* calling multiple handlers and still returning a value (see the handled
pattern below)
The handled pattern
--------------------
Several events can be used to *override* the default behaviour. For
example the 'matchregexp' plugin uses the ``matchPath`` event to replace
the default way of matching files for loading as tests during test
discovery. The handler signals that it is handling this event, and the
default implementation should not be run, by setting ``event.handled =
True``::
def matchRegexp(event):
pattern = event.pattern
name = event.name
event.handled = True
path = event.path
if matchFullPath:
return re.match(pattern, path)
return re.match(pattern, name)
Where the default implementation returns a value, for example creating a
test suite, or in the case of ``matchPath`` deciding if a path matches a
file that should be loaded as a test, the handler can return a result.
If an event sets handled on an event then no more handlers will be
called for that event. Which events can be handled, and which not, is
discussed in the events section.
The Plugin Class
================
A sometimes-more-convenient way of creating plugins is to subclass the
``unittest2.events.Plugin`` class. By default subclassing ``Plugin``
will auto-instantiate the plugin and store the instance in a list of
loaded plugins.
Each plugin has a ``register()`` method that auto-hooks up all methods
whose names correspond to events. Plugin classes may also provide
``configSection`` and ``commandLineSwitch`` class attributes which
simplifies enabling the plugin through the command line and making
available a section from the configuration file(s).
A simple plugin using this is the 'debugger' plugin that starts ``pdb``
when the ``onTestFail`` event fires::
from unittest2.events import Plugin
import pdb
import sys
class Debugger(Plugin):
configSection = 'debugger'
commandLineSwitch = ('D', 'debugger', 'Enter pdb on test fail
or error')
def __init__(self):
self.errorsOnly = self.config.as_bool('errors-only',
default=False)
def onTestFail(self, event):
value, tb = event.exc_info[1:]
test = event.test
if self.errorsOnly and isinstance(value,
test.failureException):
return
original = sys.stdout
sys.stdout = sys.__stdout__
try:
pdb.post_mortem(tb)
finally:
sys.stdout = original
A plugin that doesn't want to be auto-instantiated (for example a base
class used for several plugins) can set ``autoCreate = False`` as a
class attribute. (This attribute is only looked for on the class
directly and so isn't inherited by subclasses.) If a plugin is
auto-instantiated then the instance will be set as the ``instance``
attribute on the plugin class.
``configSection`` and ``commandLineSwitch`` are described in the
`configuration system`_ and `command line integration`_ sections.
Plugin instances also have an ``unregister`` method that unhooks all
events. It doesn't exactly correspond to the ``register`` method (it
undoes some of the work done when a plugin is instantiated) and so can
only be called once.
Plugins to be loaded are specified in configuration files. For
frameworks not using the unittest test runner and configuration system
APIs for loading plugins are available in the form of the
``loadPlugins`` function (which uses the configuration system to load
plugins) and ``loadPlugin`` which loads an individual plugin by module
name. Loading plugins just means importing the module containing the plugin.
Configuration system
====================
By default the unittest2 test runner (triggered by the unit2 script or
for unittest ``python -m unittest``) loads two configuration files to
determine which plugins to load.
A user configuration file, ~/unittest.cfg (alternative name and location
would be possible), can specify plugins that will always be loaded. A
per-project configuration file, unittest.cfg which should be located in
the current directory when unit2 is launched, can specify plugins for
individual projects.
To support this system several command line options have been added to
the test runner::
--config=CONFIGLOCATIONS
Specify local config file location
--no-user-config Don't use user config file
--no-plugins Disable all plugins
Several config files can be specified using ``--config``. If the user
config is being loaded then it will be loaded first (if it exists),
followed by the project config (if it exists) *or* any config files
specified by ``--config``. ``--config`` can point to specific files, or
to a directory containing a ``unittest.cfg``.
Config files loaded later are merged into already loaded ones. Where a
*key* is in both the later key overrides the earlier one. Where a
section is in both but with different keys they are merged. (The
exception to keys overriding is the 'plugins' key in the unittest
section - these are combined to create a full list of plugins. Perhaps
multiline values in config files could also be merged?)
plugins to be loaded are specified in the ``plugins`` key of the
``unittest`` section::
[unittest]
plugins =
unittest2.plugins.checker
unittest2.plugins.doctestloader
unittest2.plugins.matchregexp
unittest2.plugins.moduleloading
unittest2.plugins.debugger
unittest2.plugins.testcoverage
unittest2.plugins.growl
unittest2.plugins.filtertests
The plugins are simply module names. They either hook themselves up
manually on import or are created by virtue of subclassing ``Plugin``. A
list of all loaded plugins is available as
``unittest2.events.loadedPlugins`` (a list of strings).
For accessing config values there is a ``getConfig(sectionName=None)``
function. By default it returns the whole config data-structure but it
an also return individual sections by name. If the section doesn't exist
an empty section will be returned. The config data-structure is not
read-only but there is no mechanism for persisting changes.
The config is a dictionary of ``Section`` objects, where a section is a
dictionary subclass with some convenience methods for accessing values::
section = getConfig(sectionName)
integer = section.as_int('foo', default=3)
number = section.as_float('bar', default=0.0)
# as_list returns a list with empty lines and comment lines removed
items = section.as_list('items', default=[])
# as_bool allows 'true', '1', 'on', 'yes' for True (matched
case-insensitively) and
# 'false', 'off', '0', 'no', '' for False
value = section.as_bool('value', default=True)
If a plugin specifies a ``configSection`` as a class attribute then that
section will be fetched and set as the ``config`` attribute on instances.
By convention plugins should use the 'always-on' key in their config
section to specify that the plugin should be switched on by default. If
'always-on' exists and is set to 'True' then the ``register()`` method
will be called on the plugin to hook up all events. If you don't want a
plugin to be auto-registered you should fetch the config section
yourself rather than using ``configSection``.
If the plugin is configured to be 'always-on', and is auto-registered,
then it doesn't need a command line switch to turn it on (although it
may add other command line switches or options) and
``commandLineSwitch`` will be ignored.
Command Line Interface
======================
Plugins may add command line options, either switches with a callback
function or options that take values and will be added to a list. There
are two functions that do this: ``unittest2.events.addOption`` and
``unittest2.events.addDiscoveryOption``. Some of the events are only
applicable to test discovery (``matchPath`` is the only one currently I
think), options that use these events should use ``addDiscoveryOption``
which will only be used if test discovery is invoked.
Both functions have the same signature::
addDiscoveryOption(callback, opt=None, longOpt=None, help=None)
addOption(plugin.method, 'X', '--extreme', 'Run tests in extreme mode')
* ``callback`` is a callback function (taking no arguments) to be
invoked if the option is on *or* a list indicating that this is an
option that takes arguments, values passed in at the command line will
be added to the list
* ``opt`` is a short option for the command (or None) not including the
leading '-'
* ``longopt`` a long option for the command (or None) not including the
leading '--'
* ``help`` is optional help text for the option, to be displayed by
``unit2 -h``
Lowercase short options are reserved for use by unittest2 internally.
Plugins may only add uppercase short options.
If a plugin needs a simple command line switch (on/off) then it can set
the ``commandLineSwitch`` class attribute to a tuple of ``(opt, longOpt,
help)``. The ``register()`` method will be used as the callback
function, automatically hooking the plugin up to events if it is
switched on.
The Events
==========
This section details the events implemented so far, the order they are
called in, what attributes are available on the event objects, whether
the event is 'handleable' (and what that means for the event), plus the
intended use case for the event.
Events in rough order are:
* ``pluginsLoaded``
* ``handleFile``
* ``matchPath``
* ``loadTestsFromNames``
* ``loadTestsFromName``
* ``loadTestsFromModule``
* ``loadTestsFromTestCase``
* ``getTestCaseNames``
* ``runnerCreated``
* ``startTestRun``
* ``startTest``
* ``onTestFail``
* ``stopTest``
* ``stopTestRun``
pluginsLoaded
-------------
This event is useful for plugin initialisation. It is fired after all
plugins have been loaded, the config file has been read and command line
options processed.
The ``PluginsLoadedEvent`` has one attribute: ``loadedPlugins`` which
is a list of strings referring to all plugin modules that have been loaded.
handleFile
----------
This event is fired when a file is looked at in test discovery or a
*filename* is passed at the command line. It can be used for loading
tests from non-Python files, like doctests from text files, or adding
tests for a file like pep8 and pyflakes checks.
A ``HandleFileEvent`` object has the following attributes:
* ``extraTests`` - a list, extend this with tests to *add* tests that
will be loaded from this file without preventing the default test loading
* ``name`` - the name of the file
* ``path`` - the full path of the file being looked at
* ``loader`` - the ``TestLoader`` in use
* ``pattern`` - the pattern being used to match files, or None if not
called during test discovery
* ``top_level_directory`` - the top level directory of the project tests
are being loaded from, or the current working directory if not called
during test discovery
This event *can* be handled. If it is handled then the handler should
return a test suite or None. Returning None means no tests will be
loaded from this file. If any plugin has created any ``extraTests`` then
these will be used even if a handler handles the event and returns None.
If this event is not handled then it will be matched against the pattern
(test discovery only) and either be rejected or go through for standard
test loading.
matchPath
---------
``matchPath`` is called to determine if a file should be loaded as a
test module. This event only fires during test discovery.
``matchPath`` is only fired if the filename can be converted to a valid
python module name, this is because tests are loaded by importing. If
you want to load tests from files whose paths don't translate to valid
python identifiers then you should use ``handleFile`` instead.
A ``MatchPathEvent`` has the following attributes:
* ``path`` - full path to the file
* ``name`` - filename only
* ``pattern`` - pattern being used for discovery
This event *can* be handled. If it is handled then the handler should
return True or False to indicate whether or not test loading should be
attempted from this file. If this event is not handled then the pattern
supplied to test discovery will be used as a glob pattern to match the
filename.
loadTestsFromNames
------------------
This event is fired when ``TestLoader.loadTestsFromNames`` is called.
Attributes on the ``LoadFromNamesEvent`` object are:
* ``loader`` - the test loader
* ``names`` - a list of the names being loaded
* ``module`` - the module passed to ``loader.loadTestsFromNames(...)``
* ``extraTests`` - a list of extra tests to be added to the suites
loaded from the names
This event can be handled. If it is handled then the handler should
return a list of suites or None. Returning None means no tests will be
loaded from these names. If any plugin has created any ``extraTests``
then these will be used even if a handler handles the event and returns
None.
If this event is not handled then ``loader.loadTestFromName`` will be
called for each name to build up the list of suites.
loadTestsFromName
-----------------
This event is fired when ``TestLoader.loadTestsFromName`` is called.
Attributes on the ``LoadFromNameEvent`` object are:
* ``loader`` - the test loader
* ``name`` - the name being loaded
* ``module`` - the module passed to ``loader.loadTestsFromName(...)``
* ``extraTests`` - a suite of extra tests to be added to the suite
loaded from the name
This event can be handled. If it is handled then the handler should
return a TestSuite or None. Returning None means no tests will be loaded
from this name. If any plugin has created any ``extraTests`` then these
will be used even if a handler handles the event and returns None.
If the event is not handled then each name will be resolved and tests
loaded from it, which may mean calling ``loader.loadTestsFromModule`` or
``loader.loadTestsFromTestCase``.
loadTestsFromModule
-------------------
This event is fired when ``TestLoader.loadTestsFromModule`` is called.
It can be used to customise the loading of tests from a module, for
example loading tests from functions as well as from TestCase classes.
Attributes on the ``LoadFromModuleEvent`` object are:
* ``loader`` - the test loader
* ``module`` - the module object tests
* ``extraTests`` - a suite of extra tests to be added to the suite
loaded from the module
This event can be handled. If it is handled then the handler should
return a TestSuite or None. Returning None means no tests will be loaded
from this module. If any plugin has created any ``extraTests`` then
these will be used even if a handler handles the event and returns None.
If the event is not handled then ``loader.loadTestsFromTestCase`` will
be called for every TestCase in the module.
Event if the event is handled, if the module defines a ``load_tests``
function then it *will* be called for the module. This removes the
responsibility for implementing the ``load_tests`` protocol from plugin
authors.
loadTestsFromTestCase
---------------------
This event is fired when ``TestLoader.loadTestsFromTestCase`` is called.
It could be used to customise the loading of tests from a TestCase, for
example loading tests with an alternative prefix or created generative /
parameterized tests.
Attributes on the ``LoadFromTestCaseEvent`` object are:
* ``loader`` - the test loader
* ``testCase`` - the test case class being loaded
* ``extraTests`` - a suite of extra tests to be added to the suite
loaded from the TestCase
This event can be handled. If it is handled then the handler should
return a TestSuite or None. Returning None means no tests will be loaded
from this module. If any plugin has created any ``extraTests`` then
these will be used even if a handler handles the event and returns None
If the event is not handled then ``loader.getTestCaseNames`` will be
called to get method names from the test case and a suite will be
created by instantiating the TestCase class with each name it returns.
getTestCaseNames
----------------
This event is fired when ``TestLoader.getTestCaseNames`` is called. It
could be used to customise the method names used to load tests from a
TestCase, for example loading tests with an alternative prefix from the
default or filtering for specific names.
Attributes on the ``GetTestCaseNamesEvent`` object are:
* ``loader`` - the test loader
* ``testCase`` - the test case class that tests are being loaded from
* ``testMethodPrefix`` - set to None, modify this attribute to *change*
the prefix being used for this class
* ``extraNames`` - a list of extra names to use for this test case as
well as the default ones
* ``excludedNames`` - a list of names to exclude from loading from this
class
This event can be handled. If it is handled it should return a list of
strings. Note that if this event returns an empty list (or None which
will be replaced with an empty list then ``loadTestsFromTestCase`` will
still check to see if the TestCase has a ``runTest`` method.
Even if the event is handled ``extraNames`` will still be added to the
list, however *excludedNames`` won't be removed as they are filtered out
by the default implementation which looks for all attributes that are
methods (or callable) whose name begins with ``loader.testMethodPrefix``
(or ``event.testMethodPrefix`` if that is set) and aren't in the list of
excluded names (converted to a set first for efficient lookup).
The list of names will also be sorted using ``loader.sortTestMethodsUsing``.
runnerCreated
-------------
This event is fired when the ``TextTestRunner`` is instantiated. It can
be used to customize the test runner, for example replace the stream and
result class, without needing to write a custom test harness. This
should allow the default test runner script (``unit2`` or ``python -m
untitest``) to be suitable for a greater range of projects. Projects
that want to use custom test reporting should be able to do it through a
plugin rather than having to rebuild the runner and result machinery,
which also requires writing custom test collection too.
The ``RunnerCreatedEvent`` object only has one attribute; ``runner``
which is the runner instance.
startTestRun
------------
This event is fired when the test run is started. This is used, for
example, by the growl notifier that displays a growl notification when a
test run begins. It can also be used for filtering tests after they have
all been loaded or for taking over the test run machinery altogether,
for distributed testing for example.
The ``StartTestRunEvent`` object has the following attributes:
* ``test`` - the full suite of all tests to be run (may be modified in
place)
* ``result`` - the result object
* ``startTime`` - the time the test run started
Currently this event can be handled. This prevents the normal test run
from executing, allowing an alternative implementation, but the return
value is unused. Handling this event (as with handling any event)
prevents other plugins from executing. This means that the it wouldn't
be possible to safely combine a distributed test runner with a plugin
that filters the suite. Fixing this issue is one of the open issues with
the plugin system.
startTest
---------
This event is fired immediately before a test is executed (inside
``TestCase.run(...)``).
The ``StartTestEvent`` object has the following attributes:
* ``test`` - the test to be run
* ``result`` - the result object
* ``startTime`` - the time the test starts execution
This event cannot be handled.
onTestFail
----------
This event is fired when a test setUp, a test, a tearDown or a cleanUp
fails or errors. It is currently used by the debugger plugin. It is
*not* currently called for 'internal' unittest exceptions like
``SkipTest`` or expected failures and unexpected successes.
Attributes on the ``TestFailEvent`` are:
* ``test`` - the test
* ``result`` - the result
* ``exc_info`` - the result of ``sys.exc_info()`` after the error / fail
* ``when`` - one of 'setUp', 'call', 'tearDown', or 'cleanUp'
This event cannot be handled. Should this event be able to suppress
raised exceptions? It should also be able to modify the traceback so
that bare asserts could be used but still provide useful diagnostic
information. Should this event be fired for test skips?
stopTest
--------
This event is fired when a test execution is completed. It includes a
great deal of information about the test and could be used to completely
replace test reporting, making the test result potentially obsolete. It
will be used by the junit-xml plugin to generate the xml reports
describing the test run.
If there are errors during a tearDown or clean up functions then this
event may be fired several times for a test. For each call the ``stage``
will be different, although there could be several errors during clean
up functions.
Attributes on the ``StopTestEvent`` are:
* ``test`` - the test
* ``result`` - the result
* ``exc_info`` - the result of ``sys.exc_info()`` after an error / fail
or None for success
* ``stopTime``- time the test stopped, including tear down and clean up
functions
* ``timeTaken`` - total time for test execution from setUp to clean up
functions
* ``stage`` - one of setUp, call, tearDown, cleanUp, or None for success
* ``outcome`` - one of passed, failed, error, skipped,
unexpectedSuccess, expectedFailure
The outcomes all correspond to an attribute that will be set to True or
False depending on outcome:
* ``passed``
* ``failed``
* ``error``
* ``skipped``
* ``unexpectedSuccess``
* ``expectedFailure``
In addition there is a ``skipReason`` that will be None unless the test
was skipped, in which case it will be a string containing the reason.
This event cannot be handled.
stopTestRun
-----------
This event is fired when the test run completes. It is useful for
reporting tools.
The ``StopTestRunEvent`` event objects have the following attributes:
* ``runner`` - the test runner
* ``result`` - the test result
* ``stopTime`` - the time the test run completes
* ``timeTaken`` - total time taken by the test run
At 11:57 AM 7/23/2010 +0100, Brett Cannon wrote:
>On Thu, Jul 22, 2010 at 19:19, P.J. Eby
><<mailto:pje@telecommunity.com>pje(a)telecommunity.com> wrote:
>
>What does "is not a package" actually mean in that context?
>
>
>The module is a module but not a package.
Um... that's not any clearer. Are you saying that a module of the
same name takes precedence over a package? Is that the current
precedence as well?
>Regarding load_module_with_path(), how does its specification differ
>from simply creating a module in sys.modules, setting its __path__,
>and then invoking the standard load_module()? Â (i.e., is this
>method actually needed, since a correct PEP 302 loader *must* reuse
>an existing module object in sys.modules)
>
>
>It must reuse the module itself but a proper reload would reset
>__path__ as leaving it unchanged is not a proper resetting of the
>module object. So this module is needed in order to force the loaderÂ
Um, no. Reloading doesn't reset the module contents, not even
__path__. Never has, from Python 2.2 through 2.7 -- even in 3.1. At
least, not for normal filesystem .py/.pyc files. (I tested with
'os', adding an extra 'foo' attribute, and also setting a __path__;
both were unaffected by reload(), in all 7 Python versions.
Perhaps you're saying this happens with zipfiles, or packages that
already have a __path__, or...?
>Â
>
>Am I correct in understanding that, as written, one would have to
>redefine __import__ to implement this in a library for older Python
>versions? Â Or is it implementable as a meta_path importer?
>
>
>Redefine __import__ (unless Martin and I are missing something, but
>I tried to think of how to implement this use sys.meta_path and
>couldn't come up with a solution).
I'm thinking it *could* be done with a meta_path hook, but only by
doubling the search length in the event that the search failed. That
seems a bit icky, but replacing the entire import process seems
ickier (more code surface to maintain, more bug potential) in the
case of supporting older Pythons.