summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin H. Johnson <robbat2@gentoo.org>2015-08-08 13:49:04 -0700
committerRobin H. Johnson <robbat2@gentoo.org>2015-08-08 17:38:18 -0700
commit56bd759df1d0c750a065b8c845e93d5dfa6b549d (patch)
tree3f91093cdb475e565ae857f1c5a7fd339e2d781e /dev-python/paste
downloadgentoo-56bd759df1d0c750a065b8c845e93d5dfa6b549d.tar.gz
gentoo-56bd759df1d0c750a065b8c845e93d5dfa6b549d.tar.bz2
gentoo-56bd759df1d0c750a065b8c845e93d5dfa6b549d.zip
proj/gentoo: Initial commit
This commit represents a new era for Gentoo: Storing the gentoo-x86 tree in Git, as converted from CVS. This commit is the start of the NEW history. Any historical data is intended to be grafted onto this point. Creation process: 1. Take final CVS checkout snapshot 2. Remove ALL ChangeLog* files 3. Transform all Manifests to thin 4. Remove empty Manifests 5. Convert all stale $Header$/$Id$ CVS keywords to non-expanded Git $Id$ 5.1. Do not touch files with -kb/-ko keyword flags. Signed-off-by: Robin H. Johnson <robbat2@gentoo.org> X-Thanks: Alec Warner <antarus@gentoo.org> - did the GSoC 2006 migration tests X-Thanks: Robin H. Johnson <robbat2@gentoo.org> - infra guy, herding this project X-Thanks: Nguyen Thai Ngoc Duy <pclouds@gentoo.org> - Former Gentoo developer, wrote Git features for the migration X-Thanks: Brian Harring <ferringb@gentoo.org> - wrote much python to improve cvs2svn X-Thanks: Rich Freeman <rich0@gentoo.org> - validation scripts X-Thanks: Patrick Lauer <patrick@gentoo.org> - Gentoo dev, running new 2014 work in migration X-Thanks: Michał Górny <mgorny@gentoo.org> - scripts, QA, nagging X-Thanks: All of other Gentoo developers - many ideas and lots of paint on the bikeshed
Diffstat (limited to 'dev-python/paste')
-rw-r--r--dev-python/paste/Manifest2
-rw-r--r--dev-python/paste/files/paste-1.7.5.1-email-mime.patch19
-rw-r--r--dev-python/paste/files/paste-1.7.5.1-fix-tests-for-pypy.patch24
-rw-r--r--dev-python/paste/files/paste-1.7.5.1-hmac.patch11
-rw-r--r--dev-python/paste/files/paste-1.7.5.1-python27-lambda.patch12
-rw-r--r--dev-python/paste/files/paste-1.7.5.1-rfc822.patch14
-rw-r--r--dev-python/paste/files/paste-1.7.5.1-types.patch57
-rw-r--r--dev-python/paste/files/paste-1.7.5.1-unbundle-stdlib.patch1211
-rw-r--r--dev-python/paste/files/paste-1.7.5.1-unbundle-tempita.patch1860
-rw-r--r--dev-python/paste/files/paste-1.7.5.1-userdict.patch64
-rw-r--r--dev-python/paste/files/paste-2.0.2-unbundle-tempita.patch36
-rw-r--r--dev-python/paste/metadata.xml17
-rw-r--r--dev-python/paste/paste-1.7.5.1-r1.ebuild69
-rw-r--r--dev-python/paste/paste-1.7.5.1-r2.ebuild82
-rw-r--r--dev-python/paste/paste-2.0.2.ebuild73
15 files changed, 3551 insertions, 0 deletions
diff --git a/dev-python/paste/Manifest b/dev-python/paste/Manifest
new file mode 100644
index 000000000000..2b6d4408db17
--- /dev/null
+++ b/dev-python/paste/Manifest
@@ -0,0 +1,2 @@
+DIST Paste-1.7.5.1.tar.gz 523304 SHA256 11645842ba8ec986ae8cfbe4c6cacff5c35f0f4527abf4f5581ae8b4ad49c0b6 SHA512 058a86dec41f132c22b14f3bc882d9c02c04bb0cc9ea5fc5371911698c3d7a89859742a4b806ad388c7fe37289b816db16c50bce21f56c8371293c4d91b5ccb6 WHIRLPOOL 5928a6f20c136a77576b653df34337f1eb110845b8d53d7637eeb6c792fe7f10fe622adefec382378aef16c1abee91c37e90e5fe17dbaa07046681a393f6c5c8
+DIST Paste-2.0.2.tar.gz 627842 SHA256 adac3ac893a2dac6b8ffd49901377dd6819e05be3436b374d698641071daba99 SHA512 32eddeab1bab9d0b1a30848d89613b40ed02a77912f7ef5a86880e1c1a860c5637cd45114bd58cc5ac84410a569ede67e6ebd7afd2007c588433d54d6940f529 WHIRLPOOL 08c997079e632b92e592211d942298235228ec22b997d549f608552f139a941cdd47a5c2da859867c279cab0ad7f388823f1af163ed7ea8aeaaa7558407e3750
diff --git a/dev-python/paste/files/paste-1.7.5.1-email-mime.patch b/dev-python/paste/files/paste-1.7.5.1-email-mime.patch
new file mode 100644
index 000000000000..4c06997b0809
--- /dev/null
+++ b/dev-python/paste/files/paste-1.7.5.1-email-mime.patch
@@ -0,0 +1,19 @@
+--- a/paste/exceptions/reporter.py
++++ b/paste/exceptions/reporter.py
+@@ -1,8 +1,14 @@
+ # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
+ # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
+
+-from email.MIMEText import MIMEText
+-from email.MIMEMultipart import MIMEMultipart
++try:
++ from email.MIMEText import MIMEText
++except:
++ from email.mime.text import MIMEText
++try:
++ from email.MIMEMultipart import MIMEMultipart
++except:
++ from email.mime.multipart import MIMEMultipart
+ import smtplib
+ import time
+ try:
diff --git a/dev-python/paste/files/paste-1.7.5.1-fix-tests-for-pypy.patch b/dev-python/paste/files/paste-1.7.5.1-fix-tests-for-pypy.patch
new file mode 100644
index 000000000000..b5e9430c3bb9
--- /dev/null
+++ b/dev-python/paste/files/paste-1.7.5.1-fix-tests-for-pypy.patch
@@ -0,0 +1,24 @@
+Ignore the exception detail for two exceptions that are slightly
+different on pypy.
+
+diff -r 7f90a96378ed tests/test_template.txt
+--- a/tests/test_template.txt Mon Mar 05 21:14:08 2012 +0100
++++ b/tests/test_template.txt Wed May 16 23:29:46 2012 +0200
+@@ -6,7 +6,7 @@
+ 'Hi Ian'
+ >>> Template('Hi {{repr(name)}}').substitute(name='Ian')
+ "Hi 'Ian'"
+- >>> Template('Hi {{name+1}}').substitute(name='Ian')
++ >>> Template('Hi {{name+1}}').substitute(name='Ian') #doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ ...
+ TypeError: cannot concatenate 'str' and 'int' objects at line 1 column 6
+@@ -125,7 +125,7 @@
+ >>> sub('{{default x=1}}{{x}}')
+ '1'
+ >>> # The normal case:
+- >>> sub('{{x}}')
++ >>> sub('{{x}}') #doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ ...
+ NameError: name 'x' is not defined at line 1 column 3
diff --git a/dev-python/paste/files/paste-1.7.5.1-hmac.patch b/dev-python/paste/files/paste-1.7.5.1-hmac.patch
new file mode 100644
index 000000000000..c86d0f977cfe
--- /dev/null
+++ b/dev-python/paste/files/paste-1.7.5.1-hmac.patch
@@ -0,0 +1,11 @@
+--- a/paste/auth/cookie.py
++++ b/paste/auth/cookie.py
+@@ -52,7 +52,7 @@
+
+ def make_time(value):
+ return time.strftime("%Y%m%d%H%M", time.gmtime(value))
+-_signature_size = len(hmac.new('x', 'x', sha1).digest())
++_signature_size = len(hmac.new(b'x', b'x', sha1).digest())
+ _header_size = _signature_size + len(make_time(time.time()))
+
+ # @@: Should this be using urllib.quote?
diff --git a/dev-python/paste/files/paste-1.7.5.1-python27-lambda.patch b/dev-python/paste/files/paste-1.7.5.1-python27-lambda.patch
new file mode 100644
index 000000000000..91dcad137153
--- /dev/null
+++ b/dev-python/paste/files/paste-1.7.5.1-python27-lambda.patch
@@ -0,0 +1,12 @@
+diff -r 30425672adf7 paste/auth/cookie.py
+--- a/paste/auth/cookie.py Wed Jun 23 17:15:45 2010 -0500
++++ b/paste/auth/cookie.py Mon Aug 02 20:06:43 2010 -0700
+@@ -62,7 +62,7 @@
+ _decode = [(v, k) for (k, v) in _encode]
+ _decode.reverse()
+ def encode(s, sublist = _encode):
+- return reduce((lambda a, (b, c): a.replace(b, c)), sublist, str(s))
++ return reduce((lambda a, b: a.replace(b[0], b[1])), sublist, str(s))
+ decode = lambda s: encode(s, _decode)
+
+ class CookieTooLarge(RuntimeError):
diff --git a/dev-python/paste/files/paste-1.7.5.1-rfc822.patch b/dev-python/paste/files/paste-1.7.5.1-rfc822.patch
new file mode 100644
index 000000000000..e0950fcaabef
--- /dev/null
+++ b/dev-python/paste/files/paste-1.7.5.1-rfc822.patch
@@ -0,0 +1,14 @@
+--- a/paste/httpheaders.py
++++ b/paste/httpheaders.py
+@@ -137,7 +137,10 @@
+ import mimetypes
+ import urllib2
+ import re
+-from rfc822 import formatdate, parsedate_tz, mktime_tz
++try:
++ from rfc822 import formatdate, parsedate_tz, mktime_tz
++except ImportError:
++ from email.utils import formatdate, parsedate_tz, mktime_tz
+ from time import time as now
+ from httpexceptions import HTTPBadRequest
+
diff --git a/dev-python/paste/files/paste-1.7.5.1-types.patch b/dev-python/paste/files/paste-1.7.5.1-types.patch
new file mode 100644
index 000000000000..87ea53c39648
--- /dev/null
+++ b/dev-python/paste/files/paste-1.7.5.1-types.patch
@@ -0,0 +1,57 @@
+--- a/paste/lint.py
++++ b/paste/lint.py
+@@ -111,7 +111,6 @@
+
+ import re
+ import sys
+-from types import DictType, StringType, TupleType, ListType
+ import warnings
+
+ header_re = re.compile(r'^[a-zA-Z][a-zA-Z0-9\-_]*$')
+@@ -282,7 +281,7 @@
+ "Iterator garbage collected without being closed")
+
+ def check_environ(environ):
+- assert type(environ) is DictType, (
++ assert isinstance(environ,dict), (
+ "Environment is not of the right type: %r (environment: %r)"
+ % (type(environ), environ))
+
+@@ -309,11 +308,11 @@
+ if '.' in key:
+ # Extension, we don't care about its type
+ continue
+- assert type(environ[key]) is StringType, (
++ assert isinstance(environ[key], str), (
+ "Environmental variable %s is not a string: %r (value: %r)"
+ % (key, type(environ[key]), environ[key]))
+
+- assert type(environ['wsgi.version']) is TupleType, (
++ assert isinstance(environ['wsgi.version'], tuple), (
+ "wsgi.version should be a tuple (%r)" % environ['wsgi.version'])
+ assert environ['wsgi.url_scheme'] in ('http', 'https'), (
+ "wsgi.url_scheme unknown: %r" % environ['wsgi.url_scheme'])
+@@ -359,7 +358,7 @@
+ % (wsgi_errors, attr))
+
+ def check_status(status):
+- assert type(status) is StringType, (
++ assert isinstance(status, str), (
+ "Status must be a string (not %r)" % status)
+ # Implicitly check that we can turn it into an integer:
+ status_code = status.split(None, 1)[0]
+@@ -374,12 +373,12 @@
+ % status, WSGIWarning)
+
+ def check_headers(headers):
+- assert type(headers) is ListType, (
++ assert isinstance(headers,list), (
+ "Headers (%r) must be of type list: %r"
+ % (headers, type(headers)))
+ header_names = {}
+ for item in headers:
+- assert type(item) is TupleType, (
++ assert isinstance(item, tuple), (
+ "Individual headers (%r) must be of type tuple: %r"
+ % (item, type(item)))
+ assert len(item) == 2
diff --git a/dev-python/paste/files/paste-1.7.5.1-unbundle-stdlib.patch b/dev-python/paste/files/paste-1.7.5.1-unbundle-stdlib.patch
new file mode 100644
index 000000000000..b389df2482c7
--- /dev/null
+++ b/dev-python/paste/files/paste-1.7.5.1-unbundle-stdlib.patch
@@ -0,0 +1,1211 @@
+--- a/paste/debug/fsdiff.py
++++ b/paste/debug/fsdiff.py
+@@ -12,7 +12,10 @@
+ import os
+ from fnmatch import fnmatch
+ from datetime import datetime
+-from paste.util.UserDict24 import IterableUserDict
++try:
++ from UserDict import IterableUserDict
++except ImportError:
++ from paste.util.UserDict24 import IterableUserDict
+ import operator
+ import re
+
+--- a/paste/debug/doctest_webapp.py
++++ b/paste/debug/doctest_webapp.py
+@@ -8,10 +8,7 @@
+ These are functions for use when doctest-testing a document.
+ """
+
+-try:
+- import subprocess
+-except ImportError:
+- from paste.util import subprocess24 as subprocess
++import subprocess
+ import doctest
+ import os
+ import sys
+--- a/paste/debug/wdg_validate.py
++++ b/paste/debug/wdg_validate.py
+@@ -6,10 +6,7 @@
+ """
+
+ from cStringIO import StringIO
+-try:
+- import subprocess
+-except ImportError:
+- from paste.util import subprocess24 as subprocess
++import subprocess
+ from paste.response import header_value
+ import re
+ import cgi
+--- a/paste/fixture.py
++++ b/paste/fixture.py
+@@ -26,10 +26,7 @@
+ except ImportError:
+ from StringIO import StringIO
+ import re
+-try:
+- import subprocess
+-except ImportError:
+- from paste.util import subprocess24 as subprocess
++import subprocess
+
+ from paste import wsgilib
+ from paste import lint
+--- a/paste/util/subprocess24.py
++++ /dev/null
+@@ -1,1152 +0,0 @@
+-# subprocess - Subprocesses with accessible I/O streams
+-#
+-# For more information about this module, see PEP 324.
+-#
+-# This module should remain compatible with Python 2.2, see PEP 291.
+-#
+-# Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se>
+-#
+-# Licensed to PSF under a Contributor Agreement.
+-# See http://www.python.org/2.4/license for licensing details.
+-
+-r"""subprocess - Subprocesses with accessible I/O streams
+-
+-This module allows you to spawn processes, connect to their
+-input/output/error pipes, and obtain their return codes. This module
+-intends to replace several other, older modules and functions, like:
+-
+-os.system
+-os.spawn*
+-os.popen*
+-popen2.*
+-commands.*
+-
+-Information about how the subprocess module can be used to replace these
+-modules and functions can be found below.
+-
+-
+-
+-Using the subprocess module
+-===========================
+-This module defines one class called Popen:
+-
+-class Popen(args, bufsize=0, executable=None,
+- stdin=None, stdout=None, stderr=None,
+- preexec_fn=None, close_fds=False, shell=False,
+- cwd=None, env=None, universal_newlines=False,
+- startupinfo=None, creationflags=0):
+-
+-
+-Arguments are:
+-
+-args should be a string, or a sequence of program arguments. The
+-program to execute is normally the first item in the args sequence or
+-string, but can be explicitly set by using the executable argument.
+-
+-On UNIX, with shell=False (default): In this case, the Popen class
+-uses os.execvp() to execute the child program. args should normally
+-be a sequence. A string will be treated as a sequence with the string
+-as the only item (the program to execute).
+-
+-On UNIX, with shell=True: If args is a string, it specifies the
+-command string to execute through the shell. If args is a sequence,
+-the first item specifies the command string, and any additional items
+-will be treated as additional shell arguments.
+-
+-On Windows: the Popen class uses CreateProcess() to execute the child
+-program, which operates on strings. If args is a sequence, it will be
+-converted to a string using the list2cmdline method. Please note that
+-not all MS Windows applications interpret the command line the same
+-way: The list2cmdline is designed for applications using the same
+-rules as the MS C runtime.
+-
+-bufsize, if given, has the same meaning as the corresponding argument
+-to the built-in open() function: 0 means unbuffered, 1 means line
+-buffered, any other positive value means use a buffer of
+-(approximately) that size. A negative bufsize means to use the system
+-default, which usually means fully buffered. The default value for
+-bufsize is 0 (unbuffered).
+-
+-stdin, stdout and stderr specify the executed programs' standard
+-input, standard output and standard error file handles, respectively.
+-Valid values are PIPE, an existing file descriptor (a positive
+-integer), an existing file object, and None. PIPE indicates that a
+-new pipe to the child should be created. With None, no redirection
+-will occur; the child's file handles will be inherited from the
+-parent. Additionally, stderr can be STDOUT, which indicates that the
+-stderr data from the applications should be captured into the same
+-file handle as for stdout.
+-
+-If preexec_fn is set to a callable object, this object will be called
+-in the child process just before the child is executed.
+-
+-If close_fds is true, all file descriptors except 0, 1 and 2 will be
+-closed before the child process is executed.
+-
+-if shell is true, the specified command will be executed through the
+-shell.
+-
+-If cwd is not None, the current directory will be changed to cwd
+-before the child is executed.
+-
+-If env is not None, it defines the environment variables for the new
+-process.
+-
+-If universal_newlines is true, the file objects stdout and stderr are
+-opened as a text files, but lines may be terminated by any of '\n',
+-the Unix end-of-line convention, '\r', the Macintosh convention or
+-'\r\n', the Windows convention. All of these external representations
+-are seen as '\n' by the Python program. Note: This feature is only
+-available if Python is built with universal newline support (the
+-default). Also, the newlines attribute of the file objects stdout,
+-stdin and stderr are not updated by the communicate() method.
+-
+-The startupinfo and creationflags, if given, will be passed to the
+-underlying CreateProcess() function. They can specify things such as
+-appearance of the main window and priority for the new process.
+-(Windows only)
+-
+-
+-This module also defines two shortcut functions:
+-
+-call(*args, **kwargs):
+- Run command with arguments. Wait for command to complete, then
+- return the returncode attribute. The arguments are the same as for
+- the Popen constructor. Example:
+-
+- retcode = call(["ls", "-l"])
+-
+-
+-Exceptions
+-----------
+-Exceptions raised in the child process, before the new program has
+-started to execute, will be re-raised in the parent. Additionally,
+-the exception object will have one extra attribute called
+-'child_traceback', which is a string containing traceback information
+-from the childs point of view.
+-
+-The most common exception raised is OSError. This occurs, for
+-example, when trying to execute a non-existent file. Applications
+-should prepare for OSErrors.
+-
+-A ValueError will be raised if Popen is called with invalid arguments.
+-
+-
+-Security
+---------
+-Unlike some other popen functions, this implementation will never call
+-/bin/sh implicitly. This means that all characters, including shell
+-metacharacters, can safely be passed to child processes.
+-
+-
+-Popen objects
+-=============
+-Instances of the Popen class have the following methods:
+-
+-poll()
+- Check if child process has terminated. Returns returncode
+- attribute.
+-
+-wait()
+- Wait for child process to terminate. Returns returncode attribute.
+-
+-communicate(input=None)
+- Interact with process: Send data to stdin. Read data from stdout
+- and stderr, until end-of-file is reached. Wait for process to
+- terminate. The optional stdin argument should be a string to be
+- sent to the child process, or None, if no data should be sent to
+- the child.
+-
+- communicate() returns a tuple (stdout, stderr).
+-
+- Note: The data read is buffered in memory, so do not use this
+- method if the data size is large or unlimited.
+-
+-The following attributes are also available:
+-
+-stdin
+- If the stdin argument is PIPE, this attribute is a file object
+- that provides input to the child process. Otherwise, it is None.
+-
+-stdout
+- If the stdout argument is PIPE, this attribute is a file object
+- that provides output from the child process. Otherwise, it is
+- None.
+-
+-stderr
+- If the stderr argument is PIPE, this attribute is file object that
+- provides error output from the child process. Otherwise, it is
+- None.
+-
+-pid
+- The process ID of the child process.
+-
+-returncode
+- The child return code. A None value indicates that the process
+- hasn't terminated yet. A negative value -N indicates that the
+- child was terminated by signal N (UNIX only).
+-
+-
+-Replacing older functions with the subprocess module
+-====================================================
+-In this section, "a ==> b" means that b can be used as a replacement
+-for a.
+-
+-Note: All functions in this section fail (more or less) silently if
+-the executed program cannot be found; this module raises an OSError
+-exception.
+-
+-In the following examples, we assume that the subprocess module is
+-imported with "from subprocess import *".
+-
+-
+-Replacing /bin/sh shell backquote
+----------------------------------
+-output=`mycmd myarg`
+-==>
+-output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0]
+-
+-
+-Replacing shell pipe line
+--------------------------
+-output=`dmesg | grep hda`
+-==>
+-p1 = Popen(["dmesg"], stdout=PIPE)
+-p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
+-output = p2.communicate()[0]
+-
+-
+-Replacing os.system()
+----------------------
+-sts = os.system("mycmd" + " myarg")
+-==>
+-p = Popen("mycmd" + " myarg", shell=True)
+-sts = os.waitpid(p.pid, 0)
+-
+-Note:
+-
+-* Calling the program through the shell is usually not required.
+-
+-* It's easier to look at the returncode attribute than the
+- exitstatus.
+-
+-A more real-world example would look like this:
+-
+-try:
+- retcode = call("mycmd" + " myarg", shell=True)
+- if retcode < 0:
+- print >>sys.stderr, "Child was terminated by signal", -retcode
+- else:
+- print >>sys.stderr, "Child returned", retcode
+-except OSError, e:
+- print >>sys.stderr, "Execution failed:", e
+-
+-
+-Replacing os.spawn*
+--------------------
+-P_NOWAIT example:
+-
+-pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg")
+-==>
+-pid = Popen(["/bin/mycmd", "myarg"]).pid
+-
+-
+-P_WAIT example:
+-
+-retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg")
+-==>
+-retcode = call(["/bin/mycmd", "myarg"])
+-
+-
+-Vector example:
+-
+-os.spawnvp(os.P_NOWAIT, path, args)
+-==>
+-Popen([path] + args[1:])
+-
+-
+-Environment example:
+-
+-os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env)
+-==>
+-Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"})
+-
+-
+-Replacing os.popen*
+--------------------
+-pipe = os.popen(cmd, mode='r', bufsize)
+-==>
+-pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout
+-
+-pipe = os.popen(cmd, mode='w', bufsize)
+-==>
+-pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin
+-
+-
+-(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize)
+-==>
+-p = Popen(cmd, shell=True, bufsize=bufsize,
+- stdin=PIPE, stdout=PIPE, close_fds=True)
+-(child_stdin, child_stdout) = (p.stdin, p.stdout)
+-
+-
+-(child_stdin,
+- child_stdout,
+- child_stderr) = os.popen3(cmd, mode, bufsize)
+-==>
+-p = Popen(cmd, shell=True, bufsize=bufsize,
+- stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
+-(child_stdin,
+- child_stdout,
+- child_stderr) = (p.stdin, p.stdout, p.stderr)
+-
+-
+-(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize)
+-==>
+-p = Popen(cmd, shell=True, bufsize=bufsize,
+- stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
+-(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout)
+-
+-
+-Replacing popen2.*
+-------------------
+-Note: If the cmd argument to popen2 functions is a string, the command
+-is executed through /bin/sh. If it is a list, the command is directly
+-executed.
+-
+-(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode)
+-==>
+-p = Popen(["somestring"], shell=True, bufsize=bufsize
+- stdin=PIPE, stdout=PIPE, close_fds=True)
+-(child_stdout, child_stdin) = (p.stdout, p.stdin)
+-
+-
+-(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode)
+-==>
+-p = Popen(["mycmd", "myarg"], bufsize=bufsize,
+- stdin=PIPE, stdout=PIPE, close_fds=True)
+-(child_stdout, child_stdin) = (p.stdout, p.stdin)
+-
+-The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen,
+-except that:
+-
+-* subprocess.Popen raises an exception if the execution fails
+-* the capturestderr argument is replaced with the stderr argument.
+-* stdin=PIPE and stdout=PIPE must be specified.
+-* popen2 closes all filedescriptors by default, but you have to specify
+- close_fds=True with subprocess.Popen.
+-
+-
+-"""
+-
+-import sys
+-mswindows = (sys.platform == "win32")
+-
+-import os
+-import types
+-import traceback
+-
+-if mswindows:
+- import threading
+- import msvcrt
+- ## @@: Changed in Paste
+- ## Since this module is only used on pre-python-2.4 systems, they probably
+- ## don't have _subprocess installed, but hopefully have the win32 stuff
+- ## installed.
+- if 1: # <-- change this to use pywin32 instead of the _subprocess driver
+- import pywintypes
+- from win32api import GetStdHandle, STD_INPUT_HANDLE, \
+- STD_OUTPUT_HANDLE, STD_ERROR_HANDLE
+- from win32api import GetCurrentProcess, DuplicateHandle, \
+- GetModuleFileName, GetVersion
+- from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE
+- from win32pipe import CreatePipe
+- from win32process import CreateProcess, STARTUPINFO, \
+- GetExitCodeProcess, STARTF_USESTDHANDLES, \
+- STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE
+- from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0
+- else:
+- from _subprocess import *
+- class STARTUPINFO:
+- dwFlags = 0
+- hStdInput = None
+- hStdOutput = None
+- hStdError = None
+- class pywintypes:
+- error = IOError
+-else:
+- import select
+- import errno
+- import fcntl
+- import pickle
+-
+-__all__ = ["Popen", "PIPE", "STDOUT", "call"]
+-
+-try:
+- MAXFD = os.sysconf("SC_OPEN_MAX")
+-except:
+- MAXFD = 256
+-
+-# True/False does not exist on 2.2.0
+-try:
+- False
+-except NameError:
+- False = 0
+- True = 1
+-
+-_active = []
+-
+-def _cleanup():
+- for inst in _active[:]:
+- inst.poll()
+-
+-PIPE = -1
+-STDOUT = -2
+-
+-
+-def call(*args, **kwargs):
+- """Run command with arguments. Wait for command to complete, then
+- return the returncode attribute.
+-
+- The arguments are the same as for the Popen constructor. Example:
+-
+- retcode = call(["ls", "-l"])
+- """
+- return Popen(*args, **kwargs).wait()
+-
+-
+-def list2cmdline(seq):
+- """
+- Translate a sequence of arguments into a command line
+- string, using the same rules as the MS C runtime:
+-
+- 1) Arguments are delimited by white space, which is either a
+- space or a tab.
+-
+- 2) A string surrounded by double quotation marks is
+- interpreted as a single argument, regardless of white space
+- contained within. A quoted string can be embedded in an
+- argument.
+-
+- 3) A double quotation mark preceded by a backslash is
+- interpreted as a literal double quotation mark.
+-
+- 4) Backslashes are interpreted literally, unless they
+- immediately precede a double quotation mark.
+-
+- 5) If backslashes immediately precede a double quotation mark,
+- every pair of backslashes is interpreted as a literal
+- backslash. If the number of backslashes is odd, the last
+- backslash escapes the next double quotation mark as
+- described in rule 3.
+- """
+-
+- # See
+- # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp
+- result = []
+- needquote = False
+- for arg in seq:
+- bs_buf = []
+-
+- # Add a space to separate this argument from the others
+- if result:
+- result.append(' ')
+-
+- needquote = (" " in arg) or ("\t" in arg)
+- if needquote:
+- result.append('"')
+-
+- for c in arg:
+- if c == '\\':
+- # Don't know if we need to double yet.
+- bs_buf.append(c)
+- elif c == '"':
+- # Double backspaces.
+- result.append('\\' * len(bs_buf)*2)
+- bs_buf = []
+- result.append('\\"')
+- else:
+- # Normal char
+- if bs_buf:
+- result.extend(bs_buf)
+- bs_buf = []
+- result.append(c)
+-
+- # Add remaining backspaces, if any.
+- if bs_buf:
+- result.extend(bs_buf)
+-
+- if needquote:
+- result.extend(bs_buf)
+- result.append('"')
+-
+- return ''.join(result)
+-
+-
+-class Popen(object):
+- def __init__(self, args, bufsize=0, executable=None,
+- stdin=None, stdout=None, stderr=None,
+- preexec_fn=None, close_fds=False, shell=False,
+- cwd=None, env=None, universal_newlines=False,
+- startupinfo=None, creationflags=0):
+- """Create new Popen instance."""
+- _cleanup()
+-
+- if not isinstance(bufsize, (int, long)):
+- raise TypeError("bufsize must be an integer")
+-
+- if mswindows:
+- if preexec_fn is not None:
+- raise ValueError("preexec_fn is not supported on Windows "
+- "platforms")
+- if close_fds:
+- raise ValueError("close_fds is not supported on Windows "
+- "platforms")
+- else:
+- # POSIX
+- if startupinfo is not None:
+- raise ValueError("startupinfo is only supported on Windows "
+- "platforms")
+- if creationflags != 0:
+- raise ValueError("creationflags is only supported on Windows "
+- "platforms")
+-
+- self.stdin = None
+- self.stdout = None
+- self.stderr = None
+- self.pid = None
+- self.returncode = None
+- self.universal_newlines = universal_newlines
+-
+- # Input and output objects. The general principle is like
+- # this:
+- #
+- # Parent Child
+- # ------ -----
+- # p2cwrite ---stdin---> p2cread
+- # c2pread <--stdout--- c2pwrite
+- # errread <--stderr--- errwrite
+- #
+- # On POSIX, the child objects are file descriptors. On
+- # Windows, these are Windows file handles. The parent objects
+- # are file descriptors on both platforms. The parent objects
+- # are None when not using PIPEs. The child objects are None
+- # when not redirecting.
+-
+- (p2cread, p2cwrite,
+- c2pread, c2pwrite,
+- errread, errwrite) = self._get_handles(stdin, stdout, stderr)
+-
+- self._execute_child(args, executable, preexec_fn, close_fds,
+- cwd, env, universal_newlines,
+- startupinfo, creationflags, shell,
+- p2cread, p2cwrite,
+- c2pread, c2pwrite,
+- errread, errwrite)
+-
+- if p2cwrite:
+- self.stdin = os.fdopen(p2cwrite, 'wb', bufsize)
+- if c2pread:
+- if universal_newlines:
+- self.stdout = os.fdopen(c2pread, 'rU', bufsize)
+- else:
+- self.stdout = os.fdopen(c2pread, 'rb', bufsize)
+- if errread:
+- if universal_newlines:
+- self.stderr = os.fdopen(errread, 'rU', bufsize)
+- else:
+- self.stderr = os.fdopen(errread, 'rb', bufsize)
+-
+- _active.append(self)
+-
+-
+- def _translate_newlines(self, data):
+- data = data.replace("\r\n", "\n")
+- data = data.replace("\r", "\n")
+- return data
+-
+-
+- if mswindows:
+- #
+- # Windows methods
+- #
+- def _get_handles(self, stdin, stdout, stderr):
+- """Construct and return tupel with IO objects:
+- p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
+- """
+- if stdin == None and stdout == None and stderr == None:
+- return (None, None, None, None, None, None)
+-
+- p2cread, p2cwrite = None, None
+- c2pread, c2pwrite = None, None
+- errread, errwrite = None, None
+-
+- if stdin == None:
+- p2cread = GetStdHandle(STD_INPUT_HANDLE)
+- elif stdin == PIPE:
+- p2cread, p2cwrite = CreatePipe(None, 0)
+- # Detach and turn into fd
+- p2cwrite = p2cwrite.Detach()
+- p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0)
+- elif type(stdin) == types.IntType:
+- p2cread = msvcrt.get_osfhandle(stdin)
+- else:
+- # Assuming file-like object
+- p2cread = msvcrt.get_osfhandle(stdin.fileno())
+- p2cread = self._make_inheritable(p2cread)
+-
+- if stdout == None:
+- c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE)
+- elif stdout == PIPE:
+- c2pread, c2pwrite = CreatePipe(None, 0)
+- # Detach and turn into fd
+- c2pread = c2pread.Detach()
+- c2pread = msvcrt.open_osfhandle(c2pread, 0)
+- elif type(stdout) == types.IntType:
+- c2pwrite = msvcrt.get_osfhandle(stdout)
+- else:
+- # Assuming file-like object
+- c2pwrite = msvcrt.get_osfhandle(stdout.fileno())
+- c2pwrite = self._make_inheritable(c2pwrite)
+-
+- if stderr == None:
+- errwrite = GetStdHandle(STD_ERROR_HANDLE)
+- elif stderr == PIPE:
+- errread, errwrite = CreatePipe(None, 0)
+- # Detach and turn into fd
+- errread = errread.Detach()
+- errread = msvcrt.open_osfhandle(errread, 0)
+- elif stderr == STDOUT:
+- errwrite = c2pwrite
+- elif type(stderr) == types.IntType:
+- errwrite = msvcrt.get_osfhandle(stderr)
+- else:
+- # Assuming file-like object
+- errwrite = msvcrt.get_osfhandle(stderr.fileno())
+- errwrite = self._make_inheritable(errwrite)
+-
+- return (p2cread, p2cwrite,
+- c2pread, c2pwrite,
+- errread, errwrite)
+-
+-
+- def _make_inheritable(self, handle):
+- """Return a duplicate of handle, which is inheritable"""
+- return DuplicateHandle(GetCurrentProcess(), handle,
+- GetCurrentProcess(), 0, 1,
+- DUPLICATE_SAME_ACCESS)
+-
+-
+- def _find_w9xpopen(self):
+- """Find and return absolut path to w9xpopen.exe"""
+- w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)),
+- "w9xpopen.exe")
+- if not os.path.exists(w9xpopen):
+- # Eeek - file-not-found - possibly an embedding
+- # situation - see if we can locate it in sys.exec_prefix
+- w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix),
+- "w9xpopen.exe")
+- if not os.path.exists(w9xpopen):
+- raise RuntimeError("Cannot locate w9xpopen.exe, which is "
+- "needed for Popen to work with your "
+- "shell or platform.")
+- return w9xpopen
+-
+-
+- def _execute_child(self, args, executable, preexec_fn, close_fds,
+- cwd, env, universal_newlines,
+- startupinfo, creationflags, shell,
+- p2cread, p2cwrite,
+- c2pread, c2pwrite,
+- errread, errwrite):
+- """Execute program (MS Windows version)"""
+-
+- if not isinstance(args, types.StringTypes):
+- args = list2cmdline(args)
+-
+- # Process startup details
+- default_startupinfo = STARTUPINFO()
+- if startupinfo == None:
+- startupinfo = default_startupinfo
+- if not None in (p2cread, c2pwrite, errwrite):
+- startupinfo.dwFlags |= STARTF_USESTDHANDLES
+- startupinfo.hStdInput = p2cread
+- startupinfo.hStdOutput = c2pwrite
+- startupinfo.hStdError = errwrite
+-
+- if shell:
+- default_startupinfo.dwFlags |= STARTF_USESHOWWINDOW
+- default_startupinfo.wShowWindow = SW_HIDE
+- comspec = os.environ.get("COMSPEC", "cmd.exe")
+- args = comspec + " /c " + args
+- if (GetVersion() >= 0x80000000L or
+- os.path.basename(comspec).lower() == "command.com"):
+- # Win9x, or using command.com on NT. We need to
+- # use the w9xpopen intermediate program. For more
+- # information, see KB Q150956
+- # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp)
+- w9xpopen = self._find_w9xpopen()
+- args = '"%s" %s' % (w9xpopen, args)
+- # Not passing CREATE_NEW_CONSOLE has been known to
+- # cause random failures on win9x. Specifically a
+- # dialog: "Your program accessed mem currently in
+- # use at xxx" and a hopeful warning about the
+- # stability of your system. Cost is Ctrl+C wont
+- # kill children.
+- creationflags |= CREATE_NEW_CONSOLE
+-
+- # Start the process
+- try:
+- hp, ht, pid, tid = CreateProcess(executable, args,
+- # no special security
+- None, None,
+- # must inherit handles to pass std
+- # handles
+- 1,
+- creationflags,
+- env,
+- cwd,
+- startupinfo)
+- except pywintypes.error, e:
+- # Translate pywintypes.error to WindowsError, which is
+- # a subclass of OSError. FIXME: We should really
+- # translate errno using _sys_errlist (or simliar), but
+- # how can this be done from Python?
+- raise WindowsError(*e.args)
+-
+- # Retain the process handle, but close the thread handle
+- self._handle = hp
+- self.pid = pid
+- ht.Close()
+-
+- # Child is launched. Close the parent's copy of those pipe
+- # handles that only the child should have open. You need
+- # to make sure that no handles to the write end of the
+- # output pipe are maintained in this process or else the
+- # pipe will not close when the child process exits and the
+- # ReadFile will hang.
+- if p2cread != None:
+- p2cread.Close()
+- if c2pwrite != None:
+- c2pwrite.Close()
+- if errwrite != None:
+- errwrite.Close()
+-
+-
+- def poll(self):
+- """Check if child process has terminated. Returns returncode
+- attribute."""
+- if self.returncode == None:
+- if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0:
+- self.returncode = GetExitCodeProcess(self._handle)
+- _active.remove(self)
+- return self.returncode
+-
+-
+- def wait(self):
+- """Wait for child process to terminate. Returns returncode
+- attribute."""
+- if self.returncode == None:
+- obj = WaitForSingleObject(self._handle, INFINITE)
+- self.returncode = GetExitCodeProcess(self._handle)
+- _active.remove(self)
+- return self.returncode
+-
+-
+- def _readerthread(self, fh, buffer):
+- buffer.append(fh.read())
+-
+-
+- def communicate(self, input=None):
+- """Interact with process: Send data to stdin. Read data from
+- stdout and stderr, until end-of-file is reached. Wait for
+- process to terminate. The optional input argument should be a
+- string to be sent to the child process, or None, if no data
+- should be sent to the child.
+-
+- communicate() returns a tuple (stdout, stderr)."""
+- stdout = None # Return
+- stderr = None # Return
+-
+- if self.stdout:
+- stdout = []
+- stdout_thread = threading.Thread(target=self._readerthread,
+- args=(self.stdout, stdout))
+- stdout_thread.setDaemon(True)
+- stdout_thread.start()
+- if self.stderr:
+- stderr = []
+- stderr_thread = threading.Thread(target=self._readerthread,
+- args=(self.stderr, stderr))
+- stderr_thread.setDaemon(True)
+- stderr_thread.start()
+-
+- if self.stdin:
+- if input != None:
+- self.stdin.write(input)
+- self.stdin.close()
+-
+- if self.stdout:
+- stdout_thread.join()
+- if self.stderr:
+- stderr_thread.join()
+-
+- # All data exchanged. Translate lists into strings.
+- if stdout != None:
+- stdout = stdout[0]
+- if stderr != None:
+- stderr = stderr[0]
+-
+- # Translate newlines, if requested. We cannot let the file
+- # object do the translation: It is based on stdio, which is
+- # impossible to combine with select (unless forcing no
+- # buffering).
+- if self.universal_newlines and hasattr(open, 'newlines'):
+- if stdout:
+- stdout = self._translate_newlines(stdout)
+- if stderr:
+- stderr = self._translate_newlines(stderr)
+-
+- self.wait()
+- return (stdout, stderr)
+-
+- else:
+- #
+- # POSIX methods
+- #
+- def _get_handles(self, stdin, stdout, stderr):
+- """Construct and return tupel with IO objects:
+- p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
+- """
+- p2cread, p2cwrite = None, None
+- c2pread, c2pwrite = None, None
+- errread, errwrite = None, None
+-
+- if stdin == None:
+- pass
+- elif stdin == PIPE:
+- p2cread, p2cwrite = os.pipe()
+- elif type(stdin) == types.IntType:
+- p2cread = stdin
+- else:
+- # Assuming file-like object
+- p2cread = stdin.fileno()
+-
+- if stdout == None:
+- pass
+- elif stdout == PIPE:
+- c2pread, c2pwrite = os.pipe()
+- elif type(stdout) == types.IntType:
+- c2pwrite = stdout
+- else:
+- # Assuming file-like object
+- c2pwrite = stdout.fileno()
+-
+- if stderr == None:
+- pass
+- elif stderr == PIPE:
+- errread, errwrite = os.pipe()
+- elif stderr == STDOUT:
+- errwrite = c2pwrite
+- elif type(stderr) == types.IntType:
+- errwrite = stderr
+- else:
+- # Assuming file-like object
+- errwrite = stderr.fileno()
+-
+- return (p2cread, p2cwrite,
+- c2pread, c2pwrite,
+- errread, errwrite)
+-
+-
+- def _set_cloexec_flag(self, fd):
+- try:
+- cloexec_flag = fcntl.FD_CLOEXEC
+- except AttributeError:
+- cloexec_flag = 1
+-
+- old = fcntl.fcntl(fd, fcntl.F_GETFD)
+- fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag)
+-
+-
+- def _close_fds(self, but):
+- for i in range(3, MAXFD):
+- if i == but:
+- continue
+- try:
+- os.close(i)
+- except:
+- pass
+-
+-
+- def _execute_child(self, args, executable, preexec_fn, close_fds,
+- cwd, env, universal_newlines,
+- startupinfo, creationflags, shell,
+- p2cread, p2cwrite,
+- c2pread, c2pwrite,
+- errread, errwrite):
+- """Execute program (POSIX version)"""
+-
+- if isinstance(args, types.StringTypes):
+- args = [args]
+-
+- if shell:
+- args = ["/bin/sh", "-c"] + args
+-
+- if executable == None:
+- executable = args[0]
+-
+- # For transferring possible exec failure from child to parent
+- # The first char specifies the exception type: 0 means
+- # OSError, 1 means some other error.
+- errpipe_read, errpipe_write = os.pipe()
+- self._set_cloexec_flag(errpipe_write)
+-
+- self.pid = os.fork()
+- if self.pid == 0:
+- # Child
+- try:
+- # Close parent's pipe ends
+- if p2cwrite:
+- os.close(p2cwrite)
+- if c2pread:
+- os.close(c2pread)
+- if errread:
+- os.close(errread)
+- os.close(errpipe_read)
+-
+- # Dup fds for child
+- if p2cread:
+- os.dup2(p2cread, 0)
+- if c2pwrite:
+- os.dup2(c2pwrite, 1)
+- if errwrite:
+- os.dup2(errwrite, 2)
+-
+- # Close pipe fds. Make sure we doesn't close the same
+- # fd more than once.
+- if p2cread:
+- os.close(p2cread)
+- if c2pwrite and c2pwrite not in (p2cread,):
+- os.close(c2pwrite)
+- if errwrite and errwrite not in (p2cread, c2pwrite):
+- os.close(errwrite)
+-
+- # Close all other fds, if asked for
+- if close_fds:
+- self._close_fds(but=errpipe_write)
+-
+- if cwd != None:
+- os.chdir(cwd)
+-
+- if preexec_fn:
+- apply(preexec_fn)
+-
+- if env == None:
+- os.execvp(executable, args)
+- else:
+- os.execvpe(executable, args, env)
+-
+- except:
+- exc_type, exc_value, tb = sys.exc_info()
+- # Save the traceback and attach it to the exception object
+- exc_lines = traceback.format_exception(exc_type,
+- exc_value,
+- tb)
+- exc_value.child_traceback = ''.join(exc_lines)
+- os.write(errpipe_write, pickle.dumps(exc_value))
+-
+- # This exitcode won't be reported to applications, so it
+- # really doesn't matter what we return.
+- os._exit(255)
+-
+- # Parent
+- os.close(errpipe_write)
+- if p2cread and p2cwrite:
+- os.close(p2cread)
+- if c2pwrite and c2pread:
+- os.close(c2pwrite)
+- if errwrite and errread:
+- os.close(errwrite)
+-
+- # Wait for exec to fail or succeed; possibly raising exception
+- data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB
+- os.close(errpipe_read)
+- if data != "":
+- os.waitpid(self.pid, 0)
+- child_exception = pickle.loads(data)
+- raise child_exception
+-
+-
+- def _handle_exitstatus(self, sts):
+- if os.WIFSIGNALED(sts):
+- self.returncode = -os.WTERMSIG(sts)
+- elif os.WIFEXITED(sts):
+- self.returncode = os.WEXITSTATUS(sts)
+- else:
+- # Should never happen
+- raise RuntimeError("Unknown child exit status!")
+-
+- _active.remove(self)
+-
+-
+- def poll(self):
+- """Check if child process has terminated. Returns returncode
+- attribute."""
+- if self.returncode == None:
+- try:
+- pid, sts = os.waitpid(self.pid, os.WNOHANG)
+- if pid == self.pid:
+- self._handle_exitstatus(sts)
+- except os.error:
+- pass
+- return self.returncode
+-
+-
+- def wait(self):
+- """Wait for child process to terminate. Returns returncode
+- attribute."""
+- if self.returncode == None:
+- pid, sts = os.waitpid(self.pid, 0)
+- self._handle_exitstatus(sts)
+- return self.returncode
+-
+-
+- def communicate(self, input=None):
+- """Interact with process: Send data to stdin. Read data from
+- stdout and stderr, until end-of-file is reached. Wait for
+- process to terminate. The optional input argument should be a
+- string to be sent to the child process, or None, if no data
+- should be sent to the child.
+-
+- communicate() returns a tuple (stdout, stderr)."""
+- read_set = []
+- write_set = []
+- stdout = None # Return
+- stderr = None # Return
+-
+- if self.stdin:
+- # Flush stdio buffer. This might block, if the user has
+- # been writing to .stdin in an uncontrolled fashion.
+- self.stdin.flush()
+- if input:
+- write_set.append(self.stdin)
+- else:
+- self.stdin.close()
+- if self.stdout:
+- read_set.append(self.stdout)
+- stdout = []
+- if self.stderr:
+- read_set.append(self.stderr)
+- stderr = []
+-
+- while read_set or write_set:
+- rlist, wlist, xlist = select.select(read_set, write_set, [])
+-
+- if self.stdin in wlist:
+- # When select has indicated that the file is writable,
+- # we can write up to PIPE_BUF bytes without risk
+- # blocking. POSIX defines PIPE_BUF >= 512
+- bytes_written = os.write(self.stdin.fileno(), input[:512])
+- input = input[bytes_written:]
+- if not input:
+- self.stdin.close()
+- write_set.remove(self.stdin)
+-
+- if self.stdout in rlist:
+- data = os.read(self.stdout.fileno(), 1024)
+- if data == "":
+- self.stdout.close()
+- read_set.remove(self.stdout)
+- stdout.append(data)
+-
+- if self.stderr in rlist:
+- data = os.read(self.stderr.fileno(), 1024)
+- if data == "":
+- self.stderr.close()
+- read_set.remove(self.stderr)
+- stderr.append(data)
+-
+- # All data exchanged. Translate lists into strings.
+- if stdout != None:
+- stdout = ''.join(stdout)
+- if stderr != None:
+- stderr = ''.join(stderr)
+-
+- # Translate newlines, if requested. We cannot let the file
+- # object do the translation: It is based on stdio, which is
+- # impossible to combine with select (unless forcing no
+- # buffering).
+- if self.universal_newlines and hasattr(open, 'newlines'):
+- if stdout:
+- stdout = self._translate_newlines(stdout)
+- if stderr:
+- stderr = self._translate_newlines(stderr)
+-
+- self.wait()
+- return (stdout, stderr)
+-
+-
+-def _demo_posix():
+- #
+- # Example 1: Simple redirection: Get process list
+- #
+- plist = Popen(["ps"], stdout=PIPE).communicate()[0]
+- print "Process list:"
+- print plist
+-
+- #
+- # Example 2: Change uid before executing child
+- #
+- if os.getuid() == 0:
+- p = Popen(["id"], preexec_fn=lambda: os.setuid(100))
+- p.wait()
+-
+- #
+- # Example 3: Connecting several subprocesses
+- #
+- print "Looking for 'hda'..."
+- p1 = Popen(["dmesg"], stdout=PIPE)
+- p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
+- print repr(p2.communicate()[0])
+-
+- #
+- # Example 4: Catch execution error
+- #
+- print
+- print "Trying a weird file..."
+- try:
+- print Popen(["/this/path/does/not/exist"]).communicate()
+- except OSError, e:
+- if e.errno == errno.ENOENT:
+- print "The file didn't exist. I thought so..."
+- print "Child traceback:"
+- print e.child_traceback
+- else:
+- print "Error", e.errno
+- else:
+- print >>sys.stderr, "Gosh. No error."
+-
+-
+-def _demo_windows():
+- #
+- # Example 1: Connecting several subprocesses
+- #
+- print "Looking for 'PROMPT' in set output..."
+- p1 = Popen("set", stdout=PIPE, shell=True)
+- p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE)
+- print repr(p2.communicate()[0])
+-
+- #
+- # Example 2: Simple execution of program
+- #
+- print "Executing calc..."
+- p = Popen("calc")
+- p.wait()
+-
+-
+-if __name__ == "__main__":
+- if mswindows:
+- _demo_windows()
+- else:
+- _demo_posix()
diff --git a/dev-python/paste/files/paste-1.7.5.1-unbundle-tempita.patch b/dev-python/paste/files/paste-1.7.5.1-unbundle-tempita.patch
new file mode 100644
index 000000000000..689973fd88a8
--- /dev/null
+++ b/dev-python/paste/files/paste-1.7.5.1-unbundle-tempita.patch
@@ -0,0 +1,1860 @@
+Index: Paste-1.7.4/paste/util/looper/__init__.py
+===================================================================
+--- /dev/null
++++ Paste-1.7.4/paste/util/looper/__init__.py
+@@ -0,0 +1,4 @@
++try:
++ from tempita._looper import *
++except ImportError:
++ from _looper import *
+Index: Paste-1.7.4/paste/util/looper/_looper.py
+===================================================================
+--- /dev/null
++++ Paste-1.7.4/paste/util/looper/_looper.py
+@@ -0,0 +1,152 @@
++"""
++Helper for looping over sequences, particular in templates.
++
++Often in a loop in a template it's handy to know what's next up,
++previously up, if this is the first or last item in the sequence, etc.
++These can be awkward to manage in a normal Python loop, but using the
++looper you can get a better sense of the context. Use like::
++
++ >>> for loop, item in looper(['a', 'b', 'c']):
++ ... print loop.number, item
++ ... if not loop.last:
++ ... print '---'
++ 1 a
++ ---
++ 2 b
++ ---
++ 3 c
++
++"""
++
++__all__ = ['looper']
++
++class looper(object):
++ """
++ Helper for looping (particularly in templates)
++
++ Use this like::
++
++ for loop, item in looper(seq):
++ if loop.first:
++ ...
++ """
++
++ def __init__(self, seq):
++ self.seq = seq
++
++ def __iter__(self):
++ return looper_iter(self.seq)
++
++ def __repr__(self):
++ return '<%s for %r>' % (
++ self.__class__.__name__, self.seq)
++
++class looper_iter(object):
++
++ def __init__(self, seq):
++ self.seq = list(seq)
++ self.pos = 0
++
++ def __iter__(self):
++ return self
++
++ def next(self):
++ if self.pos >= len(self.seq):
++ raise StopIteration
++ result = loop_pos(self.seq, self.pos), self.seq[self.pos]
++ self.pos += 1
++ return result
++
++class loop_pos(object):
++
++ def __init__(self, seq, pos):
++ self.seq = seq
++ self.pos = pos
++
++ def __repr__(self):
++ return '<loop pos=%r at %r>' % (
++ self.seq[pos], pos)
++
++ def index(self):
++ return self.pos
++ index = property(index)
++
++ def number(self):
++ return self.pos + 1
++ number = property(number)
++
++ def item(self):
++ return self.seq[self.pos]
++ item = property(item)
++
++ def next(self):
++ try:
++ return self.seq[self.pos+1]
++ except IndexError:
++ return None
++ next = property(next)
++
++ def previous(self):
++ if self.pos == 0:
++ return None
++ return self.seq[self.pos-1]
++ previous = property(previous)
++
++ def odd(self):
++ return not self.pos % 2
++ odd = property(odd)
++
++ def even(self):
++ return self.pos % 2
++ even = property(even)
++
++ def first(self):
++ return self.pos == 0
++ first = property(first)
++
++ def last(self):
++ return self.pos == len(self.seq)-1
++ last = property(last)
++
++ def length(self):
++ return len(self.seq)
++ length = property(length)
++
++ def first_group(self, getter=None):
++ """
++ Returns true if this item is the start of a new group,
++ where groups mean that some attribute has changed. The getter
++ can be None (the item itself changes), an attribute name like
++ ``'.attr'``, a function, or a dict key or list index.
++ """
++ if self.first:
++ return True
++ return self._compare_group(self.item, self.previous, getter)
++
++ def last_group(self, getter=None):
++ """
++ Returns true if this item is the end of a new group,
++ where groups mean that some attribute has changed. The getter
++ can be None (the item itself changes), an attribute name like
++ ``'.attr'``, a function, or a dict key or list index.
++ """
++ if self.last:
++ return True
++ return self._compare_group(self.item, self.next, getter)
++
++ def _compare_group(self, item, other, getter):
++ if getter is None:
++ return item != other
++ elif (isinstance(getter, basestring)
++ and getter.startswith('.')):
++ getter = getter[1:]
++ if getter.endswith('()'):
++ getter = getter[:-2]
++ return getattr(item, getter)() != getattr(other, getter)()
++ else:
++ return getattr(item, getter) != getattr(other, getter)
++ elif callable(getter):
++ return getter(item) != getter(other)
++ else:
++ return item[getter] != other[getter]
++
+Index: Paste-1.7.4/paste/util/looper.py
+===================================================================
+--- Paste-1.7.4.orig/paste/util/looper.py
++++ /dev/null
+@@ -1,152 +0,0 @@
+-"""
+-Helper for looping over sequences, particular in templates.
+-
+-Often in a loop in a template it's handy to know what's next up,
+-previously up, if this is the first or last item in the sequence, etc.
+-These can be awkward to manage in a normal Python loop, but using the
+-looper you can get a better sense of the context. Use like::
+-
+- >>> for loop, item in looper(['a', 'b', 'c']):
+- ... print loop.number, item
+- ... if not loop.last:
+- ... print '---'
+- 1 a
+- ---
+- 2 b
+- ---
+- 3 c
+-
+-"""
+-
+-__all__ = ['looper']
+-
+-class looper(object):
+- """
+- Helper for looping (particularly in templates)
+-
+- Use this like::
+-
+- for loop, item in looper(seq):
+- if loop.first:
+- ...
+- """
+-
+- def __init__(self, seq):
+- self.seq = seq
+-
+- def __iter__(self):
+- return looper_iter(self.seq)
+-
+- def __repr__(self):
+- return '<%s for %r>' % (
+- self.__class__.__name__, self.seq)
+-
+-class looper_iter(object):
+-
+- def __init__(self, seq):
+- self.seq = list(seq)
+- self.pos = 0
+-
+- def __iter__(self):
+- return self
+-
+- def next(self):
+- if self.pos >= len(self.seq):
+- raise StopIteration
+- result = loop_pos(self.seq, self.pos), self.seq[self.pos]
+- self.pos += 1
+- return result
+-
+-class loop_pos(object):
+-
+- def __init__(self, seq, pos):
+- self.seq = seq
+- self.pos = pos
+-
+- def __repr__(self):
+- return '<loop pos=%r at %r>' % (
+- self.seq[pos], pos)
+-
+- def index(self):
+- return self.pos
+- index = property(index)
+-
+- def number(self):
+- return self.pos + 1
+- number = property(number)
+-
+- def item(self):
+- return self.seq[self.pos]
+- item = property(item)
+-
+- def next(self):
+- try:
+- return self.seq[self.pos+1]
+- except IndexError:
+- return None
+- next = property(next)
+-
+- def previous(self):
+- if self.pos == 0:
+- return None
+- return self.seq[self.pos-1]
+- previous = property(previous)
+-
+- def odd(self):
+- return not self.pos % 2
+- odd = property(odd)
+-
+- def even(self):
+- return self.pos % 2
+- even = property(even)
+-
+- def first(self):
+- return self.pos == 0
+- first = property(first)
+-
+- def last(self):
+- return self.pos == len(self.seq)-1
+- last = property(last)
+-
+- def length(self):
+- return len(self.seq)
+- length = property(length)
+-
+- def first_group(self, getter=None):
+- """
+- Returns true if this item is the start of a new group,
+- where groups mean that some attribute has changed. The getter
+- can be None (the item itself changes), an attribute name like
+- ``'.attr'``, a function, or a dict key or list index.
+- """
+- if self.first:
+- return True
+- return self._compare_group(self.item, self.previous, getter)
+-
+- def last_group(self, getter=None):
+- """
+- Returns true if this item is the end of a new group,
+- where groups mean that some attribute has changed. The getter
+- can be None (the item itself changes), an attribute name like
+- ``'.attr'``, a function, or a dict key or list index.
+- """
+- if self.last:
+- return True
+- return self._compare_group(self.item, self.next, getter)
+-
+- def _compare_group(self, item, other, getter):
+- if getter is None:
+- return item != other
+- elif (isinstance(getter, basestring)
+- and getter.startswith('.')):
+- getter = getter[1:]
+- if getter.endswith('()'):
+- getter = getter[:-2]
+- return getattr(item, getter)() != getattr(other, getter)()
+- else:
+- return getattr(item, getter) != getattr(other, getter)
+- elif callable(getter):
+- return getter(item) != getter(other)
+- else:
+- return item[getter] != other[getter]
+-
+Index: Paste-1.7.4/paste/util/template/__init__.py
+===================================================================
+--- /dev/null
++++ Paste-1.7.4/paste/util/template/__init__.py
+@@ -0,0 +1,6 @@
++try:
++ from tempita import *
++ from tempita import paste_script_template_renderer
++except ImportError:
++ from _template import *
++ from _template import paste_script_template_renderer
+Index: Paste-1.7.4/paste/util/template/_template.py
+===================================================================
+--- /dev/null
++++ Paste-1.7.4/paste/util/template/_template.py
+@@ -0,0 +1,758 @@
++"""
++A small templating language
++
++This implements a small templating language for use internally in
++Paste and Paste Script. This language implements if/elif/else,
++for/continue/break, expressions, and blocks of Python code. The
++syntax is::
++
++ {{any expression (function calls etc)}}
++ {{any expression | filter}}
++ {{for x in y}}...{{endfor}}
++ {{if x}}x{{elif y}}y{{else}}z{{endif}}
++ {{py:x=1}}
++ {{py:
++ def foo(bar):
++ return 'baz'
++ }}
++ {{default var = default_value}}
++ {{# comment}}
++
++You use this with the ``Template`` class or the ``sub`` shortcut.
++The ``Template`` class takes the template string and the name of
++the template (for errors) and a default namespace. Then (like
++``string.Template``) you can call the ``tmpl.substitute(**kw)``
++method to make a substitution (or ``tmpl.substitute(a_dict)``).
++
++``sub(content, **kw)`` substitutes the template immediately. You
++can use ``__name='tmpl.html'`` to set the name of the template.
++
++If there are syntax errors ``TemplateError`` will be raised.
++"""
++
++import re
++import sys
++import cgi
++import urllib
++from paste.util.looper import looper
++
++__all__ = ['TemplateError', 'Template', 'sub', 'HTMLTemplate',
++ 'sub_html', 'html', 'bunch']
++
++token_re = re.compile(r'\{\{|\}\}')
++in_re = re.compile(r'\s+in\s+')
++var_re = re.compile(r'^[a-z_][a-z0-9_]*$', re.I)
++
++class TemplateError(Exception):
++ """Exception raised while parsing a template
++ """
++
++ def __init__(self, message, position, name=None):
++ self.message = message
++ self.position = position
++ self.name = name
++
++ def __str__(self):
++ msg = '%s at line %s column %s' % (
++ self.message, self.position[0], self.position[1])
++ if self.name:
++ msg += ' in %s' % self.name
++ return msg
++
++class _TemplateContinue(Exception):
++ pass
++
++class _TemplateBreak(Exception):
++ pass
++
++class Template(object):
++
++ default_namespace = {
++ 'start_braces': '{{',
++ 'end_braces': '}}',
++ 'looper': looper,
++ }
++
++ default_encoding = 'utf8'
++
++ def __init__(self, content, name=None, namespace=None):
++ self.content = content
++ self._unicode = isinstance(content, unicode)
++ self.name = name
++ self._parsed = parse(content, name=name)
++ if namespace is None:
++ namespace = {}
++ self.namespace = namespace
++
++ def from_filename(cls, filename, namespace=None, encoding=None):
++ f = open(filename, 'rb')
++ c = f.read()
++ f.close()
++ if encoding:
++ c = c.decode(encoding)
++ return cls(content=c, name=filename, namespace=namespace)
++
++ from_filename = classmethod(from_filename)
++
++ def __repr__(self):
++ return '<%s %s name=%r>' % (
++ self.__class__.__name__,
++ hex(id(self))[2:], self.name)
++
++ def substitute(self, *args, **kw):
++ if args:
++ if kw:
++ raise TypeError(
++ "You can only give positional *or* keyword arguments")
++ if len(args) > 1:
++ raise TypeError(
++ "You can only give on positional argument")
++ kw = args[0]
++ ns = self.default_namespace.copy()
++ ns.update(self.namespace)
++ ns.update(kw)
++ result = self._interpret(ns)
++ return result
++
++ def _interpret(self, ns):
++ __traceback_hide__ = True
++ parts = []
++ self._interpret_codes(self._parsed, ns, out=parts)
++ return ''.join(parts)
++
++ def _interpret_codes(self, codes, ns, out):
++ __traceback_hide__ = True
++ for item in codes:
++ if isinstance(item, basestring):
++ out.append(item)
++ else:
++ self._interpret_code(item, ns, out)
++
++ def _interpret_code(self, code, ns, out):
++ __traceback_hide__ = True
++ name, pos = code[0], code[1]
++ if name == 'py':
++ self._exec(code[2], ns, pos)
++ elif name == 'continue':
++ raise _TemplateContinue()
++ elif name == 'break':
++ raise _TemplateBreak()
++ elif name == 'for':
++ vars, expr, content = code[2], code[3], code[4]
++ expr = self._eval(expr, ns, pos)
++ self._interpret_for(vars, expr, content, ns, out)
++ elif name == 'cond':
++ parts = code[2:]
++ self._interpret_if(parts, ns, out)
++ elif name == 'expr':
++ parts = code[2].split('|')
++ base = self._eval(parts[0], ns, pos)
++ for part in parts[1:]:
++ func = self._eval(part, ns, pos)
++ base = func(base)
++ out.append(self._repr(base, pos))
++ elif name == 'default':
++ var, expr = code[2], code[3]
++ if var not in ns:
++ result = self._eval(expr, ns, pos)
++ ns[var] = result
++ elif name == 'comment':
++ return
++ else:
++ assert 0, "Unknown code: %r" % name
++
++ def _interpret_for(self, vars, expr, content, ns, out):
++ __traceback_hide__ = True
++ for item in expr:
++ if len(vars) == 1:
++ ns[vars[0]] = item
++ else:
++ if len(vars) != len(item):
++ raise ValueError(
++ 'Need %i items to unpack (got %i items)'
++ % (len(vars), len(item)))
++ for name, value in zip(vars, item):
++ ns[name] = value
++ try:
++ self._interpret_codes(content, ns, out)
++ except _TemplateContinue:
++ continue
++ except _TemplateBreak:
++ break
++
++ def _interpret_if(self, parts, ns, out):
++ __traceback_hide__ = True
++ # @@: if/else/else gets through
++ for part in parts:
++ assert not isinstance(part, basestring)
++ name, pos = part[0], part[1]
++ if name == 'else':
++ result = True
++ else:
++ result = self._eval(part[2], ns, pos)
++ if result:
++ self._interpret_codes(part[3], ns, out)
++ break
++
++ def _eval(self, code, ns, pos):
++ __traceback_hide__ = True
++ try:
++ value = eval(code, ns)
++ return value
++ except:
++ exc_info = sys.exc_info()
++ e = exc_info[1]
++ if getattr(e, 'args'):
++ arg0 = e.args[0]
++ else:
++ arg0 = str(e)
++ e.args = (self._add_line_info(arg0, pos),)
++ raise exc_info[0], e, exc_info[2]
++
++ def _exec(self, code, ns, pos):
++ __traceback_hide__ = True
++ try:
++ exec code in ns
++ except:
++ exc_info = sys.exc_info()
++ e = exc_info[1]
++ e.args = (self._add_line_info(e.args[0], pos),)
++ raise exc_info[0], e, exc_info[2]
++
++ def _repr(self, value, pos):
++ __traceback_hide__ = True
++ try:
++ if value is None:
++ return ''
++ if self._unicode:
++ try:
++ value = unicode(value)
++ except UnicodeDecodeError:
++ value = str(value)
++ else:
++ value = str(value)
++ except:
++ exc_info = sys.exc_info()
++ e = exc_info[1]
++ e.args = (self._add_line_info(e.args[0], pos),)
++ raise exc_info[0], e, exc_info[2]
++ else:
++ if self._unicode and isinstance(value, str):
++ if not self.decode_encoding:
++ raise UnicodeDecodeError(
++ 'Cannot decode str value %r into unicode '
++ '(no default_encoding provided)' % value)
++ value = value.decode(self.default_encoding)
++ elif not self._unicode and isinstance(value, unicode):
++ if not self.decode_encoding:
++ raise UnicodeEncodeError(
++ 'Cannot encode unicode value %r into str '
++ '(no default_encoding provided)' % value)
++ value = value.encode(self.default_encoding)
++ return value
++
++
++ def _add_line_info(self, msg, pos):
++ msg = "%s at line %s column %s" % (
++ msg, pos[0], pos[1])
++ if self.name:
++ msg += " in file %s" % self.name
++ return msg
++
++def sub(content, **kw):
++ name = kw.get('__name')
++ tmpl = Template(content, name=name)
++ return tmpl.substitute(kw)
++ return result
++
++def paste_script_template_renderer(content, vars, filename=None):
++ tmpl = Template(content, name=filename)
++ return tmpl.substitute(vars)
++
++class bunch(dict):
++
++ def __init__(self, **kw):
++ for name, value in kw.items():
++ setattr(self, name, value)
++
++ def __setattr__(self, name, value):
++ self[name] = value
++
++ def __getattr__(self, name):
++ try:
++ return self[name]
++ except KeyError:
++ raise AttributeError(name)
++
++ def __getitem__(self, key):
++ if 'default' in self:
++ try:
++ return dict.__getitem__(self, key)
++ except KeyError:
++ return dict.__getitem__(self, 'default')
++ else:
++ return dict.__getitem__(self, key)
++
++ def __repr__(self):
++ items = [
++ (k, v) for k, v in self.items()]
++ items.sort()
++ return '<%s %s>' % (
++ self.__class__.__name__,
++ ' '.join(['%s=%r' % (k, v) for k, v in items]))
++
++############################################################
++## HTML Templating
++############################################################
++
++class html(object):
++ def __init__(self, value):
++ self.value = value
++ def __str__(self):
++ return self.value
++ def __repr__(self):
++ return '<%s %r>' % (
++ self.__class__.__name__, self.value)
++
++def html_quote(value):
++ if value is None:
++ return ''
++ if not isinstance(value, basestring):
++ if hasattr(value, '__unicode__'):
++ value = unicode(value)
++ else:
++ value = str(value)
++ value = cgi.escape(value, 1)
++ if isinstance(value, unicode):
++ value = value.encode('ascii', 'xmlcharrefreplace')
++ return value
++
++def url(v):
++ if not isinstance(v, basestring):
++ if hasattr(v, '__unicode__'):
++ v = unicode(v)
++ else:
++ v = str(v)
++ if isinstance(v, unicode):
++ v = v.encode('utf8')
++ return urllib.quote(v)
++
++def attr(**kw):
++ kw = kw.items()
++ kw.sort()
++ parts = []
++ for name, value in kw:
++ if value is None:
++ continue
++ if name.endswith('_'):
++ name = name[:-1]
++ parts.append('%s="%s"' % (html_quote(name), html_quote(value)))
++ return html(' '.join(parts))
++
++class HTMLTemplate(Template):
++
++ default_namespace = Template.default_namespace.copy()
++ default_namespace.update(dict(
++ html=html,
++ attr=attr,
++ url=url,
++ ))
++
++ def _repr(self, value, pos):
++ plain = Template._repr(self, value, pos)
++ if isinstance(value, html):
++ return plain
++ else:
++ return html_quote(plain)
++
++def sub_html(content, **kw):
++ name = kw.get('__name')
++ tmpl = HTMLTemplate(content, name=name)
++ return tmpl.substitute(kw)
++ return result
++
++
++############################################################
++## Lexing and Parsing
++############################################################
++
++def lex(s, name=None, trim_whitespace=True):
++ """
++ Lex a string into chunks:
++
++ >>> lex('hey')
++ ['hey']
++ >>> lex('hey {{you}}')
++ ['hey ', ('you', (1, 7))]
++ >>> lex('hey {{')
++ Traceback (most recent call last):
++ ...
++ TemplateError: No }} to finish last expression at line 1 column 7
++ >>> lex('hey }}')
++ Traceback (most recent call last):
++ ...
++ TemplateError: }} outside expression at line 1 column 7
++ >>> lex('hey {{ {{')
++ Traceback (most recent call last):
++ ...
++ TemplateError: {{ inside expression at line 1 column 10
++
++ """
++ in_expr = False
++ chunks = []
++ last = 0
++ last_pos = (1, 1)
++ for match in token_re.finditer(s):
++ expr = match.group(0)
++ pos = find_position(s, match.end())
++ if expr == '{{' and in_expr:
++ raise TemplateError('{{ inside expression', position=pos,
++ name=name)
++ elif expr == '}}' and not in_expr:
++ raise TemplateError('}} outside expression', position=pos,
++ name=name)
++ if expr == '{{':
++ part = s[last:match.start()]
++ if part:
++ chunks.append(part)
++ in_expr = True
++ else:
++ chunks.append((s[last:match.start()], last_pos))
++ in_expr = False
++ last = match.end()
++ last_pos = pos
++ if in_expr:
++ raise TemplateError('No }} to finish last expression',
++ name=name, position=last_pos)
++ part = s[last:]
++ if part:
++ chunks.append(part)
++ if trim_whitespace:
++ chunks = trim_lex(chunks)
++ return chunks
++
++statement_re = re.compile(r'^(?:if |elif |else |for |py:)')
++single_statements = ['endif', 'endfor', 'continue', 'break']
++trail_whitespace_re = re.compile(r'\n[\t ]*$')
++lead_whitespace_re = re.compile(r'^[\t ]*\n')
++
++def trim_lex(tokens):
++ r"""
++ Takes a lexed set of tokens, and removes whitespace when there is
++ a directive on a line by itself:
++
++ >>> tokens = lex('{{if x}}\nx\n{{endif}}\ny', trim_whitespace=False)
++ >>> tokens
++ [('if x', (1, 3)), '\nx\n', ('endif', (3, 3)), '\ny']
++ >>> trim_lex(tokens)
++ [('if x', (1, 3)), 'x\n', ('endif', (3, 3)), 'y']
++ """
++ for i in range(len(tokens)):
++ current = tokens[i]
++ if isinstance(tokens[i], basestring):
++ # we don't trim this
++ continue
++ item = current[0]
++ if not statement_re.search(item) and item not in single_statements:
++ continue
++ if not i:
++ prev = ''
++ else:
++ prev = tokens[i-1]
++ if i+1 >= len(tokens):
++ next = ''
++ else:
++ next = tokens[i+1]
++ if (not isinstance(next, basestring)
++ or not isinstance(prev, basestring)):
++ continue
++ if ((not prev or trail_whitespace_re.search(prev))
++ and (not next or lead_whitespace_re.search(next))):
++ if prev:
++ m = trail_whitespace_re.search(prev)
++ # +1 to leave the leading \n on:
++ prev = prev[:m.start()+1]
++ tokens[i-1] = prev
++ if next:
++ m = lead_whitespace_re.search(next)
++ next = next[m.end():]
++ tokens[i+1] = next
++ return tokens
++
++
++def find_position(string, index):
++ """Given a string and index, return (line, column)"""
++ leading = string[:index].splitlines()
++ return (len(leading), len(leading[-1])+1)
++
++def parse(s, name=None):
++ r"""
++ Parses a string into a kind of AST
++
++ >>> parse('{{x}}')
++ [('expr', (1, 3), 'x')]
++ >>> parse('foo')
++ ['foo']
++ >>> parse('{{if x}}test{{endif}}')
++ [('cond', (1, 3), ('if', (1, 3), 'x', ['test']))]
++ >>> parse('series->{{for x in y}}x={{x}}{{endfor}}')
++ ['series->', ('for', (1, 11), ('x',), 'y', ['x=', ('expr', (1, 27), 'x')])]
++ >>> parse('{{for x, y in z:}}{{continue}}{{endfor}}')
++ [('for', (1, 3), ('x', 'y'), 'z', [('continue', (1, 21))])]
++ >>> parse('{{py:x=1}}')
++ [('py', (1, 3), 'x=1')]
++ >>> parse('{{if x}}a{{elif y}}b{{else}}c{{endif}}')
++ [('cond', (1, 3), ('if', (1, 3), 'x', ['a']), ('elif', (1, 12), 'y', ['b']), ('else', (1, 23), None, ['c']))]
++
++ Some exceptions::
++
++ >>> parse('{{continue}}')
++ Traceback (most recent call last):
++ ...
++ TemplateError: continue outside of for loop at line 1 column 3
++ >>> parse('{{if x}}foo')
++ Traceback (most recent call last):
++ ...
++ TemplateError: No {{endif}} at line 1 column 3
++ >>> parse('{{else}}')
++ Traceback (most recent call last):
++ ...
++ TemplateError: else outside of an if block at line 1 column 3
++ >>> parse('{{if x}}{{for x in y}}{{endif}}{{endfor}}')
++ Traceback (most recent call last):
++ ...
++ TemplateError: Unexpected endif at line 1 column 25
++ >>> parse('{{if}}{{endif}}')
++ Traceback (most recent call last):
++ ...
++ TemplateError: if with no expression at line 1 column 3
++ >>> parse('{{for x y}}{{endfor}}')
++ Traceback (most recent call last):
++ ...
++ TemplateError: Bad for (no "in") in 'x y' at line 1 column 3
++ >>> parse('{{py:x=1\ny=2}}')
++ Traceback (most recent call last):
++ ...
++ TemplateError: Multi-line py blocks must start with a newline at line 1 column 3
++ """
++ tokens = lex(s, name=name)
++ result = []
++ while tokens:
++ next, tokens = parse_expr(tokens, name)
++ result.append(next)
++ return result
++
++def parse_expr(tokens, name, context=()):
++ if isinstance(tokens[0], basestring):
++ return tokens[0], tokens[1:]
++ expr, pos = tokens[0]
++ expr = expr.strip()
++ if expr.startswith('py:'):
++ expr = expr[3:].lstrip(' \t')
++ if expr.startswith('\n'):
++ expr = expr[1:]
++ else:
++ if '\n' in expr:
++ raise TemplateError(
++ 'Multi-line py blocks must start with a newline',
++ position=pos, name=name)
++ return ('py', pos, expr), tokens[1:]
++ elif expr in ('continue', 'break'):
++ if 'for' not in context:
++ raise TemplateError(
++ 'continue outside of for loop',
++ position=pos, name=name)
++ return (expr, pos), tokens[1:]
++ elif expr.startswith('if '):
++ return parse_cond(tokens, name, context)
++ elif (expr.startswith('elif ')
++ or expr == 'else'):
++ raise TemplateError(
++ '%s outside of an if block' % expr.split()[0],
++ position=pos, name=name)
++ elif expr in ('if', 'elif', 'for'):
++ raise TemplateError(
++ '%s with no expression' % expr,
++ position=pos, name=name)
++ elif expr in ('endif', 'endfor'):
++ raise TemplateError(
++ 'Unexpected %s' % expr,
++ position=pos, name=name)
++ elif expr.startswith('for '):
++ return parse_for(tokens, name, context)
++ elif expr.startswith('default '):
++ return parse_default(tokens, name, context)
++ elif expr.startswith('#'):
++ return ('comment', pos, tokens[0][0]), tokens[1:]
++ return ('expr', pos, tokens[0][0]), tokens[1:]
++
++def parse_cond(tokens, name, context):
++ start = tokens[0][1]
++ pieces = []
++ context = context + ('if',)
++ while 1:
++ if not tokens:
++ raise TemplateError(
++ 'Missing {{endif}}',
++ position=start, name=name)
++ if (isinstance(tokens[0], tuple)
++ and tokens[0][0] == 'endif'):
++ return ('cond', start) + tuple(pieces), tokens[1:]
++ next, tokens = parse_one_cond(tokens, name, context)
++ pieces.append(next)
++
++def parse_one_cond(tokens, name, context):
++ (first, pos), tokens = tokens[0], tokens[1:]
++ content = []
++ if first.endswith(':'):
++ first = first[:-1]
++ if first.startswith('if '):
++ part = ('if', pos, first[3:].lstrip(), content)
++ elif first.startswith('elif '):
++ part = ('elif', pos, first[5:].lstrip(), content)
++ elif first == 'else':
++ part = ('else', pos, None, content)
++ else:
++ assert 0, "Unexpected token %r at %s" % (first, pos)
++ while 1:
++ if not tokens:
++ raise TemplateError(
++ 'No {{endif}}',
++ position=pos, name=name)
++ if (isinstance(tokens[0], tuple)
++ and (tokens[0][0] == 'endif'
++ or tokens[0][0].startswith('elif ')
++ or tokens[0][0] == 'else')):
++ return part, tokens
++ next, tokens = parse_expr(tokens, name, context)
++ content.append(next)
++
++def parse_for(tokens, name, context):
++ first, pos = tokens[0]
++ tokens = tokens[1:]
++ context = ('for',) + context
++ content = []
++ assert first.startswith('for ')
++ if first.endswith(':'):
++ first = first[:-1]
++ first = first[3:].strip()
++ match = in_re.search(first)
++ if not match:
++ raise TemplateError(
++ 'Bad for (no "in") in %r' % first,
++ position=pos, name=name)
++ vars = first[:match.start()]
++ if '(' in vars:
++ raise TemplateError(
++ 'You cannot have () in the variable section of a for loop (%r)'
++ % vars, position=pos, name=name)
++ vars = tuple([
++ v.strip() for v in first[:match.start()].split(',')
++ if v.strip()])
++ expr = first[match.end():]
++ while 1:
++ if not tokens:
++ raise TemplateError(
++ 'No {{endfor}}',
++ position=pos, name=name)
++ if (isinstance(tokens[0], tuple)
++ and tokens[0][0] == 'endfor'):
++ return ('for', pos, vars, expr, content), tokens[1:]
++ next, tokens = parse_expr(tokens, name, context)
++ content.append(next)
++
++def parse_default(tokens, name, context):
++ first, pos = tokens[0]
++ assert first.startswith('default ')
++ first = first.split(None, 1)[1]
++ parts = first.split('=', 1)
++ if len(parts) == 1:
++ raise TemplateError(
++ "Expression must be {{default var=value}}; no = found in %r" % first,
++ position=pos, name=name)
++ var = parts[0].strip()
++ if ',' in var:
++ raise TemplateError(
++ "{{default x, y = ...}} is not supported",
++ position=pos, name=name)
++ if not var_re.search(var):
++ raise TemplateError(
++ "Not a valid variable name for {{default}}: %r"
++ % var, position=pos, name=name)
++ expr = parts[1].strip()
++ return ('default', pos, var, expr), tokens[1:]
++
++_fill_command_usage = """\
++%prog [OPTIONS] TEMPLATE arg=value
++
++Use py:arg=value to set a Python value; otherwise all values are
++strings.
++"""
++
++def fill_command(args=None):
++ import sys, optparse, pkg_resources, os
++ if args is None:
++ args = sys.argv[1:]
++ dist = pkg_resources.get_distribution('Paste')
++ parser = optparse.OptionParser(
++ version=str(dist),
++ usage=_fill_command_usage)
++ parser.add_option(
++ '-o', '--output',
++ dest='output',
++ metavar="FILENAME",
++ help="File to write output to (default stdout)")
++ parser.add_option(
++ '--html',
++ dest='use_html',
++ action='store_true',
++ help="Use HTML style filling (including automatic HTML quoting)")
++ parser.add_option(
++ '--env',
++ dest='use_env',
++ action='store_true',
++ help="Put the environment in as top-level variables")
++ options, args = parser.parse_args(args)
++ if len(args) < 1:
++ print 'You must give a template filename'
++ print dir(parser)
++ assert 0
++ template_name = args[0]
++ args = args[1:]
++ vars = {}
++ if options.use_env:
++ vars.update(os.environ)
++ for value in args:
++ if '=' not in value:
++ print 'Bad argument: %r' % value
++ sys.exit(2)
++ name, value = value.split('=', 1)
++ if name.startswith('py:'):
++ name = name[:3]
++ value = eval(value)
++ vars[name] = value
++ if template_name == '-':
++ template_content = sys.stdin.read()
++ template_name = '<stdin>'
++ else:
++ f = open(template_name, 'rb')
++ template_content = f.read()
++ f.close()
++ if options.use_html:
++ TemplateClass = HTMLTemplate
++ else:
++ TemplateClass = Template
++ template = TemplateClass(template_content, name=template_name)
++ result = template.substitute(vars)
++ if options.output:
++ f = open(options.output, 'wb')
++ f.write(result)
++ f.close()
++ else:
++ sys.stdout.write(result)
++
++if __name__ == '__main__':
++ from paste.util.template import fill_command
++ fill_command()
++
++
+Index: Paste-1.7.4/paste/util/template.py
+===================================================================
+--- Paste-1.7.4.orig/paste/util/template.py
++++ /dev/null
+@@ -1,758 +0,0 @@
+-"""
+-A small templating language
+-
+-This implements a small templating language for use internally in
+-Paste and Paste Script. This language implements if/elif/else,
+-for/continue/break, expressions, and blocks of Python code. The
+-syntax is::
+-
+- {{any expression (function calls etc)}}
+- {{any expression | filter}}
+- {{for x in y}}...{{endfor}}
+- {{if x}}x{{elif y}}y{{else}}z{{endif}}
+- {{py:x=1}}
+- {{py:
+- def foo(bar):
+- return 'baz'
+- }}
+- {{default var = default_value}}
+- {{# comment}}
+-
+-You use this with the ``Template`` class or the ``sub`` shortcut.
+-The ``Template`` class takes the template string and the name of
+-the template (for errors) and a default namespace. Then (like
+-``string.Template``) you can call the ``tmpl.substitute(**kw)``
+-method to make a substitution (or ``tmpl.substitute(a_dict)``).
+-
+-``sub(content, **kw)`` substitutes the template immediately. You
+-can use ``__name='tmpl.html'`` to set the name of the template.
+-
+-If there are syntax errors ``TemplateError`` will be raised.
+-"""
+-
+-import re
+-import sys
+-import cgi
+-import urllib
+-from paste.util.looper import looper
+-
+-__all__ = ['TemplateError', 'Template', 'sub', 'HTMLTemplate',
+- 'sub_html', 'html', 'bunch']
+-
+-token_re = re.compile(r'\{\{|\}\}')
+-in_re = re.compile(r'\s+in\s+')
+-var_re = re.compile(r'^[a-z_][a-z0-9_]*$', re.I)
+-
+-class TemplateError(Exception):
+- """Exception raised while parsing a template
+- """
+-
+- def __init__(self, message, position, name=None):
+- self.message = message
+- self.position = position
+- self.name = name
+-
+- def __str__(self):
+- msg = '%s at line %s column %s' % (
+- self.message, self.position[0], self.position[1])
+- if self.name:
+- msg += ' in %s' % self.name
+- return msg
+-
+-class _TemplateContinue(Exception):
+- pass
+-
+-class _TemplateBreak(Exception):
+- pass
+-
+-class Template(object):
+-
+- default_namespace = {
+- 'start_braces': '{{',
+- 'end_braces': '}}',
+- 'looper': looper,
+- }
+-
+- default_encoding = 'utf8'
+-
+- def __init__(self, content, name=None, namespace=None):
+- self.content = content
+- self._unicode = isinstance(content, unicode)
+- self.name = name
+- self._parsed = parse(content, name=name)
+- if namespace is None:
+- namespace = {}
+- self.namespace = namespace
+-
+- def from_filename(cls, filename, namespace=None, encoding=None):
+- f = open(filename, 'rb')
+- c = f.read()
+- f.close()
+- if encoding:
+- c = c.decode(encoding)
+- return cls(content=c, name=filename, namespace=namespace)
+-
+- from_filename = classmethod(from_filename)
+-
+- def __repr__(self):
+- return '<%s %s name=%r>' % (
+- self.__class__.__name__,
+- hex(id(self))[2:], self.name)
+-
+- def substitute(self, *args, **kw):
+- if args:
+- if kw:
+- raise TypeError(
+- "You can only give positional *or* keyword arguments")
+- if len(args) > 1:
+- raise TypeError(
+- "You can only give on positional argument")
+- kw = args[0]
+- ns = self.default_namespace.copy()
+- ns.update(self.namespace)
+- ns.update(kw)
+- result = self._interpret(ns)
+- return result
+-
+- def _interpret(self, ns):
+- __traceback_hide__ = True
+- parts = []
+- self._interpret_codes(self._parsed, ns, out=parts)
+- return ''.join(parts)
+-
+- def _interpret_codes(self, codes, ns, out):
+- __traceback_hide__ = True
+- for item in codes:
+- if isinstance(item, basestring):
+- out.append(item)
+- else:
+- self._interpret_code(item, ns, out)
+-
+- def _interpret_code(self, code, ns, out):
+- __traceback_hide__ = True
+- name, pos = code[0], code[1]
+- if name == 'py':
+- self._exec(code[2], ns, pos)
+- elif name == 'continue':
+- raise _TemplateContinue()
+- elif name == 'break':
+- raise _TemplateBreak()
+- elif name == 'for':
+- vars, expr, content = code[2], code[3], code[4]
+- expr = self._eval(expr, ns, pos)
+- self._interpret_for(vars, expr, content, ns, out)
+- elif name == 'cond':
+- parts = code[2:]
+- self._interpret_if(parts, ns, out)
+- elif name == 'expr':
+- parts = code[2].split('|')
+- base = self._eval(parts[0], ns, pos)
+- for part in parts[1:]:
+- func = self._eval(part, ns, pos)
+- base = func(base)
+- out.append(self._repr(base, pos))
+- elif name == 'default':
+- var, expr = code[2], code[3]
+- if var not in ns:
+- result = self._eval(expr, ns, pos)
+- ns[var] = result
+- elif name == 'comment':
+- return
+- else:
+- assert 0, "Unknown code: %r" % name
+-
+- def _interpret_for(self, vars, expr, content, ns, out):
+- __traceback_hide__ = True
+- for item in expr:
+- if len(vars) == 1:
+- ns[vars[0]] = item
+- else:
+- if len(vars) != len(item):
+- raise ValueError(
+- 'Need %i items to unpack (got %i items)'
+- % (len(vars), len(item)))
+- for name, value in zip(vars, item):
+- ns[name] = value
+- try:
+- self._interpret_codes(content, ns, out)
+- except _TemplateContinue:
+- continue
+- except _TemplateBreak:
+- break
+-
+- def _interpret_if(self, parts, ns, out):
+- __traceback_hide__ = True
+- # @@: if/else/else gets through
+- for part in parts:
+- assert not isinstance(part, basestring)
+- name, pos = part[0], part[1]
+- if name == 'else':
+- result = True
+- else:
+- result = self._eval(part[2], ns, pos)
+- if result:
+- self._interpret_codes(part[3], ns, out)
+- break
+-
+- def _eval(self, code, ns, pos):
+- __traceback_hide__ = True
+- try:
+- value = eval(code, ns)
+- return value
+- except:
+- exc_info = sys.exc_info()
+- e = exc_info[1]
+- if getattr(e, 'args'):
+- arg0 = e.args[0]
+- else:
+- arg0 = str(e)
+- e.args = (self._add_line_info(arg0, pos),)
+- raise exc_info[0], e, exc_info[2]
+-
+- def _exec(self, code, ns, pos):
+- __traceback_hide__ = True
+- try:
+- exec code in ns
+- except:
+- exc_info = sys.exc_info()
+- e = exc_info[1]
+- e.args = (self._add_line_info(e.args[0], pos),)
+- raise exc_info[0], e, exc_info[2]
+-
+- def _repr(self, value, pos):
+- __traceback_hide__ = True
+- try:
+- if value is None:
+- return ''
+- if self._unicode:
+- try:
+- value = unicode(value)
+- except UnicodeDecodeError:
+- value = str(value)
+- else:
+- value = str(value)
+- except:
+- exc_info = sys.exc_info()
+- e = exc_info[1]
+- e.args = (self._add_line_info(e.args[0], pos),)
+- raise exc_info[0], e, exc_info[2]
+- else:
+- if self._unicode and isinstance(value, str):
+- if not self.decode_encoding:
+- raise UnicodeDecodeError(
+- 'Cannot decode str value %r into unicode '
+- '(no default_encoding provided)' % value)
+- value = value.decode(self.default_encoding)
+- elif not self._unicode and isinstance(value, unicode):
+- if not self.decode_encoding:
+- raise UnicodeEncodeError(
+- 'Cannot encode unicode value %r into str '
+- '(no default_encoding provided)' % value)
+- value = value.encode(self.default_encoding)
+- return value
+-
+-
+- def _add_line_info(self, msg, pos):
+- msg = "%s at line %s column %s" % (
+- msg, pos[0], pos[1])
+- if self.name:
+- msg += " in file %s" % self.name
+- return msg
+-
+-def sub(content, **kw):
+- name = kw.get('__name')
+- tmpl = Template(content, name=name)
+- return tmpl.substitute(kw)
+- return result
+-
+-def paste_script_template_renderer(content, vars, filename=None):
+- tmpl = Template(content, name=filename)
+- return tmpl.substitute(vars)
+-
+-class bunch(dict):
+-
+- def __init__(self, **kw):
+- for name, value in kw.items():
+- setattr(self, name, value)
+-
+- def __setattr__(self, name, value):
+- self[name] = value
+-
+- def __getattr__(self, name):
+- try:
+- return self[name]
+- except KeyError:
+- raise AttributeError(name)
+-
+- def __getitem__(self, key):
+- if 'default' in self:
+- try:
+- return dict.__getitem__(self, key)
+- except KeyError:
+- return dict.__getitem__(self, 'default')
+- else:
+- return dict.__getitem__(self, key)
+-
+- def __repr__(self):
+- items = [
+- (k, v) for k, v in self.items()]
+- items.sort()
+- return '<%s %s>' % (
+- self.__class__.__name__,
+- ' '.join(['%s=%r' % (k, v) for k, v in items]))
+-
+-############################################################
+-## HTML Templating
+-############################################################
+-
+-class html(object):
+- def __init__(self, value):
+- self.value = value
+- def __str__(self):
+- return self.value
+- def __repr__(self):
+- return '<%s %r>' % (
+- self.__class__.__name__, self.value)
+-
+-def html_quote(value):
+- if value is None:
+- return ''
+- if not isinstance(value, basestring):
+- if hasattr(value, '__unicode__'):
+- value = unicode(value)
+- else:
+- value = str(value)
+- value = cgi.escape(value, 1)
+- if isinstance(value, unicode):
+- value = value.encode('ascii', 'xmlcharrefreplace')
+- return value
+-
+-def url(v):
+- if not isinstance(v, basestring):
+- if hasattr(v, '__unicode__'):
+- v = unicode(v)
+- else:
+- v = str(v)
+- if isinstance(v, unicode):
+- v = v.encode('utf8')
+- return urllib.quote(v)
+-
+-def attr(**kw):
+- kw = kw.items()
+- kw.sort()
+- parts = []
+- for name, value in kw:
+- if value is None:
+- continue
+- if name.endswith('_'):
+- name = name[:-1]
+- parts.append('%s="%s"' % (html_quote(name), html_quote(value)))
+- return html(' '.join(parts))
+-
+-class HTMLTemplate(Template):
+-
+- default_namespace = Template.default_namespace.copy()
+- default_namespace.update(dict(
+- html=html,
+- attr=attr,
+- url=url,
+- ))
+-
+- def _repr(self, value, pos):
+- plain = Template._repr(self, value, pos)
+- if isinstance(value, html):
+- return plain
+- else:
+- return html_quote(plain)
+-
+-def sub_html(content, **kw):
+- name = kw.get('__name')
+- tmpl = HTMLTemplate(content, name=name)
+- return tmpl.substitute(kw)
+- return result
+-
+-
+-############################################################
+-## Lexing and Parsing
+-############################################################
+-
+-def lex(s, name=None, trim_whitespace=True):
+- """
+- Lex a string into chunks:
+-
+- >>> lex('hey')
+- ['hey']
+- >>> lex('hey {{you}}')
+- ['hey ', ('you', (1, 7))]
+- >>> lex('hey {{')
+- Traceback (most recent call last):
+- ...
+- TemplateError: No }} to finish last expression at line 1 column 7
+- >>> lex('hey }}')
+- Traceback (most recent call last):
+- ...
+- TemplateError: }} outside expression at line 1 column 7
+- >>> lex('hey {{ {{')
+- Traceback (most recent call last):
+- ...
+- TemplateError: {{ inside expression at line 1 column 10
+-
+- """
+- in_expr = False
+- chunks = []
+- last = 0
+- last_pos = (1, 1)
+- for match in token_re.finditer(s):
+- expr = match.group(0)
+- pos = find_position(s, match.end())
+- if expr == '{{' and in_expr:
+- raise TemplateError('{{ inside expression', position=pos,
+- name=name)
+- elif expr == '}}' and not in_expr:
+- raise TemplateError('}} outside expression', position=pos,
+- name=name)
+- if expr == '{{':
+- part = s[last:match.start()]
+- if part:
+- chunks.append(part)
+- in_expr = True
+- else:
+- chunks.append((s[last:match.start()], last_pos))
+- in_expr = False
+- last = match.end()
+- last_pos = pos
+- if in_expr:
+- raise TemplateError('No }} to finish last expression',
+- name=name, position=last_pos)
+- part = s[last:]
+- if part:
+- chunks.append(part)
+- if trim_whitespace:
+- chunks = trim_lex(chunks)
+- return chunks
+-
+-statement_re = re.compile(r'^(?:if |elif |else |for |py:)')
+-single_statements = ['endif', 'endfor', 'continue', 'break']
+-trail_whitespace_re = re.compile(r'\n[\t ]*$')
+-lead_whitespace_re = re.compile(r'^[\t ]*\n')
+-
+-def trim_lex(tokens):
+- r"""
+- Takes a lexed set of tokens, and removes whitespace when there is
+- a directive on a line by itself:
+-
+- >>> tokens = lex('{{if x}}\nx\n{{endif}}\ny', trim_whitespace=False)
+- >>> tokens
+- [('if x', (1, 3)), '\nx\n', ('endif', (3, 3)), '\ny']
+- >>> trim_lex(tokens)
+- [('if x', (1, 3)), 'x\n', ('endif', (3, 3)), 'y']
+- """
+- for i in range(len(tokens)):
+- current = tokens[i]
+- if isinstance(tokens[i], basestring):
+- # we don't trim this
+- continue
+- item = current[0]
+- if not statement_re.search(item) and item not in single_statements:
+- continue
+- if not i:
+- prev = ''
+- else:
+- prev = tokens[i-1]
+- if i+1 >= len(tokens):
+- next = ''
+- else:
+- next = tokens[i+1]
+- if (not isinstance(next, basestring)
+- or not isinstance(prev, basestring)):
+- continue
+- if ((not prev or trail_whitespace_re.search(prev))
+- and (not next or lead_whitespace_re.search(next))):
+- if prev:
+- m = trail_whitespace_re.search(prev)
+- # +1 to leave the leading \n on:
+- prev = prev[:m.start()+1]
+- tokens[i-1] = prev
+- if next:
+- m = lead_whitespace_re.search(next)
+- next = next[m.end():]
+- tokens[i+1] = next
+- return tokens
+-
+-
+-def find_position(string, index):
+- """Given a string and index, return (line, column)"""
+- leading = string[:index].splitlines()
+- return (len(leading), len(leading[-1])+1)
+-
+-def parse(s, name=None):
+- r"""
+- Parses a string into a kind of AST
+-
+- >>> parse('{{x}}')
+- [('expr', (1, 3), 'x')]
+- >>> parse('foo')
+- ['foo']
+- >>> parse('{{if x}}test{{endif}}')
+- [('cond', (1, 3), ('if', (1, 3), 'x', ['test']))]
+- >>> parse('series->{{for x in y}}x={{x}}{{endfor}}')
+- ['series->', ('for', (1, 11), ('x',), 'y', ['x=', ('expr', (1, 27), 'x')])]
+- >>> parse('{{for x, y in z:}}{{continue}}{{endfor}}')
+- [('for', (1, 3), ('x', 'y'), 'z', [('continue', (1, 21))])]
+- >>> parse('{{py:x=1}}')
+- [('py', (1, 3), 'x=1')]
+- >>> parse('{{if x}}a{{elif y}}b{{else}}c{{endif}}')
+- [('cond', (1, 3), ('if', (1, 3), 'x', ['a']), ('elif', (1, 12), 'y', ['b']), ('else', (1, 23), None, ['c']))]
+-
+- Some exceptions::
+-
+- >>> parse('{{continue}}')
+- Traceback (most recent call last):
+- ...
+- TemplateError: continue outside of for loop at line 1 column 3
+- >>> parse('{{if x}}foo')
+- Traceback (most recent call last):
+- ...
+- TemplateError: No {{endif}} at line 1 column 3
+- >>> parse('{{else}}')
+- Traceback (most recent call last):
+- ...
+- TemplateError: else outside of an if block at line 1 column 3
+- >>> parse('{{if x}}{{for x in y}}{{endif}}{{endfor}}')
+- Traceback (most recent call last):
+- ...
+- TemplateError: Unexpected endif at line 1 column 25
+- >>> parse('{{if}}{{endif}}')
+- Traceback (most recent call last):
+- ...
+- TemplateError: if with no expression at line 1 column 3
+- >>> parse('{{for x y}}{{endfor}}')
+- Traceback (most recent call last):
+- ...
+- TemplateError: Bad for (no "in") in 'x y' at line 1 column 3
+- >>> parse('{{py:x=1\ny=2}}')
+- Traceback (most recent call last):
+- ...
+- TemplateError: Multi-line py blocks must start with a newline at line 1 column 3
+- """
+- tokens = lex(s, name=name)
+- result = []
+- while tokens:
+- next, tokens = parse_expr(tokens, name)
+- result.append(next)
+- return result
+-
+-def parse_expr(tokens, name, context=()):
+- if isinstance(tokens[0], basestring):
+- return tokens[0], tokens[1:]
+- expr, pos = tokens[0]
+- expr = expr.strip()
+- if expr.startswith('py:'):
+- expr = expr[3:].lstrip(' \t')
+- if expr.startswith('\n'):
+- expr = expr[1:]
+- else:
+- if '\n' in expr:
+- raise TemplateError(
+- 'Multi-line py blocks must start with a newline',
+- position=pos, name=name)
+- return ('py', pos, expr), tokens[1:]
+- elif expr in ('continue', 'break'):
+- if 'for' not in context:
+- raise TemplateError(
+- 'continue outside of for loop',
+- position=pos, name=name)
+- return (expr, pos), tokens[1:]
+- elif expr.startswith('if '):
+- return parse_cond(tokens, name, context)
+- elif (expr.startswith('elif ')
+- or expr == 'else'):
+- raise TemplateError(
+- '%s outside of an if block' % expr.split()[0],
+- position=pos, name=name)
+- elif expr in ('if', 'elif', 'for'):
+- raise TemplateError(
+- '%s with no expression' % expr,
+- position=pos, name=name)
+- elif expr in ('endif', 'endfor'):
+- raise TemplateError(
+- 'Unexpected %s' % expr,
+- position=pos, name=name)
+- elif expr.startswith('for '):
+- return parse_for(tokens, name, context)
+- elif expr.startswith('default '):
+- return parse_default(tokens, name, context)
+- elif expr.startswith('#'):
+- return ('comment', pos, tokens[0][0]), tokens[1:]
+- return ('expr', pos, tokens[0][0]), tokens[1:]
+-
+-def parse_cond(tokens, name, context):
+- start = tokens[0][1]
+- pieces = []
+- context = context + ('if',)
+- while 1:
+- if not tokens:
+- raise TemplateError(
+- 'Missing {{endif}}',
+- position=start, name=name)
+- if (isinstance(tokens[0], tuple)
+- and tokens[0][0] == 'endif'):
+- return ('cond', start) + tuple(pieces), tokens[1:]
+- next, tokens = parse_one_cond(tokens, name, context)
+- pieces.append(next)
+-
+-def parse_one_cond(tokens, name, context):
+- (first, pos), tokens = tokens[0], tokens[1:]
+- content = []
+- if first.endswith(':'):
+- first = first[:-1]
+- if first.startswith('if '):
+- part = ('if', pos, first[3:].lstrip(), content)
+- elif first.startswith('elif '):
+- part = ('elif', pos, first[5:].lstrip(), content)
+- elif first == 'else':
+- part = ('else', pos, None, content)
+- else:
+- assert 0, "Unexpected token %r at %s" % (first, pos)
+- while 1:
+- if not tokens:
+- raise TemplateError(
+- 'No {{endif}}',
+- position=pos, name=name)
+- if (isinstance(tokens[0], tuple)
+- and (tokens[0][0] == 'endif'
+- or tokens[0][0].startswith('elif ')
+- or tokens[0][0] == 'else')):
+- return part, tokens
+- next, tokens = parse_expr(tokens, name, context)
+- content.append(next)
+-
+-def parse_for(tokens, name, context):
+- first, pos = tokens[0]
+- tokens = tokens[1:]
+- context = ('for',) + context
+- content = []
+- assert first.startswith('for ')
+- if first.endswith(':'):
+- first = first[:-1]
+- first = first[3:].strip()
+- match = in_re.search(first)
+- if not match:
+- raise TemplateError(
+- 'Bad for (no "in") in %r' % first,
+- position=pos, name=name)
+- vars = first[:match.start()]
+- if '(' in vars:
+- raise TemplateError(
+- 'You cannot have () in the variable section of a for loop (%r)'
+- % vars, position=pos, name=name)
+- vars = tuple([
+- v.strip() for v in first[:match.start()].split(',')
+- if v.strip()])
+- expr = first[match.end():]
+- while 1:
+- if not tokens:
+- raise TemplateError(
+- 'No {{endfor}}',
+- position=pos, name=name)
+- if (isinstance(tokens[0], tuple)
+- and tokens[0][0] == 'endfor'):
+- return ('for', pos, vars, expr, content), tokens[1:]
+- next, tokens = parse_expr(tokens, name, context)
+- content.append(next)
+-
+-def parse_default(tokens, name, context):
+- first, pos = tokens[0]
+- assert first.startswith('default ')
+- first = first.split(None, 1)[1]
+- parts = first.split('=', 1)
+- if len(parts) == 1:
+- raise TemplateError(
+- "Expression must be {{default var=value}}; no = found in %r" % first,
+- position=pos, name=name)
+- var = parts[0].strip()
+- if ',' in var:
+- raise TemplateError(
+- "{{default x, y = ...}} is not supported",
+- position=pos, name=name)
+- if not var_re.search(var):
+- raise TemplateError(
+- "Not a valid variable name for {{default}}: %r"
+- % var, position=pos, name=name)
+- expr = parts[1].strip()
+- return ('default', pos, var, expr), tokens[1:]
+-
+-_fill_command_usage = """\
+-%prog [OPTIONS] TEMPLATE arg=value
+-
+-Use py:arg=value to set a Python value; otherwise all values are
+-strings.
+-"""
+-
+-def fill_command(args=None):
+- import sys, optparse, pkg_resources, os
+- if args is None:
+- args = sys.argv[1:]
+- dist = pkg_resources.get_distribution('Paste')
+- parser = optparse.OptionParser(
+- version=str(dist),
+- usage=_fill_command_usage)
+- parser.add_option(
+- '-o', '--output',
+- dest='output',
+- metavar="FILENAME",
+- help="File to write output to (default stdout)")
+- parser.add_option(
+- '--html',
+- dest='use_html',
+- action='store_true',
+- help="Use HTML style filling (including automatic HTML quoting)")
+- parser.add_option(
+- '--env',
+- dest='use_env',
+- action='store_true',
+- help="Put the environment in as top-level variables")
+- options, args = parser.parse_args(args)
+- if len(args) < 1:
+- print 'You must give a template filename'
+- print dir(parser)
+- assert 0
+- template_name = args[0]
+- args = args[1:]
+- vars = {}
+- if options.use_env:
+- vars.update(os.environ)
+- for value in args:
+- if '=' not in value:
+- print 'Bad argument: %r' % value
+- sys.exit(2)
+- name, value = value.split('=', 1)
+- if name.startswith('py:'):
+- name = name[:3]
+- value = eval(value)
+- vars[name] = value
+- if template_name == '-':
+- template_content = sys.stdin.read()
+- template_name = '<stdin>'
+- else:
+- f = open(template_name, 'rb')
+- template_content = f.read()
+- f.close()
+- if options.use_html:
+- TemplateClass = HTMLTemplate
+- else:
+- TemplateClass = Template
+- template = TemplateClass(template_content, name=template_name)
+- result = template.substitute(vars)
+- if options.output:
+- f = open(options.output, 'wb')
+- f.write(result)
+- f.close()
+- else:
+- sys.stdout.write(result)
+-
+-if __name__ == '__main__':
+- from paste.util.template import fill_command
+- fill_command()
+-
+-
diff --git a/dev-python/paste/files/paste-1.7.5.1-userdict.patch b/dev-python/paste/files/paste-1.7.5.1-userdict.patch
new file mode 100644
index 000000000000..061f38a52828
--- /dev/null
+++ b/dev-python/paste/files/paste-1.7.5.1-userdict.patch
@@ -0,0 +1,64 @@
+--- a/paste/debug/fsdiff.py
++++ b/paste/debug/fsdiff.py
+@@ -12,10 +12,14 @@
+ import os
+ from fnmatch import fnmatch
+ from datetime import datetime
++
+ try:
+- from UserDict import IterableUserDict
++ import collections.UserDict as IterableUserDict
+ except ImportError:
+- from paste.util.UserDict24 import IterableUserDict
++ try:
++ from UserDict import IterableUserDict
++ except ImportError:
++ from paste.util.UserDict24 import IterableUserDict
+ import operator
+ import re
+
+--- a/paste/request.py
++++ b/paste/request.py
+@@ -22,10 +22,12 @@
+ from StringIO import StringIO
+ import urlparse
+ import urllib
++
+ try:
+ from UserDict import DictMixin
+ except ImportError:
+- from paste.util.UserDict24 import DictMixin
++ from collections import MutableMapping as DictMixin
++
+ from paste.util.multidict import MultiDict
+
+ __all__ = ['get_cookies', 'get_cookie_dict', 'parse_querystring',
+--- a/paste/urlmap.py
++++ b/paste/urlmap.py
+@@ -4,7 +4,10 @@
+ Map URL prefixes to WSGI applications. See ``URLMap``
+ """
+
+-from UserDict import DictMixin
++try:
++ from UserDict import DictMixin
++except ImportError:
++ from collections import MutableMapping as DictMixin
+ import re
+ import os
+ import cgi
+--- a/paste/util/multidict.py
++++ b/paste/util/multidict.py
+@@ -3,7 +3,11 @@
+ import cgi
+ import copy
+ import sys
+-from UserDict import DictMixin
++
++try:
++ from UserDict import DictMixin
++except ImportError:
++ from collections import MutableMapping as DictMixin
+
+ class MultiDict(DictMixin):
+
diff --git a/dev-python/paste/files/paste-2.0.2-unbundle-tempita.patch b/dev-python/paste/files/paste-2.0.2-unbundle-tempita.patch
new file mode 100644
index 000000000000..eef7abb4108e
--- /dev/null
+++ b/dev-python/paste/files/paste-2.0.2-unbundle-tempita.patch
@@ -0,0 +1,36 @@
+ paste/util/looper/__init__.py | 4 ++++
+ paste/util/{looper.py => looper/_looper.py} | 0
+ paste/util/template/__init__.py | 6 ++++++
+ paste/util/{template.py => template/_template.py} | 0
+ 4 files changed, 10 insertions(+)
+
+diff --git a/paste/util/looper/__init__.py b/paste/util/looper/__init__.py
+new file mode 100644
+index 0000000..77d7e80
+--- /dev/null
++++ b/paste/util/looper/__init__.py
+@@ -0,0 +1,4 @@
++try:
++ from tempita._looper import *
++except ImportError:
++ from _looper import *
+diff --git a/paste/util/looper.py b/paste/util/looper/_looper.py
+similarity index 100%
+rename from paste/util/looper.py
+rename to paste/util/looper/_looper.py
+diff --git a/paste/util/template/__init__.py b/paste/util/template/__init__.py
+new file mode 100644
+index 0000000..a0a5730
+--- /dev/null
++++ b/paste/util/template/__init__.py
+@@ -0,0 +1,6 @@
++try:
++ from tempita import *
++ from tempita import paste_script_template_renderer
++except ImportError:
++ from _template import *
++ from _template import paste_script_template_renderer
+diff --git a/paste/util/template.py b/paste/util/template/_template.py
+similarity index 100%
+rename from paste/util/template.py
+rename to paste/util/template/_template.py
diff --git a/dev-python/paste/metadata.xml b/dev-python/paste/metadata.xml
new file mode 100644
index 000000000000..60a4c1835023
--- /dev/null
+++ b/dev-python/paste/metadata.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE pkgmetadata SYSTEM "http://www.gentoo.org/dtd/metadata.dtd">
+<pkgmetadata>
+ <herd>python</herd>
+ <use>
+ <flag name="flup">enable support for flup (and therefore for various
+ wgsi servers and middleware)</flag>
+ <flag name="openid">enable OpenID support</flag>
+ </use>
+ <longdescription lang="en">This package provides several pieces of "middleware" (or filters) that can
+ be nested to build web applications. Each piece of middleware uses the WSGI
+ (PEP 333) interface, and should be compatible with other middleware based on
+ those interfaces.</longdescription>
+ <upstream>
+ <remote-id type="pypi">Paste</remote-id>
+ </upstream>
+</pkgmetadata>
diff --git a/dev-python/paste/paste-1.7.5.1-r1.ebuild b/dev-python/paste/paste-1.7.5.1-r1.ebuild
new file mode 100644
index 000000000000..e3e8a3f7a006
--- /dev/null
+++ b/dev-python/paste/paste-1.7.5.1-r1.ebuild
@@ -0,0 +1,69 @@
+# Copyright 1999-2015 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+EAPI=5
+
+PYTHON_COMPAT=( python2_7 )
+
+inherit distutils-r1
+
+MY_PN="Paste"
+MY_P="${MY_PN}-${PV}"
+
+DESCRIPTION="Tools for using a Web Server Gateway Interface stack"
+HOMEPAGE="http://pythonpaste.org http://pypi.python.org/pypi/Paste"
+SRC_URI="mirror://pypi/${MY_PN:0:1}/${MY_PN}/${MY_P}.tar.gz"
+
+LICENSE="MIT"
+SLOT="0"
+KEYWORDS="amd64 ~ppc ~ppc64 x86 ~x86-interix ~amd64-linux ~x86-linux ~x64-macos ~x86-macos ~sparc-solaris"
+IUSE="doc flup openid"
+
+RDEPEND="dev-python/setuptools[${PYTHON_USEDEP}]
+ flup? ( dev-python/flup[${PYTHON_USEDEP}] )
+ openid? ( dev-python/python-openid[${PYTHON_USEDEP}] )"
+DEPEND="${RDEPEND}
+ doc? ( dev-python/sphinx[${PYTHON_USEDEP}] )"
+
+S="${WORKDIR}/${MY_P}"
+
+python_prepare_all() {
+ # Disable failing tests.
+ rm -f tests/test_cgiapp.py
+ sed \
+ -e "s/test_find_file/_&/" \
+ -e "s/test_deep/_&/" \
+ -e "s/test_static_parser/_&/" \
+ -i tests/test_urlparser.py || die "sed failed"
+
+ # Remove a test that runs against the paste website.
+ rm -f tests/test_proxy.py
+
+ local PATCHES=(
+ "${FILESDIR}/${P}-fix-tests-for-pypy.patch"
+ )
+
+ distutils-r1_python_prepare_all
+}
+
+python_compile() {
+ distutils-r1_python_compile egg_info --egg-base "${BUILD_DIR}/lib"
+}
+
+python_compile_all() {
+ use doc && esetup.py build_sphinx
+}
+
+python_test() {
+ nosetests -P || die "Tests fail with ${EPYTHON}"
+}
+
+python_install() {
+ distutils-r1_python_install egg_info --egg-base "${BUILD_DIR}/lib"
+}
+
+python_install_all() {
+ use doc && local HTML_DOCS=( "${BUILD_DIR}"/sphinx/html/. )
+ distutils-r1_python_install_all
+}
diff --git a/dev-python/paste/paste-1.7.5.1-r2.ebuild b/dev-python/paste/paste-1.7.5.1-r2.ebuild
new file mode 100644
index 000000000000..1267db6af929
--- /dev/null
+++ b/dev-python/paste/paste-1.7.5.1-r2.ebuild
@@ -0,0 +1,82 @@
+# Copyright 1999-2015 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+EAPI=5
+
+PYTHON_COMPAT=( python2_7 )
+# notes wrt py-3 compatibility:
+# Debian ships paste for py3 using 2to3. Many tests fail when using such converted code and
+# the fact that the errors are sometimes nested inside paste indicate that the
+# result is indeed broken. Upstream is not responsive nor interested in porting.
+
+inherit distutils-r1
+
+MY_PN="Paste"
+MY_P="${MY_PN}-${PV}"
+
+DESCRIPTION="Tools for using a Web Server Gateway Interface stack"
+HOMEPAGE="http://pythonpaste.org http://pypi.python.org/pypi/Paste"
+SRC_URI="mirror://pypi/${MY_PN:0:1}/${MY_PN}/${MY_P}.tar.gz"
+
+LICENSE="MIT"
+SLOT="0"
+KEYWORDS="~amd64 ~ppc ~ppc64 ~x86 ~x86-interix ~amd64-linux ~x86-linux ~x64-macos ~x86-macos ~sparc-solaris"
+IUSE="doc flup openid"
+
+RDEPEND="dev-python/setuptools[${PYTHON_USEDEP}]
+ >=dev-python/tempita-0.5.2_pre20130828[${PYTHON_USEDEP}]
+ flup? ( dev-python/flup[${PYTHON_USEDEP}] )
+ openid? ( dev-python/python-openid[${PYTHON_USEDEP}] )"
+DEPEND="${RDEPEND}
+ doc? ( dev-python/sphinx[${PYTHON_USEDEP}] )"
+
+S="${WORKDIR}/${MY_P}"
+
+python_prepare_all() {
+ # Disable failing tests.
+ rm -f tests/test_cgiapp.py
+ sed \
+ -e "s/test_find_file/_&/" \
+ -e "s/test_deep/_&/" \
+ -e "s/test_static_parser/_&/" \
+ -i tests/test_urlparser.py || die "sed failed"
+
+ # Remove a test that runs against the paste website.
+ rm -f tests/test_proxy.py
+
+ local PATCHES=(
+ "${FILESDIR}/${P}-fix-tests-for-pypy.patch"
+ "${FILESDIR}/${P}-python27-lambda.patch"
+ "${FILESDIR}/${P}-unbundle-stdlib.patch"
+ "${FILESDIR}/${P}-unbundle-tempita.patch"
+ "${FILESDIR}/${P}-userdict.patch"
+ "${FILESDIR}/${P}-rfc822.patch"
+ "${FILESDIR}/${P}-email-mime.patch"
+ "${FILESDIR}/${P}-types.patch"
+ "${FILESDIR}/${P}-hmac.patch"
+ )
+
+ distutils-r1_python_prepare_all
+}
+
+python_compile() {
+ distutils-r1_python_compile egg_info --egg-base "${BUILD_DIR}/lib"
+}
+
+python_compile_all() {
+ use doc && esetup.py build_sphinx
+}
+
+python_test() {
+ nosetests -P || die "Tests fail with ${EPYTHON}"
+}
+
+python_install() {
+ distutils-r1_python_install egg_info --egg-base "${BUILD_DIR}/lib"
+}
+
+python_install_all() {
+ use doc && local HTML_DOCS=( "${BUILD_DIR}"/sphinx/html/. )
+ distutils-r1_python_install_all
+}
diff --git a/dev-python/paste/paste-2.0.2.ebuild b/dev-python/paste/paste-2.0.2.ebuild
new file mode 100644
index 000000000000..38c1b01e6d81
--- /dev/null
+++ b/dev-python/paste/paste-2.0.2.ebuild
@@ -0,0 +1,73 @@
+# Copyright 1999-2015 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+EAPI=5
+
+PYTHON_COMPAT=( python2_7 python3_{3,4} )
+
+inherit distutils-r1
+
+MY_PN="Paste"
+MY_P="${MY_PN}-${PV}"
+
+DESCRIPTION="Tools for using a Web Server Gateway Interface stack"
+HOMEPAGE="http://pythonpaste.org http://pypi.python.org/pypi/Paste"
+SRC_URI="mirror://pypi/${MY_PN:0:1}/${MY_PN}/${MY_P}.tar.gz"
+
+LICENSE="MIT"
+SLOT="0"
+KEYWORDS="amd64 ~ppc ~ppc64 x86 ~x86-interix ~amd64-linux ~x86-linux ~x64-macos ~x86-macos ~sparc-solaris"
+IUSE="doc flup openid"
+
+RDEPEND="
+ dev-python/six[${PYTHON_USEDEP}]
+ >=dev-python/tempita-0.5.2_pre20130828[${PYTHON_USEDEP}]
+ flup? ( dev-python/flup[$(python_gen_usedep 'python2*')] )
+ openid? ( dev-python/python-openid[$(python_gen_usedep 'python2*')] )"
+DEPEND="${RDEPEND}
+ dev-python/setuptools[${PYTHON_USEDEP}]
+ doc? ( dev-python/sphinx[${PYTHON_USEDEP}] )"
+
+S="${WORKDIR}/${MY_P}"
+
+python_prepare_all() {
+ # Disable failing tests.
+ rm -f tests/test_cgiapp.py || die
+ sed \
+ -e "s/test_find_file/_&/" \
+ -e "s/test_deep/_&/" \
+ -e "s/test_static_parser/_&/" \
+ -i tests/test_urlparser.py || die "sed failed"
+
+ # Remove a test that runs against the paste website.
+ rm -f tests/test_proxy.py || die
+
+ local PATCHES=(
+ "${FILESDIR}"/${PN}-1.7.5.1-fix-tests-for-pypy.patch
+ "${FILESDIR}"/${P}-unbundle-tempita.patch
+ )
+
+ distutils-r1_python_prepare_all
+}
+
+python_compile() {
+ distutils-r1_python_compile egg_info --egg-base "${BUILD_DIR}/lib"
+}
+
+python_compile_all() {
+ use doc && esetup.py build_sphinx
+}
+
+python_test() {
+ nosetests -P -v || die "Tests fail with ${EPYTHON}"
+}
+
+python_install() {
+ distutils-r1_python_install egg_info --egg-base "${BUILD_DIR}/lib"
+}
+
+python_install_all() {
+ use doc && local HTML_DOCS=( "${BUILD_DIR}"/sphinx/html/. )
+ distutils-r1_python_install_all
+}