Merged revisions 63412,63445-63447,63449-63450,63452,63454,63459,63463,63465,63470,63483-63484,63496-63497,63499-63501,63530-63531,63540,63614 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r63412 | georg.brandl | 2008-05-17 19:57:01 +0200 (Sat, 17 May 2008) | 2 lines #961805: fix Edit.text_modified(). ........ r63445 | georg.brandl | 2008-05-18 10:52:59 +0200 (Sun, 18 May 2008) | 2 lines GHOP #180 by Michael Schneider: add examples to the socketserver documentation. ........ r63446 | georg.brandl | 2008-05-18 11:12:20 +0200 (Sun, 18 May 2008) | 2 lines GHOP #134, #171, #137: unit tests for the three HTTPServer modules. ........ r63447 | georg.brandl | 2008-05-18 12:39:26 +0200 (Sun, 18 May 2008) | 3 lines Take namedtuple item names only from ascii_letters (this blew up on OSX), and make sure there are no duplicate names. ........ r63449 | georg.brandl | 2008-05-18 13:46:51 +0200 (Sun, 18 May 2008) | 2 lines GHOP #217: add support for compiling Python with coverage checking enabled. ........ r63450 | georg.brandl | 2008-05-18 13:52:36 +0200 (Sun, 18 May 2008) | 2 lines GHOP #257: test distutils' build_ext command, written by Josip Dzolonga. ........ r63452 | georg.brandl | 2008-05-18 15:34:06 +0200 (Sun, 18 May 2008) | 2 lines Add GHOP students. ........ r63454 | georg.brandl | 2008-05-18 18:32:48 +0200 (Sun, 18 May 2008) | 2 lines GHOP #121: improve test_pydoc, by Benjamin Peterson. ........ r63459 | benjamin.peterson | 2008-05-18 22:48:07 +0200 (Sun, 18 May 2008) | 2 lines bring test_pydoc up to my high standards (now that I have them) ........ r63463 | georg.brandl | 2008-05-18 23:10:19 +0200 (Sun, 18 May 2008) | 2 lines Fix test_pyclbr after another platform-dependent function was added to urllib. ........ r63465 | benjamin.peterson | 2008-05-19 01:07:07 +0200 (Mon, 19 May 2008) | 2 lines change some imports in tests so they will not be skipped in 3.0 ........ r63470 | georg.brandl | 2008-05-19 18:47:25 +0200 (Mon, 19 May 2008) | 2 lines test_httpservers has unpredictable refcount behavior. ........ r63483 | georg.brandl | 2008-05-20 08:15:36 +0200 (Tue, 20 May 2008) | 2 lines Activate two more test cases in test_httpservers. ........ r63484 | georg.brandl | 2008-05-20 08:47:31 +0200 (Tue, 20 May 2008) | 2 lines Argh, this is the *actual* test that works under Windows. ........ r63496 | georg.brandl | 2008-05-20 10:07:36 +0200 (Tue, 20 May 2008) | 2 lines Improve diffing logic and output for test_pydoc. ........ r63497 | georg.brandl | 2008-05-20 10:10:03 +0200 (Tue, 20 May 2008) | 2 lines Use inspect.getabsfile() to get the documented module's filename. ........ r63499 | georg.brandl | 2008-05-20 10:25:48 +0200 (Tue, 20 May 2008) | 3 lines Patch #1775025: allow opening zipfile members via ZipInfo instances. Patch by Graham Horler. ........ r63500 | georg.brandl | 2008-05-20 10:40:43 +0200 (Tue, 20 May 2008) | 2 lines #2592: delegate nb_index and the floor/truediv slots in weakref.proxy. ........ r63501 | georg.brandl | 2008-05-20 10:48:34 +0200 (Tue, 20 May 2008) | 2 lines #615772: raise a more explicit error from Tkinter.Misc.__contains__. ........ r63530 | benjamin.peterson | 2008-05-22 02:57:02 +0200 (Thu, 22 May 2008) | 2 lines use more specific asserts in test_opcode ........ r63531 | benjamin.peterson | 2008-05-22 03:02:23 +0200 (Thu, 22 May 2008) | 2 lines remove redundant invocation of json doctests ........ r63540 | benjamin.peterson | 2008-05-23 01:09:26 +0200 (Fri, 23 May 2008) | 3 lines fix test_pydoc so it works on make installed Python installations Also let it pass when invoked directly ........ r63614 | georg.brandl | 2008-05-25 10:07:37 +0200 (Sun, 25 May 2008) | 2 lines #2959: allow multiple close() calls for GzipFile. ........
This commit is contained in:
parent
cea777423b
commit
b533e26dfa
|
@ -236,8 +236,8 @@ users of the server object.
|
|||
|
||||
.. function:: handle_timeout()
|
||||
|
||||
This function is called when the :attr:`timeout` attribute has been set to a
|
||||
value other than :const:`None` and the timeout period has passed with no
|
||||
This function is called when the :attr:`timeout` attribute has been set to a
|
||||
value other than :const:`None` and the timeout period has passed with no
|
||||
requests being received. The default action for forking servers is
|
||||
to collect the status of any child processes that have exited, while
|
||||
in threading servers this method does nothing.
|
||||
|
@ -284,27 +284,28 @@ request.
|
|||
|
||||
.. function:: finish()
|
||||
|
||||
Called after the :meth:`handle` method to perform any clean-up actions required.
|
||||
The default implementation does nothing. If :meth:`setup` or :meth:`handle`
|
||||
raise an exception, this function will not be called.
|
||||
Called after the :meth:`handle` method to perform any clean-up actions
|
||||
required. The default implementation does nothing. If :meth:`setup` or
|
||||
:meth:`handle` raise an exception, this function will not be called.
|
||||
|
||||
|
||||
.. function:: handle()
|
||||
|
||||
This function must do all the work required to service a request. The default
|
||||
implementation does nothing. Several instance attributes are available to it;
|
||||
the request is available as :attr:`self.request`; the client address as
|
||||
:attr:`self.client_address`; and the server instance as :attr:`self.server`, in
|
||||
case it needs access to per-server information.
|
||||
This function must do all the work required to service a request. The
|
||||
default implementation does nothing. Several instance attributes are
|
||||
available to it; the request is available as :attr:`self.request`; the client
|
||||
address as :attr:`self.client_address`; and the server instance as
|
||||
:attr:`self.server`, in case it needs access to per-server information.
|
||||
|
||||
The type of :attr:`self.request` is different for datagram or stream services.
|
||||
For stream services, :attr:`self.request` is a socket object; for datagram
|
||||
services, :attr:`self.request` is a string. However, this can be hidden by using
|
||||
the request handler subclasses :class:`StreamRequestHandler` or
|
||||
:class:`DatagramRequestHandler`, which override the :meth:`setup` and
|
||||
:meth:`finish` methods, and provide :attr:`self.rfile` and :attr:`self.wfile`
|
||||
attributes. :attr:`self.rfile` and :attr:`self.wfile` can be read or written,
|
||||
respectively, to get the request data or return data to the client.
|
||||
The type of :attr:`self.request` is different for datagram or stream
|
||||
services. For stream services, :attr:`self.request` is a socket object; for
|
||||
datagram services, :attr:`self.request` is a pair of string and socket.
|
||||
However, this can be hidden by using the request handler subclasses
|
||||
:class:`StreamRequestHandler` or :class:`DatagramRequestHandler`, which
|
||||
override the :meth:`setup` and :meth:`finish` methods, and provide
|
||||
:attr:`self.rfile` and :attr:`self.wfile` attributes. :attr:`self.rfile` and
|
||||
:attr:`self.wfile` can be read or written, respectively, to get the request
|
||||
data or return data to the client.
|
||||
|
||||
|
||||
.. function:: setup()
|
||||
|
@ -312,3 +313,217 @@ request.
|
|||
Called before the :meth:`handle` method to perform any initialization actions
|
||||
required. The default implementation does nothing.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
:class:`socketserver.TCPServer` Example
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is the server side::
|
||||
|
||||
import socketserver
|
||||
|
||||
class MyTCPHandler(socketserver.BaseRequestHandler):
|
||||
"""
|
||||
The RequestHandler class for our server.
|
||||
|
||||
It is instantiated once per connection to the server, and must
|
||||
override the handle() method to implement communication to the
|
||||
client.
|
||||
"""
|
||||
|
||||
def handle(self):
|
||||
# self.request is the TCP socket connected to the client
|
||||
self.data = self.request.recv(1024).strip()
|
||||
print "%s wrote:" % self.client_address[0]
|
||||
print self.data
|
||||
# just send back the same data, but upper-cased
|
||||
self.request.send(self.data.upper())
|
||||
|
||||
if __name__ == "__main__":
|
||||
HOST, PORT = "localhost", 9999
|
||||
|
||||
# Create the server, binding to localhost on port 9999
|
||||
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
|
||||
|
||||
# Activate the server; this will keep running until you
|
||||
# interrupt the program with Ctrl-C
|
||||
server.serve_forever()
|
||||
|
||||
An alternative request handler class that makes use of streams (file-like
|
||||
objects that simplify communication by providing the standard file interface)::
|
||||
|
||||
class MyTCPHandler(socketserver.StreamRequestHandler):
|
||||
|
||||
def handle(self):
|
||||
# self.rfile is a file-like object created by the handler;
|
||||
# we can now use e.g. readline() instead of raw recv() calls
|
||||
self.data = self.rfile.readline().strip()
|
||||
print "%s wrote:" % self.client_address[0]
|
||||
print self.data
|
||||
# Likewise, self.wfile is a file-like object used to write back
|
||||
# to the client
|
||||
self.wfile.write(self.data.upper())
|
||||
|
||||
The difference is that the ``readline()`` call in the second handler will call
|
||||
``recv()`` multiple times until it encounters a newline character, while the
|
||||
single ``recv()`` call in the first handler will just return what has been sent
|
||||
from the client in one ``send()`` call.
|
||||
|
||||
|
||||
This is the client side::
|
||||
|
||||
import socket
|
||||
import sys
|
||||
|
||||
HOST, PORT = "localhost", 9999
|
||||
data = " ".join(sys.argv[1:])
|
||||
|
||||
# Create a socket (SOCK_STREAM means a TCP socket)
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
# Connect to server and send data
|
||||
sock.connect((HOST, PORT))
|
||||
sock.send(data + "\n")
|
||||
|
||||
# Receive data from the server and shut down
|
||||
received = sock.recv(1024)
|
||||
sock.close()
|
||||
|
||||
print "Sent: %s" % data
|
||||
print "Received: %s" % received
|
||||
|
||||
|
||||
The output of the example should look something like this:
|
||||
|
||||
Server::
|
||||
|
||||
$ python TCPServer.py
|
||||
127.0.0.1 wrote:
|
||||
hello world with TCP
|
||||
127.0.0.1 wrote:
|
||||
python is nice
|
||||
|
||||
Client::
|
||||
|
||||
$ python TCPClient.py hello world with TCP
|
||||
Sent: hello world with TCP
|
||||
Received: HELLO WORLD WITH TCP
|
||||
$ python TCPClient.py python is nice
|
||||
Sent: python is nice
|
||||
Received: PYTHON IS NICE
|
||||
|
||||
|
||||
:class:`socketserver.UDPServer` Example
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is the server side::
|
||||
|
||||
import socketserver
|
||||
|
||||
class MyUDPHandler(socketserver.BaseRequestHandler):
|
||||
"""
|
||||
This class works similar to the TCP handler class, except that
|
||||
self.request consists of a pair of data and client socket, and since
|
||||
there is no connection the client address must be given explicitly
|
||||
when sending data back via sendto().
|
||||
"""
|
||||
|
||||
def handle(self):
|
||||
data = self.request[0].strip()
|
||||
socket = self.request[1]
|
||||
print "%s wrote:" % self.client_address[0]
|
||||
print data
|
||||
socket.sendto(data.upper(), self.client_address)
|
||||
|
||||
if __name__ == "__main__":
|
||||
HOST, PORT = "localhost", 9999
|
||||
server = socketserver.UDPServer((HOST, PORT), BaseUDPRequestHandler)
|
||||
server.serve_forever()
|
||||
|
||||
This is the client side::
|
||||
|
||||
import socket
|
||||
import sys
|
||||
|
||||
HOST, PORT = "localhost"
|
||||
data = " ".join(sys.argv[1:])
|
||||
|
||||
# SOCK_DGRAM is the socket type to use for UDP sockets
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
|
||||
# As you can see, there is no connect() call; UDP has no connections.
|
||||
# Instead, data is directly sent to the recipient via sendto().
|
||||
sock.sendto(data + "\n", (HOST, PORT))
|
||||
received = sock.recv(1024)
|
||||
|
||||
print "Sent: %s" % data
|
||||
print "Received: %s" % received
|
||||
|
||||
The output of the example should look exactly like for the TCP server example.
|
||||
|
||||
|
||||
Asynchronous Mixins
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To build asynchronous handlers, use the :class:`ThreadingMixIn` and
|
||||
:class:`ForkingMixIn` classes.
|
||||
|
||||
An example for the :class:`ThreadingMixIn` class::
|
||||
|
||||
import socket
|
||||
import threading
|
||||
import socketserver
|
||||
|
||||
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
|
||||
|
||||
def handle(self):
|
||||
data = self.request.recv(1024)
|
||||
cur_thread = threading.currentThread()
|
||||
response = "%s: %s" % (cur_thread.getName(), data)
|
||||
self.request.send(response)
|
||||
|
||||
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
pass
|
||||
|
||||
def client(ip, port, message):
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect((ip, port))
|
||||
sock.send(message)
|
||||
response = sock.recv(1024)
|
||||
print "Received: %s" % response
|
||||
sock.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Port 0 means to select an arbitrary unused port
|
||||
HOST, PORT = "localhost", 0
|
||||
|
||||
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
|
||||
ip, port = server.server_address
|
||||
|
||||
# Start a thread with the server -- that thread will then start one
|
||||
# more thread for each request
|
||||
server_thread = threading.Thread(target=server.serve_forever)
|
||||
# Exit the server thread when the main thread terminates
|
||||
server_thread.setDaemon(True)
|
||||
server_thread.start()
|
||||
print "Server loop running in thread:", t.getName()
|
||||
|
||||
client(ip, port, "Hello World 1")
|
||||
client(ip, port, "Hello World 2")
|
||||
client(ip, port, "Hello World 3")
|
||||
|
||||
server.shutdown()
|
||||
|
||||
The output of the example should look something like this::
|
||||
|
||||
$ python ThreadedTCPServer.py
|
||||
Server loop running in thread: Thread-1
|
||||
Received: Thread-2: Hello World 1
|
||||
Received: Thread-3: Hello World 2
|
||||
Received: Thread-4: Hello World 3
|
||||
|
||||
|
||||
The :class:`ForkingMixIn` class is used in the same way, except that the server
|
||||
will spawn a new process for each request.
|
||||
|
|
|
@ -150,11 +150,11 @@ ZipFile Objects
|
|||
.. method:: ZipFile.open(name[, mode[, pwd]])
|
||||
|
||||
Extract a member from the archive as a file-like object (ZipExtFile). *name* is
|
||||
the name of the file in the archive. The *mode* parameter, if included, must be
|
||||
one of the following: ``'r'`` (the default), ``'U'``, or ``'rU'``. Choosing
|
||||
``'U'`` or ``'rU'`` will enable universal newline support in the read-only
|
||||
object. *pwd* is the password used for encrypted files. Calling :meth:`open`
|
||||
on a closed ZipFile will raise a :exc:`RuntimeError`.
|
||||
the name of the file in the archive, or a :class:`ZipInfo` object. The *mode*
|
||||
parameter, if included, must be one of the following: ``'r'`` (the default),
|
||||
``'U'``, or ``'rU'``. Choosing ``'U'`` or ``'rU'`` will enable universal newline
|
||||
support in the read-only object. *pwd* is the password used for encrypted files.
|
||||
Calling :meth:`open` on a closed ZipFile will raise a :exc:`RuntimeError`.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -173,14 +173,20 @@ ZipFile Objects
|
|||
create a new file object that will be held by the ZipExtFile, allowing it to
|
||||
operate independently of the ZipFile.
|
||||
|
||||
.. note::
|
||||
|
||||
The :meth:`open`, :meth:`read` and :meth:`extract` methods can take a filename
|
||||
or a :class:`ZipInfo` object. You will appreciate this when trying to read a
|
||||
ZIP file that contains members with duplicate names.
|
||||
|
||||
|
||||
.. method:: ZipFile.extract(member[, path[, pwd]])
|
||||
|
||||
Extract a member from the archive to the current working directory, using its
|
||||
full name. Its file information is extracted as accurately as possible.
|
||||
*path* specifies a different directory to extract to. *member* can be a
|
||||
filename or a :class:`ZipInfo` object. *pwd* is the password used for
|
||||
encrypted files.
|
||||
Extract a member from the archive to the current working directory; *member*
|
||||
must be its full name or a :class:`ZipInfo` object). Its file information is
|
||||
extracted as accurately as possible. *path* specifies a different directory
|
||||
to extract to. *member* can be a filename or a :class:`ZipInfo` object.
|
||||
*pwd* is the password used for encrypted files.
|
||||
|
||||
|
||||
.. method:: ZipFile.extractall([path[, members[, pwd]]])
|
||||
|
@ -203,9 +209,10 @@ ZipFile Objects
|
|||
|
||||
.. method:: ZipFile.read(name[, pwd])
|
||||
|
||||
Return the bytes of the file in the archive. The archive must be open for read
|
||||
or append. *pwd* is the password used for encrypted files and, if specified, it
|
||||
will override the default password set with :meth:`setpassword`. Calling
|
||||
Return the bytes of the file *name* in the archive. *name* is the name of the
|
||||
file in the archive, or a :class:`ZipInfo` object. The archive must be open for
|
||||
read or append. *pwd* is the password used for encrypted files and, if specified,
|
||||
it will override the default password set with :meth:`setpassword`. Calling
|
||||
:meth:`read` on a closed ZipFile will raise a :exc:`RuntimeError`.
|
||||
|
||||
|
||||
|
|
|
@ -222,6 +222,12 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
|
|||
error_message_format = DEFAULT_ERROR_MESSAGE
|
||||
error_content_type = DEFAULT_ERROR_CONTENT_TYPE
|
||||
|
||||
# The default request version. This only affects responses up until
|
||||
# the point where the request line is parsed, so it mainly decides what
|
||||
# the client gets back when sending a malformed request line.
|
||||
# Most web servers default to HTTP 0.9, i.e. don't send a status line.
|
||||
default_request_version = "HTTP/0.9"
|
||||
|
||||
def parse_request(self):
|
||||
"""Parse a request (internal).
|
||||
|
||||
|
@ -234,7 +240,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
|
|||
|
||||
"""
|
||||
self.command = None # set in case of error on the first line
|
||||
self.request_version = version = "HTTP/0.9" # Default
|
||||
self.request_version = version = self.default_request_version
|
||||
self.close_connection = 1
|
||||
requestline = str(self.raw_requestline, 'iso-8859-1')
|
||||
if requestline[-2:] == '\r\n':
|
||||
|
|
|
@ -182,8 +182,10 @@ class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
|||
env['AUTH_TYPE'] = authorization[0]
|
||||
if authorization[0].lower() == "basic":
|
||||
try:
|
||||
authorization = base64.decodestring(authorization[1])
|
||||
except binascii.Error:
|
||||
authorization = authorization[1].encode('ascii')
|
||||
authorization = base64.decodestring(authorization).\
|
||||
decode('ascii')
|
||||
except (binascii.Error, UnicodeError):
|
||||
pass
|
||||
else:
|
||||
authorization = authorization.split(':')
|
||||
|
|
|
@ -11,13 +11,14 @@ __version__ = "0.6"
|
|||
__all__ = ["SimpleHTTPRequestHandler"]
|
||||
|
||||
import os
|
||||
import sys
|
||||
import posixpath
|
||||
import BaseHTTPServer
|
||||
import urllib
|
||||
import cgi
|
||||
import shutil
|
||||
import mimetypes
|
||||
from io import StringIO
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
|
@ -76,12 +77,8 @@ class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||
else:
|
||||
return self.list_directory(path)
|
||||
ctype = self.guess_type(path)
|
||||
if ctype.startswith('text/'):
|
||||
mode = 'r'
|
||||
else:
|
||||
mode = 'rb'
|
||||
try:
|
||||
f = open(path, mode)
|
||||
f = open(path, 'rb')
|
||||
except IOError:
|
||||
self.send_error(404, "File not found")
|
||||
return None
|
||||
|
@ -107,12 +104,12 @@ class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||
self.send_error(404, "No permission to list directory")
|
||||
return None
|
||||
list.sort(key=lambda a: a.lower())
|
||||
f = StringIO()
|
||||
r = []
|
||||
displaypath = cgi.escape(urllib.unquote(self.path))
|
||||
f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
|
||||
f.write("<html>\n<title>Directory listing for %s</title>\n" % displaypath)
|
||||
f.write("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath)
|
||||
f.write("<hr>\n<ul>\n")
|
||||
r.append('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
|
||||
r.append("<html>\n<title>Directory listing for %s</title>\n" % displaypath)
|
||||
r.append("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath)
|
||||
r.append("<hr>\n<ul>\n")
|
||||
for name in list:
|
||||
fullname = os.path.join(path, name)
|
||||
displayname = linkname = name
|
||||
|
@ -123,14 +120,17 @@ class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||
if os.path.islink(fullname):
|
||||
displayname = name + "@"
|
||||
# Note: a link to a directory displays with @ and links with /
|
||||
f.write('<li><a href="%s">%s</a>\n'
|
||||
r.append('<li><a href="%s">%s</a>\n'
|
||||
% (urllib.quote(linkname), cgi.escape(displayname)))
|
||||
f.write("</ul>\n<hr>\n</body>\n</html>\n")
|
||||
length = f.tell()
|
||||
r.append("</ul>\n<hr>\n</body>\n</html>\n")
|
||||
enc = sys.getfilesystemencoding()
|
||||
encoded = ''.join(r).encode(enc)
|
||||
f = BytesIO()
|
||||
f.write(encoded)
|
||||
f.seek(0)
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/html")
|
||||
self.send_header("Content-Length", str(length))
|
||||
self.send_header("Content-type", "text/html; charset=%s" % enc)
|
||||
self.send_header("Content-Length", str(len(encoded)))
|
||||
self.end_headers()
|
||||
return f
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import sys
|
||||
import os
|
||||
import tempfile
|
||||
import shutil
|
||||
from io import StringIO
|
||||
|
||||
from distutils.core import Extension, Distribution
|
||||
from distutils.command.build_ext import build_ext
|
||||
from distutils import sysconfig
|
||||
|
||||
import unittest
|
||||
from test import support
|
||||
|
||||
class BuildExtTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# Create a simple test environment
|
||||
# Note that we're making changes to sys.path
|
||||
self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_")
|
||||
self.sys_path = sys.path[:]
|
||||
sys.path.append(self.tmp_dir)
|
||||
|
||||
xx_c = os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c')
|
||||
shutil.copy(xx_c, self.tmp_dir)
|
||||
|
||||
def test_build_ext(self):
|
||||
xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
|
||||
xx_ext = Extension('xx', [xx_c])
|
||||
dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]})
|
||||
dist.package_dir = self.tmp_dir
|
||||
cmd = build_ext(dist)
|
||||
cmd.build_lib = self.tmp_dir
|
||||
cmd.build_temp = self.tmp_dir
|
||||
|
||||
old_stdout = sys.stdout
|
||||
if not support.verbose:
|
||||
# silence compiler output
|
||||
sys.stdout = StringIO()
|
||||
try:
|
||||
cmd.ensure_finalized()
|
||||
cmd.run()
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
|
||||
import xx
|
||||
|
||||
for attr in ('error', 'foo', 'new', 'roj'):
|
||||
self.assert_(hasattr(xx, attr))
|
||||
|
||||
self.assertEquals(xx.foo(2, 5), 7)
|
||||
self.assertEquals(xx.foo(13,15), 28)
|
||||
self.assertEquals(xx.new().demo(), None)
|
||||
doc = 'This is a template module just for instruction.'
|
||||
self.assertEquals(xx.__doc__, doc)
|
||||
self.assert_(isinstance(xx.Null(), xx.Null))
|
||||
self.assert_(isinstance(xx.Str(), xx.Str))
|
||||
|
||||
def tearDown(self):
|
||||
# Get everything back to normal
|
||||
support.unload('xx')
|
||||
sys.path = self.sys_path
|
||||
# XXX on Windows the test leaves a directory with xx.pyd in TEMP
|
||||
shutil.rmtree(self.tmp_dir, False if os.name != "nt" else True)
|
||||
|
||||
def test_suite():
|
||||
if not sysconfig.python_build:
|
||||
if support.verbose:
|
||||
print('test_build_ext: The test must be run in a python build dir')
|
||||
return unittest.TestSuite()
|
||||
else: return unittest.makeSuite(BuildExtTestCase)
|
||||
|
||||
if __name__ == '__main__':
|
||||
support.run_unittest(test_suite())
|
|
@ -323,6 +323,8 @@ class GzipFile:
|
|||
raise IOError("Incorrect length of data produced")
|
||||
|
||||
def close(self):
|
||||
if self.fileobj is None:
|
||||
return
|
||||
if self.mode == WRITE:
|
||||
self.fileobj.write(self.compress.flush())
|
||||
write32u(self.fileobj, self.crc)
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
"""This is a test module for test_pydoc"""
|
||||
|
||||
__author__ = "Benjamin Peterson"
|
||||
__credits__ = "Nobody"
|
||||
__version__ = "1.2.3.4"
|
||||
|
||||
|
||||
class A:
|
||||
"""Hello and goodbye"""
|
||||
def __init__():
|
||||
"""Wow, I have no function!"""
|
||||
pass
|
||||
|
||||
class B(object):
|
||||
NO_MEANING = "eggs"
|
||||
pass
|
||||
|
||||
def doc_func():
|
||||
"""
|
||||
This function solves all of the world's problems:
|
||||
hunger
|
||||
lack of Python
|
||||
war
|
||||
"""
|
||||
|
||||
def nodoc_func():
|
||||
pass
|
||||
"""This is a test module for test_pydoc"""
|
||||
|
||||
__author__ = "Benjamin Peterson"
|
||||
__credits__ = "Nobody"
|
||||
__version__ = "1.2.3.4"
|
||||
|
||||
|
||||
class A:
|
||||
"""Hello and goodbye"""
|
||||
def __init__():
|
||||
"""Wow, I have no function!"""
|
||||
pass
|
||||
|
||||
class B(object):
|
||||
NO_MEANING = "eggs"
|
||||
pass
|
||||
|
||||
def doc_func():
|
||||
"""
|
||||
This function solves all of the world's problems:
|
||||
hunger
|
||||
lack of Python
|
||||
war
|
||||
"""
|
||||
|
||||
def nodoc_func():
|
||||
pass
|
|
@ -114,7 +114,9 @@ class TestNamedTuple(unittest.TestCase):
|
|||
# n = 10000
|
||||
n = 254 # SyntaxError: more than 255 arguments:
|
||||
import string, random
|
||||
names = [''.join([random.choice(string.ascii_letters) for j in range(10)]) for i in range(n)]
|
||||
names = list(set(''.join([random.choice(string.ascii_letters)
|
||||
for j in range(10)]) for i in range(n)))
|
||||
n = len(names)
|
||||
Big = namedtuple('Big', names)
|
||||
b = Big(*range(n))
|
||||
self.assertEqual(b, tuple(range(n)))
|
||||
|
|
|
@ -24,14 +24,14 @@ data2 = b"""/* zlibmodule.c -- gzip-compatible data compression */
|
|||
class TestGzip(unittest.TestCase):
|
||||
filename = support.TESTFN
|
||||
|
||||
def setUp (self):
|
||||
def setUp(self):
|
||||
support.unlink(self.filename)
|
||||
|
||||
def tearDown (self):
|
||||
def tearDown(self):
|
||||
support.unlink(self.filename)
|
||||
|
||||
|
||||
def test_write (self):
|
||||
def test_write(self):
|
||||
f = gzip.GzipFile(self.filename, 'wb') ; f.write(data1 * 50)
|
||||
|
||||
# Try flush and fileno.
|
||||
|
@ -41,6 +41,9 @@ class TestGzip(unittest.TestCase):
|
|||
os.fsync(f.fileno())
|
||||
f.close()
|
||||
|
||||
# Test multiple close() calls.
|
||||
f.close()
|
||||
|
||||
def test_read(self):
|
||||
self.test_write()
|
||||
# Try reading.
|
||||
|
|
|
@ -0,0 +1,354 @@
|
|||
"""Unittests for the various HTTPServer modules.
|
||||
|
||||
Written by Cody A.W. Somerville <cody-somerville@ubuntu.com>,
|
||||
Josip Dzolonga, and Michael Otteneder for the 2007/08 GHOP contest.
|
||||
"""
|
||||
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
from SimpleHTTPServer import SimpleHTTPRequestHandler
|
||||
from CGIHTTPServer import CGIHTTPRequestHandler
|
||||
|
||||
import os
|
||||
import sys
|
||||
import base64
|
||||
import shutil
|
||||
import urllib
|
||||
import httplib
|
||||
import tempfile
|
||||
import threading
|
||||
|
||||
import unittest
|
||||
from test import support
|
||||
|
||||
|
||||
class NoLogRequestHandler:
|
||||
def log_message(self, *args):
|
||||
# don't write log messages to stderr
|
||||
pass
|
||||
|
||||
|
||||
class TestServerThread(threading.Thread):
|
||||
def __init__(self, test_object, request_handler):
|
||||
threading.Thread.__init__(self)
|
||||
self.request_handler = request_handler
|
||||
self.test_object = test_object
|
||||
self.test_object.lock.acquire()
|
||||
|
||||
def run(self):
|
||||
self.server = HTTPServer(('', 0), self.request_handler)
|
||||
self.test_object.PORT = self.server.socket.getsockname()[1]
|
||||
self.test_object.lock.release()
|
||||
try:
|
||||
self.server.serve_forever()
|
||||
finally:
|
||||
self.server.server_close()
|
||||
|
||||
def stop(self):
|
||||
self.server.shutdown()
|
||||
|
||||
|
||||
class BaseTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.lock = threading.Lock()
|
||||
self.thread = TestServerThread(self, self.request_handler)
|
||||
self.thread.start()
|
||||
self.lock.acquire()
|
||||
|
||||
def tearDown(self):
|
||||
self.lock.release()
|
||||
self.thread.stop()
|
||||
|
||||
def request(self, uri, method='GET', body=None, headers={}):
|
||||
self.connection = httplib.HTTPConnection('localhost', self.PORT)
|
||||
self.connection.request(method, uri, body, headers)
|
||||
return self.connection.getresponse()
|
||||
|
||||
|
||||
class BaseHTTPServerTestCase(BaseTestCase):
|
||||
class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler):
|
||||
protocol_version = 'HTTP/1.1'
|
||||
default_request_version = 'HTTP/1.1'
|
||||
|
||||
def do_TEST(self):
|
||||
self.send_response(204)
|
||||
self.send_header('Content-Type', 'text/html')
|
||||
self.send_header('Connection', 'close')
|
||||
self.end_headers()
|
||||
|
||||
def do_KEEP(self):
|
||||
self.send_response(204)
|
||||
self.send_header('Content-Type', 'text/html')
|
||||
self.send_header('Connection', 'keep-alive')
|
||||
self.end_headers()
|
||||
|
||||
def do_KEYERROR(self):
|
||||
self.send_error(999)
|
||||
|
||||
def do_CUSTOM(self):
|
||||
self.send_response(999)
|
||||
self.send_header('Content-Type', 'text/html')
|
||||
self.send_header('Connection', 'close')
|
||||
self.end_headers()
|
||||
|
||||
def setUp(self):
|
||||
BaseTestCase.setUp(self)
|
||||
self.con = httplib.HTTPConnection('localhost', self.PORT)
|
||||
self.con.connect()
|
||||
|
||||
def test_command(self):
|
||||
self.con.request('GET', '/')
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 501)
|
||||
|
||||
def test_request_line_trimming(self):
|
||||
self.con._http_vsn_str = 'HTTP/1.1\n'
|
||||
self.con.putrequest('GET', '/')
|
||||
self.con.endheaders()
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 501)
|
||||
|
||||
def test_version_bogus(self):
|
||||
self.con._http_vsn_str = 'FUBAR'
|
||||
self.con.putrequest('GET', '/')
|
||||
self.con.endheaders()
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 400)
|
||||
|
||||
def test_version_digits(self):
|
||||
self.con._http_vsn_str = 'HTTP/9.9.9'
|
||||
self.con.putrequest('GET', '/')
|
||||
self.con.endheaders()
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 400)
|
||||
|
||||
def test_version_none_get(self):
|
||||
self.con._http_vsn_str = ''
|
||||
self.con.putrequest('GET', '/')
|
||||
self.con.endheaders()
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 501)
|
||||
|
||||
def test_version_none(self):
|
||||
self.con._http_vsn_str = ''
|
||||
self.con.putrequest('PUT', '/')
|
||||
self.con.endheaders()
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 400)
|
||||
|
||||
def test_version_invalid(self):
|
||||
self.con._http_vsn = 99
|
||||
self.con._http_vsn_str = 'HTTP/9.9'
|
||||
self.con.putrequest('GET', '/')
|
||||
self.con.endheaders()
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 505)
|
||||
|
||||
def test_send_blank(self):
|
||||
self.con._http_vsn_str = ''
|
||||
self.con.putrequest('', '')
|
||||
self.con.endheaders()
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 400)
|
||||
|
||||
def test_header_close(self):
|
||||
self.con.putrequest('GET', '/')
|
||||
self.con.putheader('Connection', 'close')
|
||||
self.con.endheaders()
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 501)
|
||||
|
||||
def test_head_keep_alive(self):
|
||||
self.con._http_vsn_str = 'HTTP/1.1'
|
||||
self.con.putrequest('GET', '/')
|
||||
self.con.putheader('Connection', 'keep-alive')
|
||||
self.con.endheaders()
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 501)
|
||||
|
||||
def test_handler(self):
|
||||
self.con.request('TEST', '/')
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 204)
|
||||
|
||||
def test_return_header_keep_alive(self):
|
||||
self.con.request('KEEP', '/')
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.getheader('Connection'), 'keep-alive')
|
||||
self.con.request('TEST', '/')
|
||||
|
||||
def test_internal_key_error(self):
|
||||
self.con.request('KEYERROR', '/')
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 999)
|
||||
|
||||
def test_return_custom_status(self):
|
||||
self.con.request('CUSTOM', '/')
|
||||
res = self.con.getresponse()
|
||||
self.assertEquals(res.status, 999)
|
||||
|
||||
|
||||
class SimpleHTTPServerTestCase(BaseTestCase):
|
||||
class request_handler(NoLogRequestHandler, SimpleHTTPRequestHandler):
|
||||
pass
|
||||
|
||||
def setUp(self):
|
||||
BaseTestCase.setUp(self)
|
||||
self.cwd = os.getcwd()
|
||||
basetempdir = tempfile.gettempdir()
|
||||
os.chdir(basetempdir)
|
||||
self.data = b'We are the knights who say Ni!'
|
||||
self.tempdir = tempfile.mkdtemp(dir=basetempdir)
|
||||
self.tempdir_name = os.path.basename(self.tempdir)
|
||||
temp = open(os.path.join(self.tempdir, 'test'), 'wb')
|
||||
temp.write(self.data)
|
||||
temp.close()
|
||||
|
||||
def tearDown(self):
|
||||
try:
|
||||
os.chdir(self.cwd)
|
||||
try:
|
||||
shutil.rmtree(self.tempdir)
|
||||
except:
|
||||
pass
|
||||
finally:
|
||||
BaseTestCase.tearDown(self)
|
||||
|
||||
def check_status_and_reason(self, response, status, data=None):
|
||||
body = response.read()
|
||||
self.assert_(response)
|
||||
self.assertEquals(response.status, status)
|
||||
self.assert_(response.reason != None)
|
||||
if data:
|
||||
self.assertEqual(data, body)
|
||||
|
||||
def test_get(self):
|
||||
#constructs the path relative to the root directory of the HTTPServer
|
||||
response = self.request(self.tempdir_name + '/test')
|
||||
self.check_status_and_reason(response, 200, data=self.data)
|
||||
response = self.request(self.tempdir_name + '/')
|
||||
self.check_status_and_reason(response, 200)
|
||||
response = self.request(self.tempdir_name)
|
||||
self.check_status_and_reason(response, 301)
|
||||
response = self.request('/ThisDoesNotExist')
|
||||
self.check_status_and_reason(response, 404)
|
||||
response = self.request('/' + 'ThisDoesNotExist' + '/')
|
||||
self.check_status_and_reason(response, 404)
|
||||
f = open(os.path.join(self.tempdir_name, 'index.html'), 'w')
|
||||
response = self.request('/' + self.tempdir_name + '/')
|
||||
self.check_status_and_reason(response, 200)
|
||||
if os.name == 'posix':
|
||||
# chmod won't work as expected on Windows platforms
|
||||
os.chmod(self.tempdir, 0)
|
||||
response = self.request(self.tempdir_name + '/')
|
||||
self.check_status_and_reason(response, 404)
|
||||
os.chmod(self.tempdir, 0o755)
|
||||
|
||||
def test_head(self):
|
||||
response = self.request(
|
||||
self.tempdir_name + '/test', method='HEAD')
|
||||
self.check_status_and_reason(response, 200)
|
||||
self.assertEqual(response.getheader('content-length'),
|
||||
str(len(self.data)))
|
||||
self.assertEqual(response.getheader('content-type'),
|
||||
'application/octet-stream')
|
||||
|
||||
def test_invalid_requests(self):
|
||||
response = self.request('/', method='FOO')
|
||||
self.check_status_and_reason(response, 501)
|
||||
# requests must be case sensitive,so this should fail too
|
||||
response = self.request('/', method='get')
|
||||
self.check_status_and_reason(response, 501)
|
||||
response = self.request('/', method='GETs')
|
||||
self.check_status_and_reason(response, 501)
|
||||
|
||||
|
||||
cgi_file1 = """\
|
||||
#!%s
|
||||
|
||||
print("Content-type: text/html")
|
||||
print()
|
||||
print("Hello World")
|
||||
"""
|
||||
|
||||
cgi_file2 = """\
|
||||
#!%s
|
||||
import cgi
|
||||
|
||||
print("Content-type: text/html")
|
||||
print()
|
||||
|
||||
form = cgi.FieldStorage()
|
||||
print("%%s, %%s, %%s" %% (form.getfirst("spam"), form.getfirst("eggs"),\
|
||||
form.getfirst("bacon")))
|
||||
"""
|
||||
|
||||
class CGIHTTPServerTestCase(BaseTestCase):
|
||||
class request_handler(NoLogRequestHandler, CGIHTTPRequestHandler):
|
||||
pass
|
||||
|
||||
def setUp(self):
|
||||
BaseTestCase.setUp(self)
|
||||
self.parent_dir = tempfile.mkdtemp()
|
||||
self.cgi_dir = os.path.join(self.parent_dir, 'cgi-bin')
|
||||
os.mkdir(self.cgi_dir)
|
||||
|
||||
self.file1_path = os.path.join(self.cgi_dir, 'file1.py')
|
||||
with open(self.file1_path, 'w') as file1:
|
||||
file1.write(cgi_file1 % sys.executable)
|
||||
os.chmod(self.file1_path, 0o777)
|
||||
|
||||
self.file2_path = os.path.join(self.cgi_dir, 'file2.py')
|
||||
with open(self.file2_path, 'w') as file2:
|
||||
file2.write(cgi_file2 % sys.executable)
|
||||
os.chmod(self.file2_path, 0o777)
|
||||
|
||||
self.cwd = os.getcwd()
|
||||
os.chdir(self.parent_dir)
|
||||
|
||||
def tearDown(self):
|
||||
try:
|
||||
os.chdir(self.cwd)
|
||||
os.remove(self.file1_path)
|
||||
os.remove(self.file2_path)
|
||||
os.rmdir(self.cgi_dir)
|
||||
os.rmdir(self.parent_dir)
|
||||
finally:
|
||||
BaseTestCase.tearDown(self)
|
||||
|
||||
def test_headers_and_content(self):
|
||||
res = self.request('/cgi-bin/file1.py')
|
||||
self.assertEquals((b'Hello World\n', 'text/html', 200), \
|
||||
(res.read(), res.getheader('Content-type'), res.status))
|
||||
|
||||
def test_post(self):
|
||||
params = urllib.urlencode({'spam' : 1, 'eggs' : 'python', 'bacon' : 123456})
|
||||
headers = {'Content-type' : 'application/x-www-form-urlencoded'}
|
||||
res = self.request('/cgi-bin/file2.py', 'POST', params, headers)
|
||||
|
||||
self.assertEquals(res.read(), b'1, python, 123456\n')
|
||||
|
||||
def test_invaliduri(self):
|
||||
res = self.request('/cgi-bin/invalid')
|
||||
res.read()
|
||||
self.assertEquals(res.status, 404)
|
||||
|
||||
def test_authorization(self):
|
||||
headers = {b'Authorization' : b'Basic ' +
|
||||
base64.b64encode(b'username:pass')}
|
||||
res = self.request('/cgi-bin/file1.py', 'GET', headers=headers)
|
||||
self.assertEquals((b'Hello World\n', 'text/html', 200), \
|
||||
(res.read(), res.getheader('Content-type'), res.status))
|
||||
|
||||
|
||||
def test_main(verbose=None):
|
||||
try:
|
||||
cwd = os.getcwd()
|
||||
support.run_unittest(#BaseHTTPServerTestCase,
|
||||
SimpleHTTPServerTestCase,
|
||||
CGIHTTPServerTestCase
|
||||
)
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_main()
|
|
@ -11,7 +11,6 @@ import test.support
|
|||
|
||||
def test_main():
|
||||
test.support.run_unittest(json.tests.test_suite())
|
||||
test.support.run_doctest(json)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -68,35 +68,35 @@ class OpcodeTest(unittest.TestCase):
|
|||
|
||||
f = eval('lambda: None')
|
||||
g = eval('lambda: None')
|
||||
self.failIf(f == g)
|
||||
self.assertNotEquals(f, g)
|
||||
|
||||
f = eval('lambda a: a')
|
||||
g = eval('lambda a: a')
|
||||
self.failIf(f == g)
|
||||
self.assertNotEquals(f, g)
|
||||
|
||||
f = eval('lambda a=1: a')
|
||||
g = eval('lambda a=1: a')
|
||||
self.failIf(f == g)
|
||||
self.assertNotEquals(f, g)
|
||||
|
||||
f = eval('lambda: 0')
|
||||
g = eval('lambda: 1')
|
||||
self.failIf(f == g)
|
||||
self.assertNotEquals(f, g)
|
||||
|
||||
f = eval('lambda: None')
|
||||
g = eval('lambda a: None')
|
||||
self.failIf(f == g)
|
||||
self.assertNotEquals(f, g)
|
||||
|
||||
f = eval('lambda a: None')
|
||||
g = eval('lambda b: None')
|
||||
self.failIf(f == g)
|
||||
self.assertNotEquals(f, g)
|
||||
|
||||
f = eval('lambda a: None')
|
||||
g = eval('lambda a=None: None')
|
||||
self.failIf(f == g)
|
||||
self.assertNotEquals(f, g)
|
||||
|
||||
f = eval('lambda a=0: None')
|
||||
g = eval('lambda a=1: None')
|
||||
self.failIf(f == g)
|
||||
self.assertNotEquals(f, g)
|
||||
|
||||
|
||||
def test_main():
|
||||
|
|
|
@ -158,6 +158,7 @@ class PyclbrTest(TestCase):
|
|||
cm('cgi', ignore=('log',)) # set with = in module
|
||||
cm('urllib', ignore=('_CFNumberToInt32',
|
||||
'_CStringFromCFString',
|
||||
'_CFSetup',
|
||||
'getproxies_registry',
|
||||
'proxy_bypass_registry',
|
||||
'proxy_bypass_macosx_sysconf',
|
||||
|
|
|
@ -1,25 +1,285 @@
|
|||
from test import support
|
||||
import unittest
|
||||
import pydoc
|
||||
|
||||
class TestDescriptions(unittest.TestCase):
|
||||
def test_module(self):
|
||||
# Check that pydocfodder module can be described
|
||||
from test import pydocfodder
|
||||
doc = pydoc.render_doc(pydocfodder)
|
||||
assert "pydocfodder" in doc
|
||||
|
||||
def test_class(self):
|
||||
class C(object): "New-style class"
|
||||
c = C()
|
||||
|
||||
self.failUnlessEqual(pydoc.describe(C), 'class C')
|
||||
self.failUnlessEqual(pydoc.describe(c), 'C')
|
||||
self.failUnless('C in module test.test_pydoc object'
|
||||
in pydoc.render_doc(c))
|
||||
|
||||
def test_main():
|
||||
support.run_unittest(TestDescriptions)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
import sys
|
||||
import os
|
||||
import difflib
|
||||
import subprocess
|
||||
import re
|
||||
import pydoc
|
||||
import inspect
|
||||
import unittest
|
||||
import test.support
|
||||
|
||||
from test import pydoc_mod
|
||||
|
||||
expected_text_pattern = \
|
||||
"""
|
||||
NAME
|
||||
test.pydoc_mod - This is a test module for test_pydoc
|
||||
|
||||
FILE
|
||||
%s
|
||||
%s
|
||||
CLASSES
|
||||
builtins.object
|
||||
A
|
||||
B
|
||||
\x20\x20\x20\x20
|
||||
class A(builtins.object)
|
||||
| Hello and goodbye
|
||||
|\x20\x20
|
||||
| Methods defined here:
|
||||
|\x20\x20
|
||||
| __init__()
|
||||
| Wow, I have no function!
|
||||
|\x20\x20
|
||||
| ----------------------------------------------------------------------
|
||||
| Data descriptors defined here:
|
||||
|\x20\x20
|
||||
| __dict__
|
||||
| dictionary for instance variables (if defined)
|
||||
|\x20\x20
|
||||
| __weakref__
|
||||
| list of weak references to the object (if defined)
|
||||
\x20\x20\x20\x20
|
||||
class B(builtins.object)
|
||||
| Data descriptors defined here:
|
||||
|\x20\x20
|
||||
| __dict__
|
||||
| dictionary for instance variables (if defined)
|
||||
|\x20\x20
|
||||
| __weakref__
|
||||
| list of weak references to the object (if defined)
|
||||
|\x20\x20
|
||||
| ----------------------------------------------------------------------
|
||||
| Data and other attributes defined here:
|
||||
|\x20\x20
|
||||
| NO_MEANING = 'eggs'
|
||||
|
||||
FUNCTIONS
|
||||
doc_func()
|
||||
This function solves all of the world's problems:
|
||||
hunger
|
||||
lack of Python
|
||||
war
|
||||
\x20\x20\x20\x20
|
||||
nodoc_func()
|
||||
|
||||
DATA
|
||||
__author__ = 'Benjamin Peterson'
|
||||
__credits__ = 'Nobody'
|
||||
__package__ = None
|
||||
__version__ = '1.2.3.4'
|
||||
|
||||
VERSION
|
||||
1.2.3.4
|
||||
|
||||
AUTHOR
|
||||
Benjamin Peterson
|
||||
|
||||
CREDITS
|
||||
Nobody
|
||||
""".strip()
|
||||
|
||||
expected_html_pattern = \
|
||||
"""
|
||||
<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
|
||||
<tr bgcolor="#7799ee">
|
||||
<td valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong><a href="test.html"><font color="#ffffff">test</font></a>.pydoc_mod</strong></big></big> (version 1.2.3.4)</font></td
|
||||
><td align=right valign=bottom
|
||||
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:%s">%s</a>%s</font></td></tr></table>
|
||||
<p><tt>This is a test module for test_pydoc</tt></p>
|
||||
<p>
|
||||
<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#ee77aa">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
\x20\x20\x20\x20
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td>
|
||||
<td width="100%%"><dl>
|
||||
<dt><font face="helvetica, arial"><a href="builtins.html#object">builtins.object</a>
|
||||
</font></dt><dd>
|
||||
<dl>
|
||||
<dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#A">A</a>
|
||||
</font></dt><dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#B">B</a>
|
||||
</font></dt></dl>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>
|
||||
<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#ffc8d8">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#000000" face="helvetica, arial"><a name="A">class <strong>A</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
|
||||
\x20\x20\x20\x20
|
||||
<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td>
|
||||
<td colspan=2><tt>Hello and goodbye<br> </tt></td></tr>
|
||||
<tr><td> </td>
|
||||
<td width="100%%">Methods defined here:<br>
|
||||
<dl><dt><a name="A-__init__"><strong>__init__</strong></a>()</dt><dd><tt>Wow, I have no function!</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if defined)</tt></dd>
|
||||
</dl>
|
||||
</td></tr></table> <p>
|
||||
<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#ffc8d8">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#000000" face="helvetica, arial"><a name="B">class <strong>B</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
|
||||
\x20\x20\x20\x20
|
||||
<tr><td bgcolor="#ffc8d8"><tt> </tt></td><td> </td>
|
||||
<td width="100%%">Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if defined)</tt></dd>
|
||||
</dl>
|
||||
<hr>
|
||||
Data and other attributes defined here:<br>
|
||||
<dl><dt><strong>NO_MEANING</strong> = 'eggs'</dl>
|
||||
|
||||
</td></tr></table></td></tr></table><p>
|
||||
<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#eeaa77">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
|
||||
\x20\x20\x20\x20
|
||||
<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td>
|
||||
<td width="100%%"><dl><dt><a name="-doc_func"><strong>doc_func</strong></a>()</dt><dd><tt>This function solves all of the world's problems:<br>
|
||||
hunger<br>
|
||||
lack of Python<br>
|
||||
war</tt></dd></dl>
|
||||
<dl><dt><a name="-nodoc_func"><strong>nodoc_func</strong></a>()</dt></dl>
|
||||
</td></tr></table><p>
|
||||
<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#55aa55">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
\x20\x20\x20\x20
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </td>
|
||||
<td width="100%%"><strong>__author__</strong> = 'Benjamin Peterson'<br>
|
||||
<strong>__credits__</strong> = 'Nobody'<br>
|
||||
<strong>__package__</strong> = None<br>
|
||||
<strong>__version__</strong> = '1.2.3.4'</td></tr></table><p>
|
||||
<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#7799ee">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Author</strong></big></font></td></tr>
|
||||
\x20\x20\x20\x20
|
||||
<tr><td bgcolor="#7799ee"><tt> </tt></td><td> </td>
|
||||
<td width="100%%">Benjamin Peterson</td></tr></table><p>
|
||||
<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#7799ee">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Credits</strong></big></font></td></tr>
|
||||
\x20\x20\x20\x20
|
||||
<tr><td bgcolor="#7799ee"><tt> </tt></td><td> </td>
|
||||
<td width="100%%">Nobody</td></tr></table>
|
||||
""".strip()
|
||||
|
||||
|
||||
# output pattern for missing module
|
||||
missing_pattern = "no Python documentation found for '%s'"
|
||||
|
||||
def run_pydoc(module_name, *args):
|
||||
"""
|
||||
Runs pydoc on the specified module. Returns the stripped
|
||||
output of pydoc.
|
||||
"""
|
||||
cmd = [sys.executable, pydoc.__file__, " ".join(args), module_name]
|
||||
output = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout.read()
|
||||
return output.strip()
|
||||
|
||||
def get_pydoc_html(module):
|
||||
"Returns pydoc generated output as html"
|
||||
doc = pydoc.HTMLDoc()
|
||||
output = doc.docmodule(module)
|
||||
loc = doc.getdocloc(pydoc_mod) or ""
|
||||
if loc:
|
||||
loc = "<br><a href=\"" + loc + "\">Module Docs</a>"
|
||||
return output.strip(), loc
|
||||
|
||||
def get_pydoc_text(module):
|
||||
"Returns pydoc generated output as text"
|
||||
doc = pydoc.TextDoc()
|
||||
loc = doc.getdocloc(pydoc_mod) or ""
|
||||
if loc:
|
||||
loc = "\nMODULE DOCS\n " + loc + "\n"
|
||||
|
||||
output = doc.docmodule(module)
|
||||
|
||||
# cleanup the extra text formatting that pydoc preforms
|
||||
patt = re.compile('\b.')
|
||||
output = patt.sub('', output)
|
||||
return output.strip(), loc
|
||||
|
||||
def print_diffs(text1, text2):
|
||||
"Prints unified diffs for two texts"
|
||||
lines1 = text1.splitlines(True)
|
||||
lines2 = text2.splitlines(True)
|
||||
diffs = difflib.unified_diff(lines1, lines2, n=0, fromfile='expected',
|
||||
tofile='got')
|
||||
print('\n' + ''.join(diffs))
|
||||
|
||||
|
||||
class PyDocDocTest(unittest.TestCase):
|
||||
|
||||
def test_html_doc(self):
|
||||
result, doc_loc = get_pydoc_html(pydoc_mod)
|
||||
mod_file = inspect.getabsfile(pydoc_mod)
|
||||
expected_html = expected_html_pattern % (mod_file, mod_file, doc_loc)
|
||||
if result != expected_html:
|
||||
print_diffs(expected_html, result)
|
||||
self.fail("outputs are not equal, see diff above")
|
||||
|
||||
def test_text_doc(self):
|
||||
result, doc_loc = get_pydoc_text(pydoc_mod)
|
||||
expected_text = expected_text_pattern % \
|
||||
(inspect.getabsfile(pydoc_mod), doc_loc)
|
||||
if result != expected_text:
|
||||
print_diffs(expected_text, result)
|
||||
self.fail("outputs are not equal, see diff above")
|
||||
|
||||
def test_not_here(self):
|
||||
missing_module = "test.i_am_not_here"
|
||||
result = str(run_pydoc(missing_module), 'ascii')
|
||||
expected = missing_pattern % missing_module
|
||||
self.assertEqual(expected, result,
|
||||
"documentation for missing module found")
|
||||
|
||||
|
||||
class TestDescriptions(unittest.TestCase):
|
||||
|
||||
def test_module(self):
|
||||
# Check that pydocfodder module can be described
|
||||
from test import pydocfodder
|
||||
doc = pydoc.render_doc(pydocfodder)
|
||||
self.assert_("pydocfodder" in doc)
|
||||
|
||||
def test_classic_class(self):
|
||||
class C: "Classic class"
|
||||
c = C()
|
||||
self.assertEqual(pydoc.describe(C), 'class C')
|
||||
self.assertEqual(pydoc.describe(c), 'C')
|
||||
expected = 'C in module %s' % __name__
|
||||
self.assert_(expected in pydoc.render_doc(c))
|
||||
|
||||
def test_class(self):
|
||||
class C(object): "New-style class"
|
||||
c = C()
|
||||
|
||||
self.assertEqual(pydoc.describe(C), 'class C')
|
||||
self.assertEqual(pydoc.describe(c), 'C')
|
||||
expected = 'C in module %s object' % __name__
|
||||
self.assert_(expected in pydoc.render_doc(c))
|
||||
|
||||
|
||||
def test_main():
|
||||
test.support.run_unittest(PyDocDocTest, TestDescriptions)
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_main()
|
||||
|
|
|
@ -3,6 +3,7 @@ import sys
|
|||
import unittest
|
||||
import collections
|
||||
import weakref
|
||||
import operator
|
||||
|
||||
from test import support
|
||||
|
||||
|
@ -182,6 +183,26 @@ class ReferencesTestCase(TestBase):
|
|||
self.assertEqual(L3[:5], p3[:5])
|
||||
self.assertEqual(L3[2:5], p3[2:5])
|
||||
|
||||
def test_proxy_index(self):
|
||||
class C:
|
||||
def __index__(self):
|
||||
return 10
|
||||
o = C()
|
||||
p = weakref.proxy(o)
|
||||
self.assertEqual(operator.index(p), 10)
|
||||
|
||||
def test_proxy_div(self):
|
||||
class C:
|
||||
def __floordiv__(self, other):
|
||||
return 42
|
||||
def __ifloordiv__(self, other):
|
||||
return 21
|
||||
o = C()
|
||||
p = weakref.proxy(o)
|
||||
self.assertEqual(p // 5, 42)
|
||||
p //= 5
|
||||
self.assertEqual(p, 21)
|
||||
|
||||
# The PyWeakref_* C API is documented as allowing either NULL or
|
||||
# None as the value for the callback, where either means "no
|
||||
# callback". The "no callback" ref and proxy objects are supposed
|
||||
|
@ -1059,7 +1080,7 @@ class WeakKeyDictionaryTestCase(mapping_tests.BasicTestMappingProtocol):
|
|||
def _reference(self):
|
||||
return self.__ref.copy()
|
||||
|
||||
libreftest = """ Doctest for examples in the library reference: libweakref.tex
|
||||
libreftest = """ Doctest for examples in the library reference: weakref.rst
|
||||
|
||||
>>> import weakref
|
||||
>>> class Dict(dict):
|
||||
|
|
|
@ -125,6 +125,25 @@ class TestsWithSourceFile(unittest.TestCase):
|
|||
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
|
||||
self.zipOpenTest(f, zipfile.ZIP_STORED)
|
||||
|
||||
def testOpenViaZipInfo(self):
|
||||
# Create the ZIP archive
|
||||
zipfp = zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED)
|
||||
zipfp.writestr("name", "foo")
|
||||
zipfp.writestr("name", "bar")
|
||||
zipfp.close()
|
||||
|
||||
zipfp = zipfile.ZipFile(TESTFN2, "r")
|
||||
infos = zipfp.infolist()
|
||||
data = b""
|
||||
for info in infos:
|
||||
data += zipfp.open(info).read()
|
||||
self.assert_(data == b"foobar" or data == b"barfoo")
|
||||
data = b""
|
||||
for info in infos:
|
||||
data += zipfp.read(info)
|
||||
self.assert_(data == b"foobar" or data == b"barfoo")
|
||||
zipfp.close()
|
||||
|
||||
def zipRandomOpenTest(self, f, compression):
|
||||
self.makeTestArchive(f, compression)
|
||||
|
||||
|
|
|
@ -2903,8 +2903,7 @@ class Text(Widget):
|
|||
and edit_undo
|
||||
|
||||
"""
|
||||
return self._getints(
|
||||
self.tk.call((self._w, 'edit') + args)) or ()
|
||||
return self.tk.call(self._w, 'edit', *args)
|
||||
|
||||
def edit_modified(self, arg=None):
|
||||
"""Get or Set the modified flag
|
||||
|
|
|
@ -783,10 +783,13 @@ class ZipFile:
|
|||
else:
|
||||
zef_file = io.open(self.filename, 'rb')
|
||||
|
||||
# Get info object for name
|
||||
zinfo = self.getinfo(name)
|
||||
|
||||
filepos = zef_file.tell()
|
||||
# Make sure we have an info object
|
||||
if isinstance(name, ZipInfo):
|
||||
# 'name' is already an info object
|
||||
zinfo = name
|
||||
else:
|
||||
# Get info object for name
|
||||
zinfo = self.getinfo(name)
|
||||
|
||||
zef_file.seek(zinfo.header_offset, 0)
|
||||
|
||||
|
@ -891,7 +894,7 @@ class ZipFile:
|
|||
if upperdirs and not os.path.exists(upperdirs):
|
||||
os.makedirs(upperdirs)
|
||||
|
||||
source = self.open(member.filename, pwd=pwd)
|
||||
source = self.open(member, pwd=pwd)
|
||||
target = open(targetpath, "wb")
|
||||
shutil.copyfileobj(source, target)
|
||||
source.close()
|
||||
|
|
|
@ -373,6 +373,12 @@ run_profile_task:
|
|||
build_all_use_profile:
|
||||
$(MAKE) all CFLAGS="$(CFLAGS) -fprofile-use"
|
||||
|
||||
coverage:
|
||||
@echo "Building with support for coverage checking:"
|
||||
$(MAKE) clean
|
||||
$(MAKE) all CFLAGS="$(CFLAGS) -O0 -pg -fprofile-arcs -ftest-coverage" LIBS="$(LIBS) -lgcov"
|
||||
|
||||
|
||||
# Build the interpreter
|
||||
$(BUILDPYTHON): Modules/python.o $(LIBRARY) $(LDLIBRARY)
|
||||
$(LINKCC) $(LDFLAGS) $(LINKFORSHARED) -o $@ \
|
||||
|
|
|
@ -375,6 +375,7 @@ Damon Kohler
|
|||
Joseph Koshy
|
||||
Bob Kras
|
||||
Holger Krekel
|
||||
Michael Kremer
|
||||
Fabian Kreutz
|
||||
Hannu Krosing
|
||||
Andrew Kuchling
|
||||
|
@ -496,6 +497,7 @@ Piet van Oostrum
|
|||
Jason Orendorff
|
||||
Douglas Orr
|
||||
Denis S. Otkidach
|
||||
Michael Otteneder
|
||||
Russel Owen
|
||||
Ondrej Palkovsky
|
||||
Mike Pall
|
||||
|
@ -634,6 +636,7 @@ Gregory P. Smith
|
|||
Rafal Smotrzyk
|
||||
Dirk Soede
|
||||
Paul Sokolovsky
|
||||
Cody Somerville
|
||||
Clay Spence
|
||||
Per Spilling
|
||||
Joshua Spoerri
|
||||
|
|
|
@ -67,7 +67,7 @@ REFLOG="build/reflog.txt.out"
|
|||
# Note: test_XXX (none currently) really leak, but are disabled
|
||||
# so we don't send spam. Any test which really leaks should only
|
||||
# be listed here if there are also test cases under Lib/test/leakers.
|
||||
LEAKY_TESTS="test_(asynchat|cmd_line|popen2|socket|smtplib|sys|threadsignals|urllib2_localnet)"
|
||||
LEAKY_TESTS="test_(asynchat|cmd_line|popen2|socket|smtplib|sys|threadsignals|urllib2_localnet|httpservers)"
|
||||
|
||||
# These tests always fail, so skip them so we don't get false positives.
|
||||
_ALWAYS_SKIP=""
|
||||
|
|
|
@ -474,6 +474,8 @@ proxy_richcompare(PyObject *proxy, PyObject *v, int op)
|
|||
WRAP_BINARY(proxy_add, PyNumber_Add)
|
||||
WRAP_BINARY(proxy_sub, PyNumber_Subtract)
|
||||
WRAP_BINARY(proxy_mul, PyNumber_Multiply)
|
||||
WRAP_BINARY(proxy_floor_div, PyNumber_FloorDivide)
|
||||
WRAP_BINARY(proxy_true_div, PyNumber_TrueDivide)
|
||||
WRAP_BINARY(proxy_mod, PyNumber_Remainder)
|
||||
WRAP_BINARY(proxy_divmod, PyNumber_Divmod)
|
||||
WRAP_TERNARY(proxy_pow, PyNumber_Power)
|
||||
|
@ -492,6 +494,8 @@ WRAP_UNARY(proxy_float, PyNumber_Float)
|
|||
WRAP_BINARY(proxy_iadd, PyNumber_InPlaceAdd)
|
||||
WRAP_BINARY(proxy_isub, PyNumber_InPlaceSubtract)
|
||||
WRAP_BINARY(proxy_imul, PyNumber_InPlaceMultiply)
|
||||
WRAP_BINARY(proxy_ifloor_div, PyNumber_InPlaceFloorDivide)
|
||||
WRAP_BINARY(proxy_itrue_div, PyNumber_InPlaceTrueDivide)
|
||||
WRAP_BINARY(proxy_imod, PyNumber_InPlaceRemainder)
|
||||
WRAP_TERNARY(proxy_ipow, PyNumber_InPlacePower)
|
||||
WRAP_BINARY(proxy_ilshift, PyNumber_InPlaceLshift)
|
||||
|
@ -499,6 +503,7 @@ WRAP_BINARY(proxy_irshift, PyNumber_InPlaceRshift)
|
|||
WRAP_BINARY(proxy_iand, PyNumber_InPlaceAnd)
|
||||
WRAP_BINARY(proxy_ixor, PyNumber_InPlaceXor)
|
||||
WRAP_BINARY(proxy_ior, PyNumber_InPlaceOr)
|
||||
WRAP_UNARY(proxy_index, PyNumber_Index)
|
||||
|
||||
static int
|
||||
proxy_bool(PyWeakReference *proxy)
|
||||
|
@ -605,6 +610,11 @@ static PyNumberMethods proxy_as_number = {
|
|||
proxy_iand, /*nb_inplace_and*/
|
||||
proxy_ixor, /*nb_inplace_xor*/
|
||||
proxy_ior, /*nb_inplace_or*/
|
||||
proxy_floor_div, /*nb_floor_divide*/
|
||||
proxy_true_div, /*nb_true_divide*/
|
||||
proxy_ifloor_div, /*nb_inplace_floor_divide*/
|
||||
proxy_itrue_div, /*nb_inplace_true_divide*/
|
||||
proxy_index, /*nb_index*/
|
||||
};
|
||||
|
||||
static PySequenceMethods proxy_as_sequence = {
|
||||
|
|
Loading…
Reference in New Issue