mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Pydev updated to the latest dev version.
This commit is contained in:
@@ -1,2 +1,9 @@
|
||||
PyDev.Debugger
|
||||
==============
|
||||
|
||||
[](https://travis-ci.org/fabioz/PyDev.Debugger)
|
||||
|
||||
This repository contains the sources for the Debugger used in PyDev & PyCharm.
|
||||
|
||||
It should be compatible with Python 2.4 onwards (as well as Jython 2.2.1, IronPython and PyPy -- and any
|
||||
other variant which properly supports the Python structure for debuggers -- i.e.: sys.settrace/threading.settrace).
|
||||
@@ -1,604 +0,0 @@
|
||||
"""HTTP server base class.
|
||||
|
||||
Note: the class in this module doesn't implement any HTTP request; see
|
||||
SimpleHTTPServer for simple implementations of GET, HEAD and POST
|
||||
(including CGI scripts). It does, however, optionally implement HTTP/1.1
|
||||
persistent connections, as of version 0.3.
|
||||
|
||||
Contents:
|
||||
|
||||
- BaseHTTPRequestHandler: HTTP request handler base class
|
||||
- test: test function
|
||||
|
||||
XXX To do:
|
||||
|
||||
- log requests even later (to capture byte count)
|
||||
- log user-agent header and other interesting goodies
|
||||
- send error log to separate file
|
||||
"""
|
||||
|
||||
|
||||
# See also:
|
||||
#
|
||||
# HTTP Working Group T. Berners-Lee
|
||||
# INTERNET-DRAFT R. T. Fielding
|
||||
# <draft-ietf-http-v10-spec-00.txt> H. Frystyk Nielsen
|
||||
# Expires September 8, 1995 March 8, 1995
|
||||
#
|
||||
# URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt
|
||||
#
|
||||
# and
|
||||
#
|
||||
# Network Working Group R. Fielding
|
||||
# Request for Comments: 2616 et al
|
||||
# Obsoletes: 2068 June 1999
|
||||
# Category: Standards Track
|
||||
#
|
||||
# URL: http://www.faqs.org/rfcs/rfc2616.html
|
||||
|
||||
# Log files
|
||||
# ---------
|
||||
#
|
||||
# Here's a quote from the NCSA httpd docs about log file format.
|
||||
#
|
||||
# | The logfile format is as follows. Each line consists of:
|
||||
# |
|
||||
# | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb
|
||||
# |
|
||||
# | host: Either the DNS name or the IP number of the remote client
|
||||
# | rfc931: Any information returned by identd for this person,
|
||||
# | - otherwise.
|
||||
# | authuser: If user sent a userid for authentication, the user name,
|
||||
# | - otherwise.
|
||||
# | DD: Day
|
||||
# | Mon: Month (calendar name)
|
||||
# | YYYY: Year
|
||||
# | hh: hour (24-hour format, the machine's timezone)
|
||||
# | mm: minutes
|
||||
# | ss: seconds
|
||||
# | request: The first line of the HTTP request as sent by the client.
|
||||
# | ddd: the status code returned by the server, - if not available.
|
||||
# | bbbb: the total number of bytes sent,
|
||||
# | *not including the HTTP/1.0 header*, - if not available
|
||||
# |
|
||||
# | You can determine the name of the file accessed through request.
|
||||
#
|
||||
# (Actually, the latter is only true if you know the server configuration
|
||||
# at the time the request was made!)
|
||||
|
||||
__version__ = "0.3"
|
||||
|
||||
__all__ = ["HTTPServer", "BaseHTTPRequestHandler"]
|
||||
|
||||
import sys
|
||||
import _pydev_time as time
|
||||
import _pydev_socket as socket # For gethostbyaddr()
|
||||
from warnings import filterwarnings, catch_warnings
|
||||
with catch_warnings():
|
||||
if sys.py3kwarning:
|
||||
filterwarnings("ignore", ".*mimetools has been removed",
|
||||
DeprecationWarning)
|
||||
import mimetools
|
||||
|
||||
import _pydev_SocketServer as SocketServer
|
||||
|
||||
# Default error message template
|
||||
DEFAULT_ERROR_MESSAGE = """\
|
||||
<head>
|
||||
<title>Error response</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Error response</h1>
|
||||
<p>Error code %(code)d.
|
||||
<p>Message: %(message)s.
|
||||
<p>Error code explanation: %(code)s = %(explain)s.
|
||||
</body>
|
||||
"""
|
||||
|
||||
DEFAULT_ERROR_CONTENT_TYPE = "text/html"
|
||||
|
||||
def _quote_html(html):
|
||||
return html.replace("&", "&").replace("<", "<").replace(">", ">")
|
||||
|
||||
class HTTPServer(SocketServer.TCPServer):
|
||||
|
||||
allow_reuse_address = 1 # Seems to make sense in testing environment
|
||||
|
||||
def server_bind(self):
|
||||
"""Override server_bind to store the server name."""
|
||||
SocketServer.TCPServer.server_bind(self)
|
||||
host, port = self.socket.getsockname()[:2]
|
||||
self.server_name = socket.getfqdn(host)
|
||||
self.server_port = port
|
||||
|
||||
|
||||
class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler):
|
||||
|
||||
"""HTTP request handler base class.
|
||||
|
||||
The following explanation of HTTP serves to guide you through the
|
||||
code as well as to expose any misunderstandings I may have about
|
||||
HTTP (so you don't need to read the code to figure out I'm wrong
|
||||
:-).
|
||||
|
||||
HTTP (HyperText Transfer Protocol) is an extensible protocol on
|
||||
top of a reliable stream transport (e.g. TCP/IP). The protocol
|
||||
recognizes three parts to a request:
|
||||
|
||||
1. One line identifying the request type and path
|
||||
2. An optional set of RFC-822-style headers
|
||||
3. An optional data part
|
||||
|
||||
The headers and data are separated by a blank line.
|
||||
|
||||
The first line of the request has the form
|
||||
|
||||
<command> <path> <version>
|
||||
|
||||
where <command> is a (case-sensitive) keyword such as GET or POST,
|
||||
<path> is a string containing path information for the request,
|
||||
and <version> should be the string "HTTP/1.0" or "HTTP/1.1".
|
||||
<path> is encoded using the URL encoding scheme (using %xx to signify
|
||||
the ASCII character with hex code xx).
|
||||
|
||||
The specification specifies that lines are separated by CRLF but
|
||||
for compatibility with the widest range of clients recommends
|
||||
servers also handle LF. Similarly, whitespace in the request line
|
||||
is treated sensibly (allowing multiple spaces between components
|
||||
and allowing trailing whitespace).
|
||||
|
||||
Similarly, for output, lines ought to be separated by CRLF pairs
|
||||
but most clients grok LF characters just fine.
|
||||
|
||||
If the first line of the request has the form
|
||||
|
||||
<command> <path>
|
||||
|
||||
(i.e. <version> is left out) then this is assumed to be an HTTP
|
||||
0.9 request; this form has no optional headers and data part and
|
||||
the reply consists of just the data.
|
||||
|
||||
The reply form of the HTTP 1.x protocol again has three parts:
|
||||
|
||||
1. One line giving the response code
|
||||
2. An optional set of RFC-822-style headers
|
||||
3. The data
|
||||
|
||||
Again, the headers and data are separated by a blank line.
|
||||
|
||||
The response code line has the form
|
||||
|
||||
<version> <responsecode> <responsestring>
|
||||
|
||||
where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
|
||||
<responsecode> is a 3-digit response code indicating success or
|
||||
failure of the request, and <responsestring> is an optional
|
||||
human-readable string explaining what the response code means.
|
||||
|
||||
This server parses the request and the headers, and then calls a
|
||||
function specific to the request type (<command>). Specifically,
|
||||
a request SPAM will be handled by a method do_SPAM(). If no
|
||||
such method exists the server sends an error response to the
|
||||
client. If it exists, it is called with no arguments:
|
||||
|
||||
do_SPAM()
|
||||
|
||||
Note that the request name is case sensitive (i.e. SPAM and spam
|
||||
are different requests).
|
||||
|
||||
The various request details are stored in instance variables:
|
||||
|
||||
- client_address is the client IP address in the form (host,
|
||||
port);
|
||||
|
||||
- command, path and version are the broken-down request line;
|
||||
|
||||
- headers is an instance of mimetools.Message (or a derived
|
||||
class) containing the header information;
|
||||
|
||||
- rfile is a file object open for reading positioned at the
|
||||
start of the optional input data part;
|
||||
|
||||
- wfile is a file object open for writing.
|
||||
|
||||
IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
|
||||
|
||||
The first thing to be written must be the response line. Then
|
||||
follow 0 or more header lines, then a blank line, and then the
|
||||
actual data (if any). The meaning of the header lines depends on
|
||||
the command executed by the server; in most cases, when data is
|
||||
returned, there should be at least one header line of the form
|
||||
|
||||
Content-type: <type>/<subtype>
|
||||
|
||||
where <type> and <subtype> should be registered MIME types,
|
||||
e.g. "text/html" or "text/plain".
|
||||
|
||||
"""
|
||||
|
||||
# The Python system version, truncated to its first component.
|
||||
sys_version = "Python/" + sys.version.split()[0]
|
||||
|
||||
# The server software version. You may want to override this.
|
||||
# The format is multiple whitespace-separated strings,
|
||||
# where each string is of the form name[/version].
|
||||
server_version = "BaseHTTP/" + __version__
|
||||
|
||||
# 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).
|
||||
|
||||
The request should be stored in self.raw_requestline; the results
|
||||
are in self.command, self.path, self.request_version and
|
||||
self.headers.
|
||||
|
||||
Return True for success, False for failure; on failure, an
|
||||
error is sent back.
|
||||
|
||||
"""
|
||||
self.command = None # set in case of error on the first line
|
||||
self.request_version = version = self.default_request_version
|
||||
self.close_connection = 1
|
||||
requestline = self.raw_requestline
|
||||
requestline = requestline.rstrip('\r\n')
|
||||
self.requestline = requestline
|
||||
words = requestline.split()
|
||||
if len(words) == 3:
|
||||
command, path, version = words
|
||||
if version[:5] != 'HTTP/':
|
||||
self.send_error(400, "Bad request version (%r)" % version)
|
||||
return False
|
||||
try:
|
||||
base_version_number = version.split('/', 1)[1]
|
||||
version_number = base_version_number.split(".")
|
||||
# RFC 2145 section 3.1 says there can be only one "." and
|
||||
# - major and minor numbers MUST be treated as
|
||||
# separate integers;
|
||||
# - HTTP/2.4 is a lower version than HTTP/2.13, which in
|
||||
# turn is lower than HTTP/12.3;
|
||||
# - Leading zeros MUST be ignored by recipients.
|
||||
if len(version_number) != 2:
|
||||
raise ValueError
|
||||
version_number = int(version_number[0]), int(version_number[1])
|
||||
except (ValueError, IndexError):
|
||||
self.send_error(400, "Bad request version (%r)" % version)
|
||||
return False
|
||||
if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
|
||||
self.close_connection = 0
|
||||
if version_number >= (2, 0):
|
||||
self.send_error(505,
|
||||
"Invalid HTTP Version (%s)" % base_version_number)
|
||||
return False
|
||||
elif len(words) == 2:
|
||||
command, path = words
|
||||
self.close_connection = 1
|
||||
if command != 'GET':
|
||||
self.send_error(400,
|
||||
"Bad HTTP/0.9 request type (%r)" % command)
|
||||
return False
|
||||
elif not words:
|
||||
return False
|
||||
else:
|
||||
self.send_error(400, "Bad request syntax (%r)" % requestline)
|
||||
return False
|
||||
self.command, self.path, self.request_version = command, path, version
|
||||
|
||||
# Examine the headers and look for a Connection directive
|
||||
self.headers = self.MessageClass(self.rfile, 0)
|
||||
|
||||
conntype = self.headers.get('Connection', "")
|
||||
if conntype.lower() == 'close':
|
||||
self.close_connection = 1
|
||||
elif (conntype.lower() == 'keep-alive' and
|
||||
self.protocol_version >= "HTTP/1.1"):
|
||||
self.close_connection = 0
|
||||
return True
|
||||
|
||||
def handle_one_request(self):
|
||||
"""Handle a single HTTP request.
|
||||
|
||||
You normally don't need to override this method; see the class
|
||||
__doc__ string for information on how to handle specific HTTP
|
||||
commands such as GET and POST.
|
||||
|
||||
"""
|
||||
try:
|
||||
self.raw_requestline = self.rfile.readline(65537)
|
||||
if len(self.raw_requestline) > 65536:
|
||||
self.requestline = ''
|
||||
self.request_version = ''
|
||||
self.command = ''
|
||||
self.send_error(414)
|
||||
return
|
||||
if not self.raw_requestline:
|
||||
self.close_connection = 1
|
||||
return
|
||||
if not self.parse_request():
|
||||
# An error code has been sent, just exit
|
||||
return
|
||||
mname = 'do_' + self.command
|
||||
if not hasattr(self, mname):
|
||||
self.send_error(501, "Unsupported method (%r)" % self.command)
|
||||
return
|
||||
method = getattr(self, mname)
|
||||
method()
|
||||
self.wfile.flush() #actually send the response if not already done.
|
||||
except socket.timeout:
|
||||
#a read or a write timed out. Discard this connection
|
||||
self.log_error("Request timed out: %r", sys.exc_info()[1])
|
||||
self.close_connection = 1
|
||||
return
|
||||
|
||||
def handle(self):
|
||||
"""Handle multiple requests if necessary."""
|
||||
self.close_connection = 1
|
||||
|
||||
self.handle_one_request()
|
||||
while not self.close_connection:
|
||||
self.handle_one_request()
|
||||
|
||||
def send_error(self, code, message=None):
|
||||
"""Send and log an error reply.
|
||||
|
||||
Arguments are the error code, and a detailed message.
|
||||
The detailed message defaults to the short entry matching the
|
||||
response code.
|
||||
|
||||
This sends an error response (so it must be called before any
|
||||
output has been generated), logs the error, and finally sends
|
||||
a piece of HTML explaining the error to the user.
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
short, long = self.responses[code]
|
||||
except KeyError:
|
||||
short, long = '???', '???'
|
||||
if message is None:
|
||||
message = short
|
||||
explain = long
|
||||
self.log_error("code %d, message %s", code, message)
|
||||
# using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201)
|
||||
content = (self.error_message_format %
|
||||
{'code': code, 'message': _quote_html(message), 'explain': explain})
|
||||
self.send_response(code, message)
|
||||
self.send_header("Content-Type", self.error_content_type)
|
||||
self.send_header('Connection', 'close')
|
||||
self.end_headers()
|
||||
if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
|
||||
self.wfile.write(content)
|
||||
|
||||
error_message_format = DEFAULT_ERROR_MESSAGE
|
||||
error_content_type = DEFAULT_ERROR_CONTENT_TYPE
|
||||
|
||||
def send_response(self, code, message=None):
|
||||
"""Send the response header and log the response code.
|
||||
|
||||
Also send two standard headers with the server software
|
||||
version and the current date.
|
||||
|
||||
"""
|
||||
self.log_request(code)
|
||||
if message is None:
|
||||
if code in self.responses:
|
||||
message = self.responses[code][0]
|
||||
else:
|
||||
message = ''
|
||||
if self.request_version != 'HTTP/0.9':
|
||||
self.wfile.write("%s %d %s\r\n" %
|
||||
(self.protocol_version, code, message))
|
||||
# print (self.protocol_version, code, message)
|
||||
self.send_header('Server', self.version_string())
|
||||
self.send_header('Date', self.date_time_string())
|
||||
|
||||
def send_header(self, keyword, value):
|
||||
"""Send a MIME header."""
|
||||
if self.request_version != 'HTTP/0.9':
|
||||
self.wfile.write("%s: %s\r\n" % (keyword, value))
|
||||
|
||||
if keyword.lower() == 'connection':
|
||||
if value.lower() == 'close':
|
||||
self.close_connection = 1
|
||||
elif value.lower() == 'keep-alive':
|
||||
self.close_connection = 0
|
||||
|
||||
def end_headers(self):
|
||||
"""Send the blank line ending the MIME headers."""
|
||||
if self.request_version != 'HTTP/0.9':
|
||||
self.wfile.write("\r\n")
|
||||
|
||||
def log_request(self, code='-', size='-'):
|
||||
"""Log an accepted request.
|
||||
|
||||
This is called by send_response().
|
||||
|
||||
"""
|
||||
|
||||
self.log_message('"%s" %s %s',
|
||||
self.requestline, str(code), str(size))
|
||||
|
||||
def log_error(self, format, *args):
|
||||
"""Log an error.
|
||||
|
||||
This is called when a request cannot be fulfilled. By
|
||||
default it passes the message on to log_message().
|
||||
|
||||
Arguments are the same as for log_message().
|
||||
|
||||
XXX This should go to the separate error log.
|
||||
|
||||
"""
|
||||
|
||||
self.log_message(format, *args)
|
||||
|
||||
def log_message(self, format, *args):
|
||||
"""Log an arbitrary message.
|
||||
|
||||
This is used by all other logging functions. Override
|
||||
it if you have specific logging wishes.
|
||||
|
||||
The first argument, FORMAT, is a format string for the
|
||||
message to be logged. If the format string contains
|
||||
any % escapes requiring parameters, they should be
|
||||
specified as subsequent arguments (it's just like
|
||||
printf!).
|
||||
|
||||
The client host and current date/time are prefixed to
|
||||
every message.
|
||||
|
||||
"""
|
||||
|
||||
sys.stderr.write("%s - - [%s] %s\n" %
|
||||
(self.address_string(),
|
||||
self.log_date_time_string(),
|
||||
format%args))
|
||||
|
||||
def version_string(self):
|
||||
"""Return the server software version string."""
|
||||
return self.server_version + ' ' + self.sys_version
|
||||
|
||||
def date_time_string(self, timestamp=None):
|
||||
"""Return the current date and time formatted for a message header."""
|
||||
if timestamp is None:
|
||||
timestamp = time.time()
|
||||
year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
|
||||
s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
|
||||
self.weekdayname[wd],
|
||||
day, self.monthname[month], year,
|
||||
hh, mm, ss)
|
||||
return s
|
||||
|
||||
def log_date_time_string(self):
|
||||
"""Return the current time formatted for logging."""
|
||||
now = time.time()
|
||||
year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
|
||||
s = "%02d/%3s/%04d %02d:%02d:%02d" % (
|
||||
day, self.monthname[month], year, hh, mm, ss)
|
||||
return s
|
||||
|
||||
weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||||
|
||||
monthname = [None,
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||
|
||||
def address_string(self):
|
||||
"""Return the client address formatted for logging.
|
||||
|
||||
This version looks up the full hostname using gethostbyaddr(),
|
||||
and tries to find a name that contains at least one dot.
|
||||
|
||||
"""
|
||||
|
||||
host, port = self.client_address[:2]
|
||||
return socket.getfqdn(host)
|
||||
|
||||
# Essentially static class variables
|
||||
|
||||
# The version of the HTTP protocol we support.
|
||||
# Set this to HTTP/1.1 to enable automatic keepalive
|
||||
protocol_version = "HTTP/1.0"
|
||||
|
||||
# The Message-like class used to parse headers
|
||||
MessageClass = mimetools.Message
|
||||
|
||||
# Table mapping response codes to messages; entries have the
|
||||
# form {code: (shortmessage, longmessage)}.
|
||||
# See RFC 2616.
|
||||
responses = {
|
||||
100: ('Continue', 'Request received, please continue'),
|
||||
101: ('Switching Protocols',
|
||||
'Switching to new protocol; obey Upgrade header'),
|
||||
|
||||
200: ('OK', 'Request fulfilled, document follows'),
|
||||
201: ('Created', 'Document created, URL follows'),
|
||||
202: ('Accepted',
|
||||
'Request accepted, processing continues off-line'),
|
||||
203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
|
||||
204: ('No Content', 'Request fulfilled, nothing follows'),
|
||||
205: ('Reset Content', 'Clear input form for further input.'),
|
||||
206: ('Partial Content', 'Partial content follows.'),
|
||||
|
||||
300: ('Multiple Choices',
|
||||
'Object has several resources -- see URI list'),
|
||||
301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
|
||||
302: ('Found', 'Object moved temporarily -- see URI list'),
|
||||
303: ('See Other', 'Object moved -- see Method and URL list'),
|
||||
304: ('Not Modified',
|
||||
'Document has not changed since given time'),
|
||||
305: ('Use Proxy',
|
||||
'You must use proxy specified in Location to access this '
|
||||
'resource.'),
|
||||
307: ('Temporary Redirect',
|
||||
'Object moved temporarily -- see URI list'),
|
||||
|
||||
400: ('Bad Request',
|
||||
'Bad request syntax or unsupported method'),
|
||||
401: ('Unauthorized',
|
||||
'No permission -- see authorization schemes'),
|
||||
402: ('Payment Required',
|
||||
'No payment -- see charging schemes'),
|
||||
403: ('Forbidden',
|
||||
'Request forbidden -- authorization will not help'),
|
||||
404: ('Not Found', 'Nothing matches the given URI'),
|
||||
405: ('Method Not Allowed',
|
||||
'Specified method is invalid for this resource.'),
|
||||
406: ('Not Acceptable', 'URI not available in preferred format.'),
|
||||
407: ('Proxy Authentication Required', 'You must authenticate with '
|
||||
'this proxy before proceeding.'),
|
||||
408: ('Request Timeout', 'Request timed out; try again later.'),
|
||||
409: ('Conflict', 'Request conflict.'),
|
||||
410: ('Gone',
|
||||
'URI no longer exists and has been permanently removed.'),
|
||||
411: ('Length Required', 'Client must specify Content-Length.'),
|
||||
412: ('Precondition Failed', 'Precondition in headers is false.'),
|
||||
413: ('Request Entity Too Large', 'Entity is too large.'),
|
||||
414: ('Request-URI Too Long', 'URI is too long.'),
|
||||
415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
|
||||
416: ('Requested Range Not Satisfiable',
|
||||
'Cannot satisfy request range.'),
|
||||
417: ('Expectation Failed',
|
||||
'Expect condition could not be satisfied.'),
|
||||
|
||||
500: ('Internal Server Error', 'Server got itself in trouble'),
|
||||
501: ('Not Implemented',
|
||||
'Server does not support this operation'),
|
||||
502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
|
||||
503: ('Service Unavailable',
|
||||
'The server cannot process the request due to a high load'),
|
||||
504: ('Gateway Timeout',
|
||||
'The gateway server did not receive a timely response'),
|
||||
505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
|
||||
}
|
||||
|
||||
|
||||
def test(HandlerClass = BaseHTTPRequestHandler,
|
||||
ServerClass = HTTPServer, protocol="HTTP/1.0"):
|
||||
"""Test the HTTP request handler class.
|
||||
|
||||
This runs an HTTP server on port 8000 (or the first command line
|
||||
argument).
|
||||
|
||||
"""
|
||||
|
||||
if sys.argv[1:]:
|
||||
port = int(sys.argv[1])
|
||||
else:
|
||||
port = 8000
|
||||
server_address = ('', port)
|
||||
|
||||
HandlerClass.protocol_version = protocol
|
||||
httpd = ServerClass(server_address, HandlerClass)
|
||||
|
||||
sa = httpd.socket.getsockname()
|
||||
print ("Serving HTTP on", sa[0], "port", sa[1], "...")
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
@@ -1,244 +0,0 @@
|
||||
"""A multi-producer, multi-consumer queue."""
|
||||
|
||||
from _pydev_time import time as _time
|
||||
try:
|
||||
import _pydev_threading as _threading
|
||||
except ImportError:
|
||||
import dummy_threading as _threading
|
||||
from collections import deque
|
||||
import heapq
|
||||
|
||||
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
|
||||
|
||||
class Empty(Exception):
|
||||
"Exception raised by Queue.get(block=0)/get_nowait()."
|
||||
pass
|
||||
|
||||
class Full(Exception):
|
||||
"Exception raised by Queue.put(block=0)/put_nowait()."
|
||||
pass
|
||||
|
||||
class Queue:
|
||||
"""Create a queue object with a given maximum size.
|
||||
|
||||
If maxsize is <= 0, the queue size is infinite.
|
||||
"""
|
||||
def __init__(self, maxsize=0):
|
||||
self.maxsize = maxsize
|
||||
self._init(maxsize)
|
||||
# mutex must be held whenever the queue is mutating. All methods
|
||||
# that acquire mutex must release it before returning. mutex
|
||||
# is shared between the three conditions, so acquiring and
|
||||
# releasing the conditions also acquires and releases mutex.
|
||||
self.mutex = _threading.Lock()
|
||||
# Notify not_empty whenever an item is added to the queue; a
|
||||
# thread waiting to get is notified then.
|
||||
self.not_empty = _threading.Condition(self.mutex)
|
||||
# Notify not_full whenever an item is removed from the queue;
|
||||
# a thread waiting to put is notified then.
|
||||
self.not_full = _threading.Condition(self.mutex)
|
||||
# Notify all_tasks_done whenever the number of unfinished tasks
|
||||
# drops to zero; thread waiting to join() is notified to resume
|
||||
self.all_tasks_done = _threading.Condition(self.mutex)
|
||||
self.unfinished_tasks = 0
|
||||
|
||||
def task_done(self):
|
||||
"""Indicate that a formerly enqueued task is complete.
|
||||
|
||||
Used by Queue consumer threads. For each get() used to fetch a task,
|
||||
a subsequent call to task_done() tells the queue that the processing
|
||||
on the task is complete.
|
||||
|
||||
If a join() is currently blocking, it will resume when all items
|
||||
have been processed (meaning that a task_done() call was received
|
||||
for every item that had been put() into the queue).
|
||||
|
||||
Raises a ValueError if called more times than there were items
|
||||
placed in the queue.
|
||||
"""
|
||||
self.all_tasks_done.acquire()
|
||||
try:
|
||||
unfinished = self.unfinished_tasks - 1
|
||||
if unfinished <= 0:
|
||||
if unfinished < 0:
|
||||
raise ValueError('task_done() called too many times')
|
||||
self.all_tasks_done.notify_all()
|
||||
self.unfinished_tasks = unfinished
|
||||
finally:
|
||||
self.all_tasks_done.release()
|
||||
|
||||
def join(self):
|
||||
"""Blocks until all items in the Queue have been gotten and processed.
|
||||
|
||||
The count of unfinished tasks goes up whenever an item is added to the
|
||||
queue. The count goes down whenever a consumer thread calls task_done()
|
||||
to indicate the item was retrieved and all work on it is complete.
|
||||
|
||||
When the count of unfinished tasks drops to zero, join() unblocks.
|
||||
"""
|
||||
self.all_tasks_done.acquire()
|
||||
try:
|
||||
while self.unfinished_tasks:
|
||||
self.all_tasks_done.wait()
|
||||
finally:
|
||||
self.all_tasks_done.release()
|
||||
|
||||
def qsize(self):
|
||||
"""Return the approximate size of the queue (not reliable!)."""
|
||||
self.mutex.acquire()
|
||||
n = self._qsize()
|
||||
self.mutex.release()
|
||||
return n
|
||||
|
||||
def empty(self):
|
||||
"""Return True if the queue is empty, False otherwise (not reliable!)."""
|
||||
self.mutex.acquire()
|
||||
n = not self._qsize()
|
||||
self.mutex.release()
|
||||
return n
|
||||
|
||||
def full(self):
|
||||
"""Return True if the queue is full, False otherwise (not reliable!)."""
|
||||
self.mutex.acquire()
|
||||
n = 0 < self.maxsize == self._qsize()
|
||||
self.mutex.release()
|
||||
return n
|
||||
|
||||
def put(self, item, block=True, timeout=None):
|
||||
"""Put an item into the queue.
|
||||
|
||||
If optional args 'block' is true and 'timeout' is None (the default),
|
||||
block if necessary until a free slot is available. If 'timeout' is
|
||||
a positive number, it blocks at most 'timeout' seconds and raises
|
||||
the Full exception if no free slot was available within that time.
|
||||
Otherwise ('block' is false), put an item on the queue if a free slot
|
||||
is immediately available, else raise the Full exception ('timeout'
|
||||
is ignored in that case).
|
||||
"""
|
||||
self.not_full.acquire()
|
||||
try:
|
||||
if self.maxsize > 0:
|
||||
if not block:
|
||||
if self._qsize() == self.maxsize:
|
||||
raise Full
|
||||
elif timeout is None:
|
||||
while self._qsize() == self.maxsize:
|
||||
self.not_full.wait()
|
||||
elif timeout < 0:
|
||||
raise ValueError("'timeout' must be a positive number")
|
||||
else:
|
||||
endtime = _time() + timeout
|
||||
while self._qsize() == self.maxsize:
|
||||
remaining = endtime - _time()
|
||||
if remaining <= 0.0:
|
||||
raise Full
|
||||
self.not_full.wait(remaining)
|
||||
self._put(item)
|
||||
self.unfinished_tasks += 1
|
||||
self.not_empty.notify()
|
||||
finally:
|
||||
self.not_full.release()
|
||||
|
||||
def put_nowait(self, item):
|
||||
"""Put an item into the queue without blocking.
|
||||
|
||||
Only enqueue the item if a free slot is immediately available.
|
||||
Otherwise raise the Full exception.
|
||||
"""
|
||||
return self.put(item, False)
|
||||
|
||||
def get(self, block=True, timeout=None):
|
||||
"""Remove and return an item from the queue.
|
||||
|
||||
If optional args 'block' is true and 'timeout' is None (the default),
|
||||
block if necessary until an item is available. If 'timeout' is
|
||||
a positive number, it blocks at most 'timeout' seconds and raises
|
||||
the Empty exception if no item was available within that time.
|
||||
Otherwise ('block' is false), return an item if one is immediately
|
||||
available, else raise the Empty exception ('timeout' is ignored
|
||||
in that case).
|
||||
"""
|
||||
self.not_empty.acquire()
|
||||
try:
|
||||
if not block:
|
||||
if not self._qsize():
|
||||
raise Empty
|
||||
elif timeout is None:
|
||||
while not self._qsize():
|
||||
self.not_empty.wait()
|
||||
elif timeout < 0:
|
||||
raise ValueError("'timeout' must be a positive number")
|
||||
else:
|
||||
endtime = _time() + timeout
|
||||
while not self._qsize():
|
||||
remaining = endtime - _time()
|
||||
if remaining <= 0.0:
|
||||
raise Empty
|
||||
self.not_empty.wait(remaining)
|
||||
item = self._get()
|
||||
self.not_full.notify()
|
||||
return item
|
||||
finally:
|
||||
self.not_empty.release()
|
||||
|
||||
def get_nowait(self):
|
||||
"""Remove and return an item from the queue without blocking.
|
||||
|
||||
Only get an item if one is immediately available. Otherwise
|
||||
raise the Empty exception.
|
||||
"""
|
||||
return self.get(False)
|
||||
|
||||
# Override these methods to implement other queue organizations
|
||||
# (e.g. stack or priority queue).
|
||||
# These will only be called with appropriate locks held
|
||||
|
||||
# Initialize the queue representation
|
||||
def _init(self, maxsize):
|
||||
self.queue = deque()
|
||||
|
||||
def _qsize(self, len=len):
|
||||
return len(self.queue)
|
||||
|
||||
# Put a new item in the queue
|
||||
def _put(self, item):
|
||||
self.queue.append(item)
|
||||
|
||||
# Get an item from the queue
|
||||
def _get(self):
|
||||
return self.queue.popleft()
|
||||
|
||||
|
||||
class PriorityQueue(Queue):
|
||||
'''Variant of Queue that retrieves open entries in priority order (lowest first).
|
||||
|
||||
Entries are typically tuples of the form: (priority number, data).
|
||||
'''
|
||||
|
||||
def _init(self, maxsize):
|
||||
self.queue = []
|
||||
|
||||
def _qsize(self, len=len):
|
||||
return len(self.queue)
|
||||
|
||||
def _put(self, item, heappush=heapq.heappush):
|
||||
heappush(self.queue, item)
|
||||
|
||||
def _get(self, heappop=heapq.heappop):
|
||||
return heappop(self.queue)
|
||||
|
||||
|
||||
class LifoQueue(Queue):
|
||||
'''Variant of Queue that retrieves most recently added entries first.'''
|
||||
|
||||
def _init(self, maxsize):
|
||||
self.queue = []
|
||||
|
||||
def _qsize(self, len=len):
|
||||
return len(self.queue)
|
||||
|
||||
def _put(self, item):
|
||||
self.queue.append(item)
|
||||
|
||||
def _get(self):
|
||||
return self.queue.pop()
|
||||
@@ -1,610 +0,0 @@
|
||||
#Just a copy of the version in python 2.5 to be used if it's not available in jython 2.1
|
||||
|
||||
"""Simple XML-RPC Server.
|
||||
|
||||
This module can be used to create simple XML-RPC servers
|
||||
by creating a server and either installing functions, a
|
||||
class instance, or by extending the SimpleXMLRPCServer
|
||||
class.
|
||||
|
||||
It can also be used to handle XML-RPC requests in a CGI
|
||||
environment using CGIXMLRPCRequestHandler.
|
||||
|
||||
A list of possible usage patterns follows:
|
||||
|
||||
1. Install functions:
|
||||
|
||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
||||
server.register_function(pow)
|
||||
server.register_function(lambda x,y: x+y, 'add')
|
||||
server.serve_forever()
|
||||
|
||||
2. Install an instance:
|
||||
|
||||
class MyFuncs:
|
||||
def __init__(self):
|
||||
# make all of the string functions available through
|
||||
# string.func_name
|
||||
import string
|
||||
self.string = string
|
||||
def _listMethods(self):
|
||||
# implement this method so that system.listMethods
|
||||
# knows to advertise the strings methods
|
||||
return list_public_methods(self) + \
|
||||
['string.' + method for method in list_public_methods(self.string)]
|
||||
def pow(self, x, y): return pow(x, y)
|
||||
def add(self, x, y) : return x + y
|
||||
|
||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
||||
server.register_introspection_functions()
|
||||
server.register_instance(MyFuncs())
|
||||
server.serve_forever()
|
||||
|
||||
3. Install an instance with custom dispatch method:
|
||||
|
||||
class Math:
|
||||
def _listMethods(self):
|
||||
# this method must be present for system.listMethods
|
||||
# to work
|
||||
return ['add', 'pow']
|
||||
def _methodHelp(self, method):
|
||||
# this method must be present for system.methodHelp
|
||||
# to work
|
||||
if method == 'add':
|
||||
return "add(2,3) => 5"
|
||||
elif method == 'pow':
|
||||
return "pow(x, y[, z]) => number"
|
||||
else:
|
||||
# By convention, return empty
|
||||
# string if no help is available
|
||||
return ""
|
||||
def _dispatch(self, method, params):
|
||||
if method == 'pow':
|
||||
return pow(*params)
|
||||
elif method == 'add':
|
||||
return params[0] + params[1]
|
||||
else:
|
||||
raise 'bad method'
|
||||
|
||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
||||
server.register_introspection_functions()
|
||||
server.register_instance(Math())
|
||||
server.serve_forever()
|
||||
|
||||
4. Subclass SimpleXMLRPCServer:
|
||||
|
||||
class MathServer(SimpleXMLRPCServer):
|
||||
def _dispatch(self, method, params):
|
||||
try:
|
||||
# We are forcing the 'export_' prefix on methods that are
|
||||
# callable through XML-RPC to prevent potential security
|
||||
# problems
|
||||
func = getattr(self, 'export_' + method)
|
||||
except AttributeError:
|
||||
raise Exception('method "%s" is not supported' % method)
|
||||
else:
|
||||
return func(*params)
|
||||
|
||||
def export_add(self, x, y):
|
||||
return x + y
|
||||
|
||||
server = MathServer(("localhost", 8000))
|
||||
server.serve_forever()
|
||||
|
||||
5. CGI script:
|
||||
|
||||
server = CGIXMLRPCRequestHandler()
|
||||
server.register_function(pow)
|
||||
server.handle_request()
|
||||
"""
|
||||
|
||||
# Written by Brian Quinlan (brian@sweetapp.com).
|
||||
# Based on code written by Fredrik Lundh.
|
||||
|
||||
try:
|
||||
True
|
||||
False
|
||||
except:
|
||||
import __builtin__
|
||||
setattr(__builtin__, 'True', 1) #Python 3.0 does not accept __builtin__.True = 1 in its syntax
|
||||
setattr(__builtin__, 'False', 0)
|
||||
|
||||
|
||||
import _pydev_xmlrpclib as xmlrpclib
|
||||
from _pydev_xmlrpclib import Fault
|
||||
import _pydev_SocketServer as SocketServer
|
||||
import _pydev_BaseHTTPServer as BaseHTTPServer
|
||||
import sys
|
||||
import os
|
||||
try:
|
||||
import fcntl
|
||||
except ImportError:
|
||||
fcntl = None
|
||||
|
||||
def resolve_dotted_attribute(obj, attr, allow_dotted_names=True):
|
||||
"""resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
|
||||
|
||||
Resolves a dotted attribute name to an object. Raises
|
||||
an AttributeError if any attribute in the chain starts with a '_'.
|
||||
|
||||
If the optional allow_dotted_names argument is false, dots are not
|
||||
supported and this function operates similar to getattr(obj, attr).
|
||||
"""
|
||||
|
||||
if allow_dotted_names:
|
||||
attrs = attr.split('.')
|
||||
else:
|
||||
attrs = [attr]
|
||||
|
||||
for i in attrs:
|
||||
if i.startswith('_'):
|
||||
raise AttributeError(
|
||||
'attempt to access private attribute "%s"' % i
|
||||
)
|
||||
else:
|
||||
obj = getattr(obj, i)
|
||||
return obj
|
||||
|
||||
def list_public_methods(obj):
|
||||
"""Returns a list of attribute strings, found in the specified
|
||||
object, which represent callable attributes"""
|
||||
|
||||
return [member for member in dir(obj)
|
||||
if not member.startswith('_') and
|
||||
callable(getattr(obj, member))]
|
||||
|
||||
def remove_duplicates(lst):
|
||||
"""remove_duplicates([2,2,2,1,3,3]) => [3,1,2]
|
||||
|
||||
Returns a copy of a list without duplicates. Every list
|
||||
item must be hashable and the order of the items in the
|
||||
resulting list is not defined.
|
||||
"""
|
||||
u = {}
|
||||
for x in lst:
|
||||
u[x] = 1
|
||||
|
||||
return u.keys()
|
||||
|
||||
class SimpleXMLRPCDispatcher:
|
||||
"""Mix-in class that dispatches XML-RPC requests.
|
||||
|
||||
This class is used to register XML-RPC method handlers
|
||||
and then to dispatch them. There should never be any
|
||||
reason to instantiate this class directly.
|
||||
"""
|
||||
|
||||
def __init__(self, allow_none, encoding):
|
||||
self.funcs = {}
|
||||
self.instance = None
|
||||
self.allow_none = allow_none
|
||||
self.encoding = encoding
|
||||
|
||||
def register_instance(self, instance, allow_dotted_names=False):
|
||||
"""Registers an instance to respond to XML-RPC requests.
|
||||
|
||||
Only one instance can be installed at a time.
|
||||
|
||||
If the registered instance has a _dispatch method then that
|
||||
method will be called with the name of the XML-RPC method and
|
||||
its parameters as a tuple
|
||||
e.g. instance._dispatch('add',(2,3))
|
||||
|
||||
If the registered instance does not have a _dispatch method
|
||||
then the instance will be searched to find a matching method
|
||||
and, if found, will be called. Methods beginning with an '_'
|
||||
are considered private and will not be called by
|
||||
SimpleXMLRPCServer.
|
||||
|
||||
If a registered function matches a XML-RPC request, then it
|
||||
will be called instead of the registered instance.
|
||||
|
||||
If the optional allow_dotted_names argument is true and the
|
||||
instance does not have a _dispatch method, method names
|
||||
containing dots are supported and resolved, as long as none of
|
||||
the name segments start with an '_'.
|
||||
|
||||
*** SECURITY WARNING: ***
|
||||
|
||||
Enabling the allow_dotted_names options allows intruders
|
||||
to access your module's global variables and may allow
|
||||
intruders to execute arbitrary code on your machine. Only
|
||||
use this option on a secure, closed network.
|
||||
|
||||
"""
|
||||
|
||||
self.instance = instance
|
||||
self.allow_dotted_names = allow_dotted_names
|
||||
|
||||
def register_function(self, function, name=None):
|
||||
"""Registers a function to respond to XML-RPC requests.
|
||||
|
||||
The optional name argument can be used to set a Unicode name
|
||||
for the function.
|
||||
"""
|
||||
|
||||
if name is None:
|
||||
name = function.__name__
|
||||
self.funcs[name] = function
|
||||
|
||||
def register_introspection_functions(self):
|
||||
"""Registers the XML-RPC introspection methods in the system
|
||||
namespace.
|
||||
|
||||
see http://xmlrpc.usefulinc.com/doc/reserved.html
|
||||
"""
|
||||
|
||||
self.funcs.update({'system.listMethods' : self.system_listMethods,
|
||||
'system.methodSignature' : self.system_methodSignature,
|
||||
'system.methodHelp' : self.system_methodHelp})
|
||||
|
||||
def register_multicall_functions(self):
|
||||
"""Registers the XML-RPC multicall method in the system
|
||||
namespace.
|
||||
|
||||
see http://www.xmlrpc.com/discuss/msgReader$1208"""
|
||||
|
||||
self.funcs.update({'system.multicall' : self.system_multicall})
|
||||
|
||||
def _marshaled_dispatch(self, data, dispatch_method=None):
|
||||
"""Dispatches an XML-RPC method from marshalled (XML) data.
|
||||
|
||||
XML-RPC methods are dispatched from the marshalled (XML) data
|
||||
using the _dispatch method and the result is returned as
|
||||
marshalled data. For backwards compatibility, a dispatch
|
||||
function can be provided as an argument (see comment in
|
||||
SimpleXMLRPCRequestHandler.do_POST) but overriding the
|
||||
existing method through subclassing is the prefered means
|
||||
of changing method dispatch behavior.
|
||||
"""
|
||||
try:
|
||||
params, method = xmlrpclib.loads(data)
|
||||
|
||||
# generate response
|
||||
if dispatch_method is not None:
|
||||
response = dispatch_method(method, params)
|
||||
else:
|
||||
response = self._dispatch(method, params)
|
||||
# wrap response in a singleton tuple
|
||||
response = (response,)
|
||||
response = xmlrpclib.dumps(response, methodresponse=1,
|
||||
allow_none=self.allow_none, encoding=self.encoding)
|
||||
except Fault, fault:
|
||||
response = xmlrpclib.dumps(fault, allow_none=self.allow_none,
|
||||
encoding=self.encoding)
|
||||
except:
|
||||
# report exception back to server
|
||||
response = xmlrpclib.dumps(
|
||||
xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)), #@UndefinedVariable exc_value only available when we actually have an exception
|
||||
encoding=self.encoding, allow_none=self.allow_none,
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
def system_listMethods(self):
|
||||
"""system.listMethods() => ['add', 'subtract', 'multiple']
|
||||
|
||||
Returns a list of the methods supported by the server."""
|
||||
|
||||
methods = self.funcs.keys()
|
||||
if self.instance is not None:
|
||||
# Instance can implement _listMethod to return a list of
|
||||
# methods
|
||||
if hasattr(self.instance, '_listMethods'):
|
||||
methods = remove_duplicates(
|
||||
methods + self.instance._listMethods()
|
||||
)
|
||||
# if the instance has a _dispatch method then we
|
||||
# don't have enough information to provide a list
|
||||
# of methods
|
||||
elif not hasattr(self.instance, '_dispatch'):
|
||||
methods = remove_duplicates(
|
||||
methods + list_public_methods(self.instance)
|
||||
)
|
||||
methods.sort()
|
||||
return methods
|
||||
|
||||
def system_methodSignature(self, method_name):
|
||||
"""system.methodSignature('add') => [double, int, int]
|
||||
|
||||
Returns a list describing the signature of the method. In the
|
||||
above example, the add method takes two integers as arguments
|
||||
and returns a double result.
|
||||
|
||||
This server does NOT support system.methodSignature."""
|
||||
|
||||
# See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
|
||||
|
||||
return 'signatures not supported'
|
||||
|
||||
def system_methodHelp(self, method_name):
|
||||
"""system.methodHelp('add') => "Adds two integers together"
|
||||
|
||||
Returns a string containing documentation for the specified method."""
|
||||
|
||||
method = None
|
||||
if self.funcs.has_key(method_name):
|
||||
method = self.funcs[method_name]
|
||||
elif self.instance is not None:
|
||||
# Instance can implement _methodHelp to return help for a method
|
||||
if hasattr(self.instance, '_methodHelp'):
|
||||
return self.instance._methodHelp(method_name)
|
||||
# if the instance has a _dispatch method then we
|
||||
# don't have enough information to provide help
|
||||
elif not hasattr(self.instance, '_dispatch'):
|
||||
try:
|
||||
method = resolve_dotted_attribute(
|
||||
self.instance,
|
||||
method_name,
|
||||
self.allow_dotted_names
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Note that we aren't checking that the method actually
|
||||
# be a callable object of some kind
|
||||
if method is None:
|
||||
return ""
|
||||
else:
|
||||
try:
|
||||
import pydoc
|
||||
except ImportError:
|
||||
return "" #not there for jython
|
||||
else:
|
||||
return pydoc.getdoc(method)
|
||||
|
||||
def system_multicall(self, call_list):
|
||||
"""system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
|
||||
[[4], ...]
|
||||
|
||||
Allows the caller to package multiple XML-RPC calls into a single
|
||||
request.
|
||||
|
||||
See http://www.xmlrpc.com/discuss/msgReader$1208
|
||||
"""
|
||||
|
||||
results = []
|
||||
for call in call_list:
|
||||
method_name = call['methodName']
|
||||
params = call['params']
|
||||
|
||||
try:
|
||||
# XXX A marshalling error in any response will fail the entire
|
||||
# multicall. If someone cares they should fix this.
|
||||
results.append([self._dispatch(method_name, params)])
|
||||
except Fault, fault:
|
||||
results.append(
|
||||
{'faultCode' : fault.faultCode,
|
||||
'faultString' : fault.faultString}
|
||||
)
|
||||
except:
|
||||
results.append(
|
||||
{'faultCode' : 1,
|
||||
'faultString' : "%s:%s" % (sys.exc_type, sys.exc_value)} #@UndefinedVariable exc_value only available when we actually have an exception
|
||||
)
|
||||
return results
|
||||
|
||||
def _dispatch(self, method, params):
|
||||
"""Dispatches the XML-RPC method.
|
||||
|
||||
XML-RPC calls are forwarded to a registered function that
|
||||
matches the called XML-RPC method name. If no such function
|
||||
exists then the call is forwarded to the registered instance,
|
||||
if available.
|
||||
|
||||
If the registered instance has a _dispatch method then that
|
||||
method will be called with the name of the XML-RPC method and
|
||||
its parameters as a tuple
|
||||
e.g. instance._dispatch('add',(2,3))
|
||||
|
||||
If the registered instance does not have a _dispatch method
|
||||
then the instance will be searched to find a matching method
|
||||
and, if found, will be called.
|
||||
|
||||
Methods beginning with an '_' are considered private and will
|
||||
not be called.
|
||||
"""
|
||||
|
||||
func = None
|
||||
try:
|
||||
# check to see if a matching function has been registered
|
||||
func = self.funcs[method]
|
||||
except KeyError:
|
||||
if self.instance is not None:
|
||||
# check for a _dispatch method
|
||||
if hasattr(self.instance, '_dispatch'):
|
||||
return self.instance._dispatch(method, params)
|
||||
else:
|
||||
# call instance method directly
|
||||
try:
|
||||
func = resolve_dotted_attribute(
|
||||
self.instance,
|
||||
method,
|
||||
self.allow_dotted_names
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if func is not None:
|
||||
return func(*params)
|
||||
else:
|
||||
raise Exception('method "%s" is not supported' % method)
|
||||
|
||||
class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
"""Simple XML-RPC request handler class.
|
||||
|
||||
Handles all HTTP POST requests and attempts to decode them as
|
||||
XML-RPC requests.
|
||||
"""
|
||||
|
||||
# Class attribute listing the accessible path components;
|
||||
# paths not on this list will result in a 404 error.
|
||||
rpc_paths = ('/', '/RPC2')
|
||||
|
||||
def is_rpc_path_valid(self):
|
||||
if self.rpc_paths:
|
||||
return self.path in self.rpc_paths
|
||||
else:
|
||||
# If .rpc_paths is empty, just assume all paths are legal
|
||||
return True
|
||||
|
||||
def do_POST(self):
|
||||
"""Handles the HTTP POST request.
|
||||
|
||||
Attempts to interpret all HTTP POST requests as XML-RPC calls,
|
||||
which are forwarded to the server's _dispatch method for handling.
|
||||
"""
|
||||
|
||||
# Check that the path is legal
|
||||
if not self.is_rpc_path_valid():
|
||||
self.report_404()
|
||||
return
|
||||
|
||||
try:
|
||||
# Get arguments by reading body of request.
|
||||
# We read this in chunks to avoid straining
|
||||
# socket.read(); around the 10 or 15Mb mark, some platforms
|
||||
# begin to have problems (bug #792570).
|
||||
max_chunk_size = 10 * 1024 * 1024
|
||||
size_remaining = int(self.headers["content-length"])
|
||||
L = []
|
||||
while size_remaining:
|
||||
chunk_size = min(size_remaining, max_chunk_size)
|
||||
L.append(self.rfile.read(chunk_size))
|
||||
size_remaining -= len(L[-1])
|
||||
data = ''.join(L)
|
||||
|
||||
# In previous versions of SimpleXMLRPCServer, _dispatch
|
||||
# could be overridden in this class, instead of in
|
||||
# SimpleXMLRPCDispatcher. To maintain backwards compatibility,
|
||||
# check to see if a subclass implements _dispatch and dispatch
|
||||
# using that method if present.
|
||||
response = self.server._marshaled_dispatch(
|
||||
data, getattr(self, '_dispatch', None)
|
||||
)
|
||||
except: # This should only happen if the module is buggy
|
||||
# internal error, report as HTTP server error
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
else:
|
||||
# got a valid XML RPC response
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/xml")
|
||||
self.send_header("Content-length", str(len(response)))
|
||||
self.end_headers()
|
||||
self.wfile.write(response)
|
||||
|
||||
# shut down the connection
|
||||
self.wfile.flush()
|
||||
self.connection.shutdown(1)
|
||||
|
||||
def report_404 (self):
|
||||
# Report a 404 error
|
||||
self.send_response(404)
|
||||
response = 'No such page'
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.send_header("Content-length", str(len(response)))
|
||||
self.end_headers()
|
||||
self.wfile.write(response)
|
||||
# shut down the connection
|
||||
self.wfile.flush()
|
||||
self.connection.shutdown(1)
|
||||
|
||||
def log_request(self, code='-', size='-'):
|
||||
"""Selectively log an accepted request."""
|
||||
|
||||
if self.server.logRequests:
|
||||
BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size)
|
||||
|
||||
class SimpleXMLRPCServer(SocketServer.TCPServer,
|
||||
SimpleXMLRPCDispatcher):
|
||||
"""Simple XML-RPC server.
|
||||
|
||||
Simple XML-RPC server that allows functions and a single instance
|
||||
to be installed to handle requests. The default implementation
|
||||
attempts to dispatch XML-RPC calls to the functions or instance
|
||||
installed in the server. Override the _dispatch method inhereted
|
||||
from SimpleXMLRPCDispatcher to change this behavior.
|
||||
"""
|
||||
|
||||
allow_reuse_address = True
|
||||
|
||||
def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
|
||||
logRequests=True, allow_none=False, encoding=None):
|
||||
self.logRequests = logRequests
|
||||
|
||||
SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
|
||||
SocketServer.TCPServer.__init__(self, addr, requestHandler)
|
||||
|
||||
# [Bug #1222790] If possible, set close-on-exec flag; if a
|
||||
# method spawns a subprocess, the subprocess shouldn't have
|
||||
# the listening socket open.
|
||||
if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
|
||||
flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
|
||||
flags |= fcntl.FD_CLOEXEC
|
||||
fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
|
||||
|
||||
class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
|
||||
"""Simple handler for XML-RPC data passed through CGI."""
|
||||
|
||||
def __init__(self, allow_none=False, encoding=None):
|
||||
SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
|
||||
|
||||
def handle_xmlrpc(self, request_text):
|
||||
"""Handle a single XML-RPC request"""
|
||||
|
||||
response = self._marshaled_dispatch(request_text)
|
||||
|
||||
sys.stdout.write('Content-Type: text/xml\n')
|
||||
sys.stdout.write('Content-Length: %d\n' % len(response))
|
||||
sys.stdout.write('\n')
|
||||
|
||||
sys.stdout.write(response)
|
||||
|
||||
def handle_get(self):
|
||||
"""Handle a single HTTP GET request.
|
||||
|
||||
Default implementation indicates an error because
|
||||
XML-RPC uses the POST method.
|
||||
"""
|
||||
|
||||
code = 400
|
||||
message, explain = \
|
||||
BaseHTTPServer.BaseHTTPRequestHandler.responses[code]
|
||||
|
||||
response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % { #@UndefinedVariable
|
||||
'code' : code,
|
||||
'message' : message,
|
||||
'explain' : explain
|
||||
}
|
||||
sys.stdout.write('Status: %d %s\n' % (code, message))
|
||||
sys.stdout.write('Content-Type: text/html\n')
|
||||
sys.stdout.write('Content-Length: %d\n' % len(response))
|
||||
sys.stdout.write('\n')
|
||||
|
||||
sys.stdout.write(response)
|
||||
|
||||
def handle_request(self, request_text=None):
|
||||
"""Handle a single XML-RPC request passed through a CGI post method.
|
||||
|
||||
If no XML data is given then it is read from stdin. The resulting
|
||||
XML-RPC response is printed to stdout along with the correct HTTP
|
||||
headers.
|
||||
"""
|
||||
|
||||
if request_text is None and \
|
||||
os.environ.get('REQUEST_METHOD', None) == 'GET':
|
||||
self.handle_get()
|
||||
else:
|
||||
# POST data is normally available through stdin
|
||||
if request_text is None:
|
||||
request_text = sys.stdin.read()
|
||||
|
||||
self.handle_xmlrpc(request_text)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.stdout.write('Running XML-RPC server on port 8000\n')
|
||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
||||
server.register_function(pow)
|
||||
server.register_function(lambda x, y: x + y, 'add')
|
||||
server.serve_forever()
|
||||
@@ -1,715 +0,0 @@
|
||||
"""Generic socket server classes.
|
||||
|
||||
This module tries to capture the various aspects of defining a server:
|
||||
|
||||
For socket-based servers:
|
||||
|
||||
- address family:
|
||||
- AF_INET{,6}: IP (Internet Protocol) sockets (default)
|
||||
- AF_UNIX: Unix domain sockets
|
||||
- others, e.g. AF_DECNET are conceivable (see <socket.h>
|
||||
- socket type:
|
||||
- SOCK_STREAM (reliable stream, e.g. TCP)
|
||||
- SOCK_DGRAM (datagrams, e.g. UDP)
|
||||
|
||||
For request-based servers (including socket-based):
|
||||
|
||||
- client address verification before further looking at the request
|
||||
(This is actually a hook for any processing that needs to look
|
||||
at the request before anything else, e.g. logging)
|
||||
- how to handle multiple requests:
|
||||
- synchronous (one request is handled at a time)
|
||||
- forking (each request is handled by a new process)
|
||||
- threading (each request is handled by a new thread)
|
||||
|
||||
The classes in this module favor the server type that is simplest to
|
||||
write: a synchronous TCP/IP server. This is bad class design, but
|
||||
save some typing. (There's also the issue that a deep class hierarchy
|
||||
slows down method lookups.)
|
||||
|
||||
There are five classes in an inheritance diagram, four of which represent
|
||||
synchronous servers of four types:
|
||||
|
||||
+------------+
|
||||
| BaseServer |
|
||||
+------------+
|
||||
|
|
||||
v
|
||||
+-----------+ +------------------+
|
||||
| TCPServer |------->| UnixStreamServer |
|
||||
+-----------+ +------------------+
|
||||
|
|
||||
v
|
||||
+-----------+ +--------------------+
|
||||
| UDPServer |------->| UnixDatagramServer |
|
||||
+-----------+ +--------------------+
|
||||
|
||||
Note that UnixDatagramServer derives from UDPServer, not from
|
||||
UnixStreamServer -- the only difference between an IP and a Unix
|
||||
stream server is the address family, which is simply repeated in both
|
||||
unix server classes.
|
||||
|
||||
Forking and threading versions of each type of server can be created
|
||||
using the ForkingMixIn and ThreadingMixIn mix-in classes. For
|
||||
instance, a threading UDP server class is created as follows:
|
||||
|
||||
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
|
||||
|
||||
The Mix-in class must come first, since it overrides a method defined
|
||||
in UDPServer! Setting the various member variables also changes
|
||||
the behavior of the underlying server mechanism.
|
||||
|
||||
To implement a service, you must derive a class from
|
||||
BaseRequestHandler and redefine its handle() method. You can then run
|
||||
various versions of the service by combining one of the server classes
|
||||
with your request handler class.
|
||||
|
||||
The request handler class must be different for datagram or stream
|
||||
services. This can be hidden by using the request handler
|
||||
subclasses StreamRequestHandler or DatagramRequestHandler.
|
||||
|
||||
Of course, you still have to use your head!
|
||||
|
||||
For instance, it makes no sense to use a forking server if the service
|
||||
contains state in memory that can be modified by requests (since the
|
||||
modifications in the child process would never reach the initial state
|
||||
kept in the parent process and passed to each child). In this case,
|
||||
you can use a threading server, but you will probably have to use
|
||||
locks to avoid two requests that come in nearly simultaneous to apply
|
||||
conflicting changes to the server state.
|
||||
|
||||
On the other hand, if you are building e.g. an HTTP server, where all
|
||||
data is stored externally (e.g. in the file system), a synchronous
|
||||
class will essentially render the service "deaf" while one request is
|
||||
being handled -- which may be for a very long time if a client is slow
|
||||
to read all the data it has requested. Here a threading or forking
|
||||
server is appropriate.
|
||||
|
||||
In some cases, it may be appropriate to process part of a request
|
||||
synchronously, but to finish processing in a forked child depending on
|
||||
the request data. This can be implemented by using a synchronous
|
||||
server and doing an explicit fork in the request handler class
|
||||
handle() method.
|
||||
|
||||
Another approach to handling multiple simultaneous requests in an
|
||||
environment that supports neither threads nor fork (or where these are
|
||||
too expensive or inappropriate for the service) is to maintain an
|
||||
explicit table of partially finished requests and to use select() to
|
||||
decide which request to work on next (or whether to handle a new
|
||||
incoming request). This is particularly important for stream services
|
||||
where each client can potentially be connected for a long time (if
|
||||
threads or subprocesses cannot be used).
|
||||
|
||||
Future work:
|
||||
- Standard classes for Sun RPC (which uses either UDP or TCP)
|
||||
- Standard mix-in classes to implement various authentication
|
||||
and encryption schemes
|
||||
- Standard framework for select-based multiplexing
|
||||
|
||||
XXX Open problems:
|
||||
- What to do with out-of-band data?
|
||||
|
||||
BaseServer:
|
||||
- split generic "request" functionality out into BaseServer class.
|
||||
Copyright (C) 2000 Luke Kenneth Casson Leighton <lkcl@samba.org>
|
||||
|
||||
example: read entries from a SQL database (requires overriding
|
||||
get_request() to return a table entry from the database).
|
||||
entry is processed by a RequestHandlerClass.
|
||||
|
||||
"""
|
||||
|
||||
# Author of the BaseServer patch: Luke Kenneth Casson Leighton
|
||||
|
||||
# XXX Warning!
|
||||
# There is a test suite for this module, but it cannot be run by the
|
||||
# standard regression test.
|
||||
# To run it manually, run Lib/test/test_socketserver.py.
|
||||
|
||||
__version__ = "0.4"
|
||||
|
||||
|
||||
import _pydev_socket as socket
|
||||
import _pydev_select as select
|
||||
import sys
|
||||
import os
|
||||
try:
|
||||
import _pydev_threading as threading
|
||||
except ImportError:
|
||||
import dummy_threading as threading
|
||||
|
||||
__all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer",
|
||||
"ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler",
|
||||
"StreamRequestHandler","DatagramRequestHandler",
|
||||
"ThreadingMixIn", "ForkingMixIn"]
|
||||
if hasattr(socket, "AF_UNIX"):
|
||||
__all__.extend(["UnixStreamServer","UnixDatagramServer",
|
||||
"ThreadingUnixStreamServer",
|
||||
"ThreadingUnixDatagramServer"])
|
||||
|
||||
class BaseServer:
|
||||
|
||||
"""Base class for server classes.
|
||||
|
||||
Methods for the caller:
|
||||
|
||||
- __init__(server_address, RequestHandlerClass)
|
||||
- serve_forever(poll_interval=0.5)
|
||||
- shutdown()
|
||||
- handle_request() # if you do not use serve_forever()
|
||||
- fileno() -> int # for select()
|
||||
|
||||
Methods that may be overridden:
|
||||
|
||||
- server_bind()
|
||||
- server_activate()
|
||||
- get_request() -> request, client_address
|
||||
- handle_timeout()
|
||||
- verify_request(request, client_address)
|
||||
- server_close()
|
||||
- process_request(request, client_address)
|
||||
- shutdown_request(request)
|
||||
- close_request(request)
|
||||
- handle_error()
|
||||
|
||||
Methods for derived classes:
|
||||
|
||||
- finish_request(request, client_address)
|
||||
|
||||
Class variables that may be overridden by derived classes or
|
||||
instances:
|
||||
|
||||
- timeout
|
||||
- address_family
|
||||
- socket_type
|
||||
- allow_reuse_address
|
||||
|
||||
Instance variables:
|
||||
|
||||
- RequestHandlerClass
|
||||
- socket
|
||||
|
||||
"""
|
||||
|
||||
timeout = None
|
||||
|
||||
def __init__(self, server_address, RequestHandlerClass):
|
||||
"""Constructor. May be extended, do not override."""
|
||||
self.server_address = server_address
|
||||
self.RequestHandlerClass = RequestHandlerClass
|
||||
self.__is_shut_down = threading.Event()
|
||||
self.__shutdown_request = False
|
||||
|
||||
def server_activate(self):
|
||||
"""Called by constructor to activate the server.
|
||||
|
||||
May be overridden.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def serve_forever(self, poll_interval=0.5):
|
||||
"""Handle one request at a time until shutdown.
|
||||
|
||||
Polls for shutdown every poll_interval seconds. Ignores
|
||||
self.timeout. If you need to do periodic tasks, do them in
|
||||
another thread.
|
||||
"""
|
||||
self.__is_shut_down.clear()
|
||||
try:
|
||||
while not self.__shutdown_request:
|
||||
# XXX: Consider using another file descriptor or
|
||||
# connecting to the socket to wake this up instead of
|
||||
# polling. Polling reduces our responsiveness to a
|
||||
# shutdown request and wastes cpu at all other times.
|
||||
r, w, e = select.select([self], [], [], poll_interval)
|
||||
if self in r:
|
||||
self._handle_request_noblock()
|
||||
finally:
|
||||
self.__shutdown_request = False
|
||||
self.__is_shut_down.set()
|
||||
|
||||
def shutdown(self):
|
||||
"""Stops the serve_forever loop.
|
||||
|
||||
Blocks until the loop has finished. This must be called while
|
||||
serve_forever() is running in another thread, or it will
|
||||
deadlock.
|
||||
"""
|
||||
self.__shutdown_request = True
|
||||
self.__is_shut_down.wait()
|
||||
|
||||
# The distinction between handling, getting, processing and
|
||||
# finishing a request is fairly arbitrary. Remember:
|
||||
#
|
||||
# - handle_request() is the top-level call. It calls
|
||||
# select, get_request(), verify_request() and process_request()
|
||||
# - get_request() is different for stream or datagram sockets
|
||||
# - process_request() is the place that may fork a new process
|
||||
# or create a new thread to finish the request
|
||||
# - finish_request() instantiates the request handler class;
|
||||
# this constructor will handle the request all by itself
|
||||
|
||||
def handle_request(self):
|
||||
"""Handle one request, possibly blocking.
|
||||
|
||||
Respects self.timeout.
|
||||
"""
|
||||
# Support people who used socket.settimeout() to escape
|
||||
# handle_request before self.timeout was available.
|
||||
timeout = self.socket.gettimeout()
|
||||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
elif self.timeout is not None:
|
||||
timeout = min(timeout, self.timeout)
|
||||
fd_sets = select.select([self], [], [], timeout)
|
||||
if not fd_sets[0]:
|
||||
self.handle_timeout()
|
||||
return
|
||||
self._handle_request_noblock()
|
||||
|
||||
def _handle_request_noblock(self):
|
||||
"""Handle one request, without blocking.
|
||||
|
||||
I assume that select.select has returned that the socket is
|
||||
readable before this function was called, so there should be
|
||||
no risk of blocking in get_request().
|
||||
"""
|
||||
try:
|
||||
request, client_address = self.get_request()
|
||||
except socket.error:
|
||||
return
|
||||
if self.verify_request(request, client_address):
|
||||
try:
|
||||
self.process_request(request, client_address)
|
||||
except:
|
||||
self.handle_error(request, client_address)
|
||||
self.shutdown_request(request)
|
||||
|
||||
def handle_timeout(self):
|
||||
"""Called if no new request arrives within self.timeout.
|
||||
|
||||
Overridden by ForkingMixIn.
|
||||
"""
|
||||
pass
|
||||
|
||||
def verify_request(self, request, client_address):
|
||||
"""Verify the request. May be overridden.
|
||||
|
||||
Return True if we should proceed with this request.
|
||||
|
||||
"""
|
||||
return True
|
||||
|
||||
def process_request(self, request, client_address):
|
||||
"""Call finish_request.
|
||||
|
||||
Overridden by ForkingMixIn and ThreadingMixIn.
|
||||
|
||||
"""
|
||||
self.finish_request(request, client_address)
|
||||
self.shutdown_request(request)
|
||||
|
||||
def server_close(self):
|
||||
"""Called to clean-up the server.
|
||||
|
||||
May be overridden.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def finish_request(self, request, client_address):
|
||||
"""Finish one request by instantiating RequestHandlerClass."""
|
||||
self.RequestHandlerClass(request, client_address, self)
|
||||
|
||||
def shutdown_request(self, request):
|
||||
"""Called to shutdown and close an individual request."""
|
||||
self.close_request(request)
|
||||
|
||||
def close_request(self, request):
|
||||
"""Called to clean up an individual request."""
|
||||
pass
|
||||
|
||||
def handle_error(self, request, client_address):
|
||||
"""Handle an error gracefully. May be overridden.
|
||||
|
||||
The default is to print a traceback and continue.
|
||||
|
||||
"""
|
||||
print '-'*40
|
||||
print 'Exception happened during processing of request from',
|
||||
print client_address
|
||||
import traceback
|
||||
traceback.print_exc() # XXX But this goes to stderr!
|
||||
print '-'*40
|
||||
|
||||
|
||||
class TCPServer(BaseServer):
|
||||
|
||||
"""Base class for various socket-based server classes.
|
||||
|
||||
Defaults to synchronous IP stream (i.e., TCP).
|
||||
|
||||
Methods for the caller:
|
||||
|
||||
- __init__(server_address, RequestHandlerClass, bind_and_activate=True)
|
||||
- serve_forever(poll_interval=0.5)
|
||||
- shutdown()
|
||||
- handle_request() # if you don't use serve_forever()
|
||||
- fileno() -> int # for select()
|
||||
|
||||
Methods that may be overridden:
|
||||
|
||||
- server_bind()
|
||||
- server_activate()
|
||||
- get_request() -> request, client_address
|
||||
- handle_timeout()
|
||||
- verify_request(request, client_address)
|
||||
- process_request(request, client_address)
|
||||
- shutdown_request(request)
|
||||
- close_request(request)
|
||||
- handle_error()
|
||||
|
||||
Methods for derived classes:
|
||||
|
||||
- finish_request(request, client_address)
|
||||
|
||||
Class variables that may be overridden by derived classes or
|
||||
instances:
|
||||
|
||||
- timeout
|
||||
- address_family
|
||||
- socket_type
|
||||
- request_queue_size (only for stream sockets)
|
||||
- allow_reuse_address
|
||||
|
||||
Instance variables:
|
||||
|
||||
- server_address
|
||||
- RequestHandlerClass
|
||||
- socket
|
||||
|
||||
"""
|
||||
|
||||
address_family = socket.AF_INET
|
||||
|
||||
socket_type = socket.SOCK_STREAM
|
||||
|
||||
request_queue_size = 5
|
||||
|
||||
allow_reuse_address = False
|
||||
|
||||
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
|
||||
"""Constructor. May be extended, do not override."""
|
||||
BaseServer.__init__(self, server_address, RequestHandlerClass)
|
||||
self.socket = socket.socket(self.address_family,
|
||||
self.socket_type)
|
||||
if bind_and_activate:
|
||||
self.server_bind()
|
||||
self.server_activate()
|
||||
|
||||
def server_bind(self):
|
||||
"""Called by constructor to bind the socket.
|
||||
|
||||
May be overridden.
|
||||
|
||||
"""
|
||||
if self.allow_reuse_address:
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.socket.bind(self.server_address)
|
||||
self.server_address = self.socket.getsockname()
|
||||
|
||||
def server_activate(self):
|
||||
"""Called by constructor to activate the server.
|
||||
|
||||
May be overridden.
|
||||
|
||||
"""
|
||||
self.socket.listen(self.request_queue_size)
|
||||
|
||||
def server_close(self):
|
||||
"""Called to clean-up the server.
|
||||
|
||||
May be overridden.
|
||||
|
||||
"""
|
||||
self.socket.close()
|
||||
|
||||
def fileno(self):
|
||||
"""Return socket file number.
|
||||
|
||||
Interface required by select().
|
||||
|
||||
"""
|
||||
return self.socket.fileno()
|
||||
|
||||
def get_request(self):
|
||||
"""Get the request and client address from the socket.
|
||||
|
||||
May be overridden.
|
||||
|
||||
"""
|
||||
return self.socket.accept()
|
||||
|
||||
def shutdown_request(self, request):
|
||||
"""Called to shutdown and close an individual request."""
|
||||
try:
|
||||
#explicitly shutdown. socket.close() merely releases
|
||||
#the socket and waits for GC to perform the actual close.
|
||||
request.shutdown(socket.SHUT_WR)
|
||||
except socket.error:
|
||||
pass #some platforms may raise ENOTCONN here
|
||||
self.close_request(request)
|
||||
|
||||
def close_request(self, request):
|
||||
"""Called to clean up an individual request."""
|
||||
request.close()
|
||||
|
||||
|
||||
class UDPServer(TCPServer):
|
||||
|
||||
"""UDP server class."""
|
||||
|
||||
allow_reuse_address = False
|
||||
|
||||
socket_type = socket.SOCK_DGRAM
|
||||
|
||||
max_packet_size = 8192
|
||||
|
||||
def get_request(self):
|
||||
data, client_addr = self.socket.recvfrom(self.max_packet_size)
|
||||
return (data, self.socket), client_addr
|
||||
|
||||
def server_activate(self):
|
||||
# No need to call listen() for UDP.
|
||||
pass
|
||||
|
||||
def shutdown_request(self, request):
|
||||
# No need to shutdown anything.
|
||||
self.close_request(request)
|
||||
|
||||
def close_request(self, request):
|
||||
# No need to close anything.
|
||||
pass
|
||||
|
||||
class ForkingMixIn:
|
||||
|
||||
"""Mix-in class to handle each request in a new process."""
|
||||
|
||||
timeout = 300
|
||||
active_children = None
|
||||
max_children = 40
|
||||
|
||||
def collect_children(self):
|
||||
"""Internal routine to wait for children that have exited."""
|
||||
if self.active_children is None: return
|
||||
while len(self.active_children) >= self.max_children:
|
||||
# XXX: This will wait for any child process, not just ones
|
||||
# spawned by this library. This could confuse other
|
||||
# libraries that expect to be able to wait for their own
|
||||
# children.
|
||||
try:
|
||||
pid, status = os.waitpid(0, 0)
|
||||
except os.error:
|
||||
pid = None
|
||||
if pid not in self.active_children: continue
|
||||
self.active_children.remove(pid)
|
||||
|
||||
# XXX: This loop runs more system calls than it ought
|
||||
# to. There should be a way to put the active_children into a
|
||||
# process group and then use os.waitpid(-pgid) to wait for any
|
||||
# of that set, but I couldn't find a way to allocate pgids
|
||||
# that couldn't collide.
|
||||
for child in self.active_children:
|
||||
try:
|
||||
pid, status = os.waitpid(child, os.WNOHANG)
|
||||
except os.error:
|
||||
pid = None
|
||||
if not pid: continue
|
||||
try:
|
||||
self.active_children.remove(pid)
|
||||
except ValueError, e:
|
||||
raise ValueError('%s. x=%d and list=%r' % (e.message, pid,
|
||||
self.active_children))
|
||||
|
||||
def handle_timeout(self):
|
||||
"""Wait for zombies after self.timeout seconds of inactivity.
|
||||
|
||||
May be extended, do not override.
|
||||
"""
|
||||
self.collect_children()
|
||||
|
||||
def process_request(self, request, client_address):
|
||||
"""Fork a new subprocess to process the request."""
|
||||
self.collect_children()
|
||||
pid = os.fork()
|
||||
if pid:
|
||||
# Parent process
|
||||
if self.active_children is None:
|
||||
self.active_children = []
|
||||
self.active_children.append(pid)
|
||||
self.close_request(request) #close handle in parent process
|
||||
return
|
||||
else:
|
||||
# Child process.
|
||||
# This must never return, hence os._exit()!
|
||||
try:
|
||||
self.finish_request(request, client_address)
|
||||
self.shutdown_request(request)
|
||||
os._exit(0)
|
||||
except:
|
||||
try:
|
||||
self.handle_error(request, client_address)
|
||||
self.shutdown_request(request)
|
||||
finally:
|
||||
os._exit(1)
|
||||
|
||||
|
||||
class ThreadingMixIn:
|
||||
"""Mix-in class to handle each request in a new thread."""
|
||||
|
||||
# Decides how threads will act upon termination of the
|
||||
# main process
|
||||
daemon_threads = False
|
||||
|
||||
def process_request_thread(self, request, client_address):
|
||||
"""Same as in BaseServer but as a thread.
|
||||
|
||||
In addition, exception handling is done here.
|
||||
|
||||
"""
|
||||
try:
|
||||
self.finish_request(request, client_address)
|
||||
self.shutdown_request(request)
|
||||
except:
|
||||
self.handle_error(request, client_address)
|
||||
self.shutdown_request(request)
|
||||
|
||||
def process_request(self, request, client_address):
|
||||
"""Start a new thread to process the request."""
|
||||
t = threading.Thread(target = self.process_request_thread,
|
||||
args = (request, client_address))
|
||||
t.daemon = self.daemon_threads
|
||||
t.start()
|
||||
|
||||
|
||||
class ForkingUDPServer(ForkingMixIn, UDPServer): pass
|
||||
class ForkingTCPServer(ForkingMixIn, TCPServer): pass
|
||||
|
||||
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
|
||||
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
|
||||
|
||||
if hasattr(socket, 'AF_UNIX'):
|
||||
|
||||
class UnixStreamServer(TCPServer):
|
||||
address_family = socket.AF_UNIX
|
||||
|
||||
class UnixDatagramServer(UDPServer):
|
||||
address_family = socket.AF_UNIX
|
||||
|
||||
class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass
|
||||
|
||||
class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass
|
||||
|
||||
class BaseRequestHandler:
|
||||
|
||||
"""Base class for request handler classes.
|
||||
|
||||
This class is instantiated for each request to be handled. The
|
||||
constructor sets the instance variables request, client_address
|
||||
and server, and then calls the handle() method. To implement a
|
||||
specific service, all you need to do is to derive a class which
|
||||
defines a handle() method.
|
||||
|
||||
The handle() method can find the request as self.request, the
|
||||
client address as self.client_address, and the server (in case it
|
||||
needs access to per-server information) as self.server. Since a
|
||||
separate instance is created for each request, the handle() method
|
||||
can define arbitrary other instance variariables.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, request, client_address, server):
|
||||
self.request = request
|
||||
self.client_address = client_address
|
||||
self.server = server
|
||||
self.setup()
|
||||
try:
|
||||
self.handle()
|
||||
finally:
|
||||
self.finish()
|
||||
|
||||
def setup(self):
|
||||
pass
|
||||
|
||||
def handle(self):
|
||||
pass
|
||||
|
||||
def finish(self):
|
||||
pass
|
||||
|
||||
|
||||
# The following two classes make it possible to use the same service
|
||||
# class for stream or datagram servers.
|
||||
# Each class sets up these instance variables:
|
||||
# - rfile: a file object from which receives the request is read
|
||||
# - wfile: a file object to which the reply is written
|
||||
# When the handle() method returns, wfile is flushed properly
|
||||
|
||||
|
||||
class StreamRequestHandler(BaseRequestHandler):
|
||||
|
||||
"""Define self.rfile and self.wfile for stream sockets."""
|
||||
|
||||
# Default buffer sizes for rfile, wfile.
|
||||
# We default rfile to buffered because otherwise it could be
|
||||
# really slow for large data (a getc() call per byte); we make
|
||||
# wfile unbuffered because (a) often after a write() we want to
|
||||
# read and we need to flush the line; (b) big writes to unbuffered
|
||||
# files are typically optimized by stdio even when big reads
|
||||
# aren't.
|
||||
rbufsize = -1
|
||||
wbufsize = 0
|
||||
|
||||
# A timeout to apply to the request socket, if not None.
|
||||
timeout = None
|
||||
|
||||
# Disable nagle algorithm for this socket, if True.
|
||||
# Use only when wbufsize != 0, to avoid small packets.
|
||||
disable_nagle_algorithm = False
|
||||
|
||||
def setup(self):
|
||||
self.connection = self.request
|
||||
if self.timeout is not None:
|
||||
self.connection.settimeout(self.timeout)
|
||||
if self.disable_nagle_algorithm:
|
||||
self.connection.setsockopt(socket.IPPROTO_TCP,
|
||||
socket.TCP_NODELAY, True)
|
||||
self.rfile = self.connection.makefile('rb', self.rbufsize)
|
||||
self.wfile = self.connection.makefile('wb', self.wbufsize)
|
||||
|
||||
def finish(self):
|
||||
if not self.wfile.closed:
|
||||
self.wfile.flush()
|
||||
self.wfile.close()
|
||||
self.rfile.close()
|
||||
|
||||
|
||||
class DatagramRequestHandler(BaseRequestHandler):
|
||||
|
||||
# XXX Regrettably, I cannot get this working on Linux;
|
||||
# s.recvfrom() doesn't return a meaningful client address.
|
||||
|
||||
"""Define self.rfile and self.wfile for datagram sockets."""
|
||||
|
||||
def setup(self):
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
self.packet, self.socket = self.request
|
||||
self.rfile = StringIO(self.packet)
|
||||
self.wfile = StringIO()
|
||||
|
||||
def finish(self):
|
||||
self.socket.sendto(self.wfile.getvalue(), self.client_address)
|
||||
@@ -1,6 +1,7 @@
|
||||
"""A multi-producer, multi-consumer queue."""
|
||||
|
||||
from _pydev_imps._pydev_time import time as _time
|
||||
from _pydev_imps import _pydev_thread
|
||||
try:
|
||||
import _pydev_threading as _threading
|
||||
except ImportError:
|
||||
@@ -30,7 +31,7 @@ class Queue:
|
||||
# that acquire mutex must release it before returning. mutex
|
||||
# is shared between the three conditions, so acquiring and
|
||||
# releasing the conditions also acquires and releases mutex.
|
||||
self.mutex = _threading.Lock()
|
||||
self.mutex = _pydev_thread.allocate_lock()
|
||||
# Notify not_empty whenever an item is added to the queue; a
|
||||
# thread waiting to get is notified then.
|
||||
self.not_empty = _threading.Condition(self.mutex)
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
from select import *
|
||||
from select import *
|
||||
|
||||
try:
|
||||
from gevent import monkey
|
||||
saved = monkey.saved['select']
|
||||
for key, val in saved.items():
|
||||
globals()[key] = val
|
||||
except:
|
||||
pass
|
||||
@@ -1 +1,9 @@
|
||||
from socket import *
|
||||
from socket import *
|
||||
|
||||
try:
|
||||
from gevent import monkey
|
||||
saved = monkey.saved['socket']
|
||||
for key, val in saved.items():
|
||||
globals()[key] = val
|
||||
except:
|
||||
pass
|
||||
@@ -2,3 +2,11 @@ try:
|
||||
from thread import *
|
||||
except:
|
||||
from _thread import * #Py3k
|
||||
|
||||
try:
|
||||
from gevent import monkey
|
||||
saved = monkey.saved['thread']
|
||||
for key, val in saved.items():
|
||||
globals()[key] = val
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
from time import *
|
||||
|
||||
try:
|
||||
from gevent import monkey
|
||||
saved = monkey.saved['time']
|
||||
for key, val in saved.items():
|
||||
globals()[key] = val
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -1,788 +0,0 @@
|
||||
"""Get useful information from live Python objects.
|
||||
|
||||
This module encapsulates the interface provided by the internal special
|
||||
attributes (func_*, co_*, im_*, tb_*, etc.) in a friendlier fashion.
|
||||
It also provides some help for examining source code and class layout.
|
||||
|
||||
Here are some of the useful functions provided by this module:
|
||||
|
||||
ismodule(), isclass(), ismethod(), isfunction(), istraceback(),
|
||||
isframe(), iscode(), isbuiltin(), isroutine() - check object types
|
||||
getmembers() - get members of an object that satisfy a given condition
|
||||
|
||||
getfile(), getsourcefile(), getsource() - find an object's source code
|
||||
getdoc(), getcomments() - get documentation on an object
|
||||
getmodule() - determine the module that an object came from
|
||||
getclasstree() - arrange classes so as to represent their hierarchy
|
||||
|
||||
getargspec(), getargvalues() - get info about function arguments
|
||||
formatargspec(), formatargvalues() - format an argument spec
|
||||
getouterframes(), getinnerframes() - get info about frames
|
||||
currentframe() - get the current stack frame
|
||||
stack(), trace() - get info about frames on the stack or in a traceback
|
||||
"""
|
||||
|
||||
# This module is in the public domain. No warranties.
|
||||
|
||||
__author__ = 'Ka-Ping Yee <ping@lfw.org>'
|
||||
__date__ = '1 Jan 2001'
|
||||
|
||||
import sys, os, types, string, re, imp, tokenize
|
||||
|
||||
# ----------------------------------------------------------- type-checking
|
||||
def ismodule(object):
|
||||
"""Return true if the object is a module.
|
||||
|
||||
Module objects provide these attributes:
|
||||
__doc__ documentation string
|
||||
__file__ filename (missing for built-in modules)"""
|
||||
return isinstance(object, types.ModuleType)
|
||||
|
||||
def isclass(object):
|
||||
"""Return true if the object is a class.
|
||||
|
||||
Class objects provide these attributes:
|
||||
__doc__ documentation string
|
||||
__module__ name of module in which this class was defined"""
|
||||
return isinstance(object, types.ClassType) or hasattr(object, '__bases__')
|
||||
|
||||
def ismethod(object):
|
||||
"""Return true if the object is an instance method.
|
||||
|
||||
Instance method objects provide these attributes:
|
||||
__doc__ documentation string
|
||||
__name__ name with which this method was defined
|
||||
im_class class object in which this method belongs
|
||||
im_func function object containing implementation of method
|
||||
im_self instance to which this method is bound, or None"""
|
||||
return isinstance(object, types.MethodType)
|
||||
|
||||
def ismethoddescriptor(object):
|
||||
"""Return true if the object is a method descriptor.
|
||||
|
||||
But not if ismethod() or isclass() or isfunction() are true.
|
||||
|
||||
This is new in Python 2.2, and, for example, is true of int.__add__.
|
||||
An object passing this test has a __get__ attribute but not a __set__
|
||||
attribute, but beyond that the set of attributes varies. __name__ is
|
||||
usually sensible, and __doc__ often is.
|
||||
|
||||
Methods implemented via descriptors that also pass one of the other
|
||||
tests return false from the ismethoddescriptor() test, simply because
|
||||
the other tests promise more -- you can, e.g., count on having the
|
||||
im_func attribute (etc) when an object passes ismethod()."""
|
||||
return (hasattr(object, "__get__")
|
||||
and not hasattr(object, "__set__") # else it's a data descriptor
|
||||
and not ismethod(object) # mutual exclusion
|
||||
and not isfunction(object)
|
||||
and not isclass(object))
|
||||
|
||||
def isfunction(object):
|
||||
"""Return true if the object is a user-defined function.
|
||||
|
||||
Function objects provide these attributes:
|
||||
__doc__ documentation string
|
||||
__name__ name with which this function was defined
|
||||
func_code code object containing compiled function bytecode
|
||||
func_defaults tuple of any default values for arguments
|
||||
func_doc (same as __doc__)
|
||||
func_globals global namespace in which this function was defined
|
||||
func_name (same as __name__)"""
|
||||
return isinstance(object, types.FunctionType)
|
||||
|
||||
def istraceback(object):
|
||||
"""Return true if the object is a traceback.
|
||||
|
||||
Traceback objects provide these attributes:
|
||||
tb_frame frame object at this level
|
||||
tb_lasti index of last attempted instruction in bytecode
|
||||
tb_lineno current line number in Python source code
|
||||
tb_next next inner traceback object (called by this level)"""
|
||||
return isinstance(object, types.TracebackType)
|
||||
|
||||
def isframe(object):
|
||||
"""Return true if the object is a frame object.
|
||||
|
||||
Frame objects provide these attributes:
|
||||
f_back next outer frame object (this frame's caller)
|
||||
f_builtins built-in namespace seen by this frame
|
||||
f_code code object being executed in this frame
|
||||
f_exc_traceback traceback if raised in this frame, or None
|
||||
f_exc_type exception type if raised in this frame, or None
|
||||
f_exc_value exception value if raised in this frame, or None
|
||||
f_globals global namespace seen by this frame
|
||||
f_lasti index of last attempted instruction in bytecode
|
||||
f_lineno current line number in Python source code
|
||||
f_locals local namespace seen by this frame
|
||||
f_restricted 0 or 1 if frame is in restricted execution mode
|
||||
f_trace tracing function for this frame, or None"""
|
||||
return isinstance(object, types.FrameType)
|
||||
|
||||
def iscode(object):
|
||||
"""Return true if the object is a code object.
|
||||
|
||||
Code objects provide these attributes:
|
||||
co_argcount number of arguments (not including * or ** args)
|
||||
co_code string of raw compiled bytecode
|
||||
co_consts tuple of constants used in the bytecode
|
||||
co_filename name of file in which this code object was created
|
||||
co_firstlineno number of first line in Python source code
|
||||
co_flags bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg
|
||||
co_lnotab encoded mapping of line numbers to bytecode indices
|
||||
co_name name with which this code object was defined
|
||||
co_names tuple of names of local variables
|
||||
co_nlocals number of local variables
|
||||
co_stacksize virtual machine stack space required
|
||||
co_varnames tuple of names of arguments and local variables"""
|
||||
return isinstance(object, types.CodeType)
|
||||
|
||||
def isbuiltin(object):
|
||||
"""Return true if the object is a built-in function or method.
|
||||
|
||||
Built-in functions and methods provide these attributes:
|
||||
__doc__ documentation string
|
||||
__name__ original name of this function or method
|
||||
__self__ instance to which a method is bound, or None"""
|
||||
return isinstance(object, types.BuiltinFunctionType)
|
||||
|
||||
def isroutine(object):
|
||||
"""Return true if the object is any kind of function or method."""
|
||||
return (isbuiltin(object)
|
||||
or isfunction(object)
|
||||
or ismethod(object)
|
||||
or ismethoddescriptor(object))
|
||||
|
||||
def getmembers(object, predicate=None):
|
||||
"""Return all members of an object as (name, value) pairs sorted by name.
|
||||
Optionally, only return members that satisfy a given predicate."""
|
||||
results = []
|
||||
for key in dir(object):
|
||||
value = getattr(object, key)
|
||||
if not predicate or predicate(value):
|
||||
results.append((key, value))
|
||||
results.sort()
|
||||
return results
|
||||
|
||||
def classify_class_attrs(cls):
|
||||
"""Return list of attribute-descriptor tuples.
|
||||
|
||||
For each name in dir(cls), the return list contains a 4-tuple
|
||||
with these elements:
|
||||
|
||||
0. The name (a string).
|
||||
|
||||
1. The kind of attribute this is, one of these strings:
|
||||
'class method' created via classmethod()
|
||||
'static method' created via staticmethod()
|
||||
'property' created via property()
|
||||
'method' any other flavor of method
|
||||
'data' not a method
|
||||
|
||||
2. The class which defined this attribute (a class).
|
||||
|
||||
3. The object as obtained directly from the defining class's
|
||||
__dict__, not via getattr. This is especially important for
|
||||
data attributes: C.data is just a data object, but
|
||||
C.__dict__['data'] may be a data descriptor with additional
|
||||
info, like a __doc__ string.
|
||||
"""
|
||||
|
||||
mro = getmro(cls)
|
||||
names = dir(cls)
|
||||
result = []
|
||||
for name in names:
|
||||
# Get the object associated with the name.
|
||||
# Getting an obj from the __dict__ sometimes reveals more than
|
||||
# using getattr. Static and class methods are dramatic examples.
|
||||
if name in cls.__dict__:
|
||||
obj = cls.__dict__[name]
|
||||
else:
|
||||
obj = getattr(cls, name)
|
||||
|
||||
# Figure out where it was defined.
|
||||
homecls = getattr(obj, "__objclass__", None)
|
||||
if homecls is None:
|
||||
# search the dicts.
|
||||
for base in mro:
|
||||
if name in base.__dict__:
|
||||
homecls = base
|
||||
break
|
||||
|
||||
# Get the object again, in order to get it from the defining
|
||||
# __dict__ instead of via getattr (if possible).
|
||||
if homecls is not None and name in homecls.__dict__:
|
||||
obj = homecls.__dict__[name]
|
||||
|
||||
# Also get the object via getattr.
|
||||
obj_via_getattr = getattr(cls, name)
|
||||
|
||||
# Classify the object.
|
||||
if isinstance(obj, staticmethod):
|
||||
kind = "static method"
|
||||
elif isinstance(obj, classmethod):
|
||||
kind = "class method"
|
||||
elif isinstance(obj, property):
|
||||
kind = "property"
|
||||
elif (ismethod(obj_via_getattr) or
|
||||
ismethoddescriptor(obj_via_getattr)):
|
||||
kind = "method"
|
||||
else:
|
||||
kind = "data"
|
||||
|
||||
result.append((name, kind, homecls, obj))
|
||||
|
||||
return result
|
||||
|
||||
# ----------------------------------------------------------- class helpers
|
||||
def _searchbases(cls, accum):
|
||||
# Simulate the "classic class" search order.
|
||||
if cls in accum:
|
||||
return
|
||||
accum.append(cls)
|
||||
for base in cls.__bases__:
|
||||
_searchbases(base, accum)
|
||||
|
||||
def getmro(cls):
|
||||
"Return tuple of base classes (including cls) in method resolution order."
|
||||
if hasattr(cls, "__mro__"):
|
||||
return cls.__mro__
|
||||
else:
|
||||
result = []
|
||||
_searchbases(cls, result)
|
||||
return tuple(result)
|
||||
|
||||
# -------------------------------------------------- source code extraction
|
||||
def indentsize(line):
|
||||
"""Return the indent size, in spaces, at the start of a line of text."""
|
||||
expline = string.expandtabs(line)
|
||||
return len(expline) - len(string.lstrip(expline))
|
||||
|
||||
def getdoc(object):
|
||||
"""Get the documentation string for an object.
|
||||
|
||||
All tabs are expanded to spaces. To clean up docstrings that are
|
||||
indented to line up with blocks of code, any whitespace than can be
|
||||
uniformly removed from the second line onwards is removed."""
|
||||
try:
|
||||
doc = object.__doc__
|
||||
except AttributeError:
|
||||
return None
|
||||
if not isinstance(doc, (str, unicode)):
|
||||
return None
|
||||
try:
|
||||
lines = string.split(string.expandtabs(doc), '\n')
|
||||
except UnicodeError:
|
||||
return None
|
||||
else:
|
||||
margin = None
|
||||
for line in lines[1:]:
|
||||
content = len(string.lstrip(line))
|
||||
if not content: continue
|
||||
indent = len(line) - content
|
||||
if margin is None: margin = indent
|
||||
else: margin = min(margin, indent)
|
||||
if margin is not None:
|
||||
for i in range(1, len(lines)): lines[i] = lines[i][margin:]
|
||||
return string.join(lines, '\n')
|
||||
|
||||
def getfile(object):
|
||||
"""Work out which source or compiled file an object was defined in."""
|
||||
if ismodule(object):
|
||||
if hasattr(object, '__file__'):
|
||||
return object.__file__
|
||||
raise TypeError, 'arg is a built-in module'
|
||||
if isclass(object):
|
||||
object = sys.modules.get(object.__module__)
|
||||
if hasattr(object, '__file__'):
|
||||
return object.__file__
|
||||
raise TypeError, 'arg is a built-in class'
|
||||
if ismethod(object):
|
||||
object = object.im_func
|
||||
if isfunction(object):
|
||||
object = object.func_code
|
||||
if istraceback(object):
|
||||
object = object.tb_frame
|
||||
if isframe(object):
|
||||
object = object.f_code
|
||||
if iscode(object):
|
||||
return object.co_filename
|
||||
raise TypeError, 'arg is not a module, class, method, ' \
|
||||
'function, traceback, frame, or code object'
|
||||
|
||||
def getmoduleinfo(path):
|
||||
"""Get the module name, suffix, mode, and module type for a given file."""
|
||||
filename = os.path.basename(path)
|
||||
suffixes = map(lambda (suffix, mode, mtype):
|
||||
(-len(suffix), suffix, mode, mtype), imp.get_suffixes())
|
||||
suffixes.sort() # try longest suffixes first, in case they overlap
|
||||
for neglen, suffix, mode, mtype in suffixes:
|
||||
if filename[neglen:] == suffix:
|
||||
return filename[:neglen], suffix, mode, mtype
|
||||
|
||||
def getmodulename(path):
|
||||
"""Return the module name for a given file, or None."""
|
||||
info = getmoduleinfo(path)
|
||||
if info: return info[0]
|
||||
|
||||
def getsourcefile(object):
|
||||
"""Return the Python source file an object was defined in, if it exists."""
|
||||
filename = getfile(object)
|
||||
if string.lower(filename[-4:]) in ['.pyc', '.pyo']:
|
||||
filename = filename[:-4] + '.py'
|
||||
for suffix, mode, kind in imp.get_suffixes():
|
||||
if 'b' in mode and string.lower(filename[-len(suffix):]) == suffix:
|
||||
# Looks like a binary file. We want to only return a text file.
|
||||
return None
|
||||
if os.path.exists(filename):
|
||||
return filename
|
||||
|
||||
def getabsfile(object):
|
||||
"""Return an absolute path to the source or compiled file for an object.
|
||||
|
||||
The idea is for each object to have a unique origin, so this routine
|
||||
normalizes the result as much as possible."""
|
||||
return os.path.normcase(
|
||||
os.path.abspath(getsourcefile(object) or getfile(object)))
|
||||
|
||||
modulesbyfile = {}
|
||||
|
||||
def getmodule(object):
|
||||
"""Return the module an object was defined in, or None if not found."""
|
||||
if ismodule(object):
|
||||
return object
|
||||
if isclass(object):
|
||||
return sys.modules.get(object.__module__)
|
||||
try:
|
||||
file = getabsfile(object)
|
||||
except TypeError:
|
||||
return None
|
||||
if modulesbyfile.has_key(file):
|
||||
return sys.modules[modulesbyfile[file]]
|
||||
for module in sys.modules.values():
|
||||
if hasattr(module, '__file__'):
|
||||
modulesbyfile[getabsfile(module)] = module.__name__
|
||||
if modulesbyfile.has_key(file):
|
||||
return sys.modules[modulesbyfile[file]]
|
||||
main = sys.modules['__main__']
|
||||
if hasattr(main, object.__name__):
|
||||
mainobject = getattr(main, object.__name__)
|
||||
if mainobject is object:
|
||||
return main
|
||||
builtin = sys.modules['__builtin__']
|
||||
if hasattr(builtin, object.__name__):
|
||||
builtinobject = getattr(builtin, object.__name__)
|
||||
if builtinobject is object:
|
||||
return builtin
|
||||
|
||||
def findsource(object):
|
||||
"""Return the entire source file and starting line number for an object.
|
||||
|
||||
The argument may be a module, class, method, function, traceback, frame,
|
||||
or code object. The source code is returned as a list of all the lines
|
||||
in the file and the line number indexes a line in that list. An IOError
|
||||
is raised if the source code cannot be retrieved."""
|
||||
try:
|
||||
file = open(getsourcefile(object))
|
||||
except (TypeError, IOError):
|
||||
raise IOError, 'could not get source code'
|
||||
lines = file.readlines()
|
||||
file.close()
|
||||
|
||||
if ismodule(object):
|
||||
return lines, 0
|
||||
|
||||
if isclass(object):
|
||||
name = object.__name__
|
||||
pat = re.compile(r'^\s*class\s*' + name + r'\b')
|
||||
for i in range(len(lines)):
|
||||
if pat.match(lines[i]): return lines, i
|
||||
else: raise IOError, 'could not find class definition'
|
||||
|
||||
if ismethod(object):
|
||||
object = object.im_func
|
||||
if isfunction(object):
|
||||
object = object.func_code
|
||||
if istraceback(object):
|
||||
object = object.tb_frame
|
||||
if isframe(object):
|
||||
object = object.f_code
|
||||
if iscode(object):
|
||||
if not hasattr(object, 'co_firstlineno'):
|
||||
raise IOError, 'could not find function definition'
|
||||
lnum = object.co_firstlineno - 1
|
||||
pat = re.compile(r'^(\s*def\s)|(.*\slambda(:|\s))')
|
||||
while lnum > 0:
|
||||
if pat.match(lines[lnum]): break
|
||||
lnum = lnum - 1
|
||||
return lines, lnum
|
||||
raise IOError, 'could not find code object'
|
||||
|
||||
def getcomments(object):
|
||||
"""Get lines of comments immediately preceding an object's source code."""
|
||||
try: lines, lnum = findsource(object)
|
||||
except IOError: return None
|
||||
|
||||
if ismodule(object):
|
||||
# Look for a comment block at the top of the file.
|
||||
start = 0
|
||||
if lines and lines[0][:2] == '#!': start = 1
|
||||
while start < len(lines) and string.strip(lines[start]) in ['', '#']:
|
||||
start = start + 1
|
||||
if start < len(lines) and lines[start][:1] == '#':
|
||||
comments = []
|
||||
end = start
|
||||
while end < len(lines) and lines[end][:1] == '#':
|
||||
comments.append(string.expandtabs(lines[end]))
|
||||
end = end + 1
|
||||
return string.join(comments, '')
|
||||
|
||||
# Look for a preceding block of comments at the same indentation.
|
||||
elif lnum > 0:
|
||||
indent = indentsize(lines[lnum])
|
||||
end = lnum - 1
|
||||
if end >= 0 and string.lstrip(lines[end])[:1] == '#' and \
|
||||
indentsize(lines[end]) == indent:
|
||||
comments = [string.lstrip(string.expandtabs(lines[end]))]
|
||||
if end > 0:
|
||||
end = end - 1
|
||||
comment = string.lstrip(string.expandtabs(lines[end]))
|
||||
while comment[:1] == '#' and indentsize(lines[end]) == indent:
|
||||
comments[:0] = [comment]
|
||||
end = end - 1
|
||||
if end < 0: break
|
||||
comment = string.lstrip(string.expandtabs(lines[end]))
|
||||
while comments and string.strip(comments[0]) == '#':
|
||||
comments[:1] = []
|
||||
while comments and string.strip(comments[-1]) == '#':
|
||||
comments[-1:] = []
|
||||
return string.join(comments, '')
|
||||
|
||||
class ListReader:
|
||||
"""Provide a readline() method to return lines from a list of strings."""
|
||||
def __init__(self, lines):
|
||||
self.lines = lines
|
||||
self.index = 0
|
||||
|
||||
def readline(self):
|
||||
i = self.index
|
||||
if i < len(self.lines):
|
||||
self.index = i + 1
|
||||
return self.lines[i]
|
||||
else: return ''
|
||||
|
||||
class EndOfBlock(Exception): pass
|
||||
|
||||
class BlockFinder:
|
||||
"""Provide a tokeneater() method to detect the end of a code block."""
|
||||
def __init__(self):
|
||||
self.indent = 0
|
||||
self.started = 0
|
||||
self.last = 0
|
||||
|
||||
def tokeneater(self, type, token, (srow, scol), (erow, ecol), line):
|
||||
if not self.started:
|
||||
if type == tokenize.NAME: self.started = 1
|
||||
elif type == tokenize.NEWLINE:
|
||||
self.last = srow
|
||||
elif type == tokenize.INDENT:
|
||||
self.indent = self.indent + 1
|
||||
elif type == tokenize.DEDENT:
|
||||
self.indent = self.indent - 1
|
||||
if self.indent == 0: raise EndOfBlock, self.last
|
||||
elif type == tokenize.NAME and scol == 0:
|
||||
raise EndOfBlock, self.last
|
||||
|
||||
def getblock(lines):
|
||||
"""Extract the block of code at the top of the given list of lines."""
|
||||
try:
|
||||
tokenize.tokenize(ListReader(lines).readline, BlockFinder().tokeneater)
|
||||
except EndOfBlock, eob:
|
||||
return lines[:eob.args[0]]
|
||||
# Fooling the indent/dedent logic implies a one-line definition
|
||||
return lines[:1]
|
||||
|
||||
def getsourcelines(object):
|
||||
"""Return a list of source lines and starting line number for an object.
|
||||
|
||||
The argument may be a module, class, method, function, traceback, frame,
|
||||
or code object. The source code is returned as a list of the lines
|
||||
corresponding to the object and the line number indicates where in the
|
||||
original source file the first line of code was found. An IOError is
|
||||
raised if the source code cannot be retrieved."""
|
||||
lines, lnum = findsource(object)
|
||||
|
||||
if ismodule(object): return lines, 0
|
||||
else: return getblock(lines[lnum:]), lnum + 1
|
||||
|
||||
def getsource(object):
|
||||
"""Return the text of the source code for an object.
|
||||
|
||||
The argument may be a module, class, method, function, traceback, frame,
|
||||
or code object. The source code is returned as a single string. An
|
||||
IOError is raised if the source code cannot be retrieved."""
|
||||
lines, lnum = getsourcelines(object)
|
||||
return string.join(lines, '')
|
||||
|
||||
# --------------------------------------------------- class tree extraction
|
||||
def walktree(classes, children, parent):
|
||||
"""Recursive helper function for getclasstree()."""
|
||||
results = []
|
||||
classes.sort(lambda a, b: cmp(a.__name__, b.__name__))
|
||||
for c in classes:
|
||||
results.append((c, c.__bases__))
|
||||
if children.has_key(c):
|
||||
results.append(walktree(children[c], children, c))
|
||||
return results
|
||||
|
||||
def getclasstree(classes, unique=0):
|
||||
"""Arrange the given list of classes into a hierarchy of nested lists.
|
||||
|
||||
Where a nested list appears, it contains classes derived from the class
|
||||
whose entry immediately precedes the list. Each entry is a 2-tuple
|
||||
containing a class and a tuple of its base classes. If the 'unique'
|
||||
argument is true, exactly one entry appears in the returned structure
|
||||
for each class in the given list. Otherwise, classes using multiple
|
||||
inheritance and their descendants will appear multiple times."""
|
||||
children = {}
|
||||
roots = []
|
||||
for c in classes:
|
||||
if c.__bases__:
|
||||
for parent in c.__bases__:
|
||||
if not children.has_key(parent):
|
||||
children[parent] = []
|
||||
children[parent].append(c)
|
||||
if unique and parent in classes: break
|
||||
elif c not in roots:
|
||||
roots.append(c)
|
||||
for parent in children.keys():
|
||||
if parent not in classes:
|
||||
roots.append(parent)
|
||||
return walktree(roots, children, None)
|
||||
|
||||
# ------------------------------------------------ argument list extraction
|
||||
# These constants are from Python's compile.h.
|
||||
CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 1, 2, 4, 8
|
||||
|
||||
def getargs(co):
|
||||
"""Get information about the arguments accepted by a code object.
|
||||
|
||||
Three things are returned: (args, varargs, varkw), where 'args' is
|
||||
a list of argument names (possibly containing nested lists), and
|
||||
'varargs' and 'varkw' are the names of the * and ** arguments or None."""
|
||||
if not iscode(co): raise TypeError, 'arg is not a code object'
|
||||
|
||||
nargs = co.co_argcount
|
||||
names = co.co_varnames
|
||||
args = list(names[:nargs])
|
||||
step = 0
|
||||
|
||||
# The following acrobatics are for anonymous (tuple) arguments.
|
||||
if not sys.platform.startswith('java'):#Jython doesn't have co_code
|
||||
code = co.co_code
|
||||
import dis
|
||||
for i in range(nargs):
|
||||
if args[i][:1] in ['', '.']:
|
||||
stack, remain, count = [], [], []
|
||||
while step < len(code):
|
||||
op = ord(code[step])
|
||||
step = step + 1
|
||||
if op >= dis.HAVE_ARGUMENT:
|
||||
opname = dis.opname[op]
|
||||
value = ord(code[step]) + ord(code[step + 1]) * 256
|
||||
step = step + 2
|
||||
if opname in ['UNPACK_TUPLE', 'UNPACK_SEQUENCE']:
|
||||
remain.append(value)
|
||||
count.append(value)
|
||||
elif opname == 'STORE_FAST':
|
||||
stack.append(names[value])
|
||||
remain[-1] = remain[-1] - 1
|
||||
while remain[-1] == 0:
|
||||
remain.pop()
|
||||
size = count.pop()
|
||||
stack[-size:] = [stack[-size:]]
|
||||
if not remain: break
|
||||
remain[-1] = remain[-1] - 1
|
||||
if not remain: break
|
||||
args[i] = stack[0]
|
||||
|
||||
varargs = None
|
||||
if co.co_flags & CO_VARARGS:
|
||||
varargs = co.co_varnames[nargs]
|
||||
nargs = nargs + 1
|
||||
varkw = None
|
||||
if co.co_flags & CO_VARKEYWORDS:
|
||||
varkw = co.co_varnames[nargs]
|
||||
return args, varargs, varkw
|
||||
|
||||
def getargspec(func):
|
||||
"""Get the names and default values of a function's arguments.
|
||||
|
||||
A tuple of four things is returned: (args, varargs, varkw, defaults).
|
||||
'args' is a list of the argument names (it may contain nested lists).
|
||||
'varargs' and 'varkw' are the names of the * and ** arguments or None.
|
||||
'defaults' is an n-tuple of the default values of the last n arguments."""
|
||||
if ismethod(func):
|
||||
func = func.im_func
|
||||
if not isfunction(func): raise TypeError, 'arg is not a Python function'
|
||||
args, varargs, varkw = getargs(func.func_code)
|
||||
return args, varargs, varkw, func.func_defaults
|
||||
|
||||
def getargvalues(frame):
|
||||
"""Get information about arguments passed into a particular frame.
|
||||
|
||||
A tuple of four things is returned: (args, varargs, varkw, locals).
|
||||
'args' is a list of the argument names (it may contain nested lists).
|
||||
'varargs' and 'varkw' are the names of the * and ** arguments or None.
|
||||
'locals' is the locals dictionary of the given frame."""
|
||||
args, varargs, varkw = getargs(frame.f_code)
|
||||
return args, varargs, varkw, frame.f_locals
|
||||
|
||||
def joinseq(seq):
|
||||
if len(seq) == 1:
|
||||
return '(' + seq[0] + ',)'
|
||||
else:
|
||||
return '(' + string.join(seq, ', ') + ')'
|
||||
|
||||
def strseq(object, convert, join=joinseq):
|
||||
"""Recursively walk a sequence, stringifying each element."""
|
||||
if type(object) in [types.ListType, types.TupleType]:
|
||||
return join(map(lambda o, c=convert, j=join: strseq(o, c, j), object))
|
||||
else:
|
||||
return convert(object)
|
||||
|
||||
def formatargspec(args, varargs=None, varkw=None, defaults=None,
|
||||
formatarg=str,
|
||||
formatvarargs=lambda name: '*' + name,
|
||||
formatvarkw=lambda name: '**' + name,
|
||||
formatvalue=lambda value: '=' + repr(value),
|
||||
join=joinseq):
|
||||
"""Format an argument spec from the 4 values returned by getargspec.
|
||||
|
||||
The first four arguments are (args, varargs, varkw, defaults). The
|
||||
other four arguments are the corresponding optional formatting functions
|
||||
that are called to turn names and values into strings. The ninth
|
||||
argument is an optional function to format the sequence of arguments."""
|
||||
specs = []
|
||||
if defaults:
|
||||
firstdefault = len(args) - len(defaults)
|
||||
for i in range(len(args)):
|
||||
spec = strseq(args[i], formatarg, join)
|
||||
if defaults and i >= firstdefault:
|
||||
spec = spec + formatvalue(defaults[i - firstdefault])
|
||||
specs.append(spec)
|
||||
if varargs:
|
||||
specs.append(formatvarargs(varargs))
|
||||
if varkw:
|
||||
specs.append(formatvarkw(varkw))
|
||||
return '(' + string.join(specs, ', ') + ')'
|
||||
|
||||
def formatargvalues(args, varargs, varkw, locals,
|
||||
formatarg=str,
|
||||
formatvarargs=lambda name: '*' + name,
|
||||
formatvarkw=lambda name: '**' + name,
|
||||
formatvalue=lambda value: '=' + repr(value),
|
||||
join=joinseq):
|
||||
"""Format an argument spec from the 4 values returned by getargvalues.
|
||||
|
||||
The first four arguments are (args, varargs, varkw, locals). The
|
||||
next four arguments are the corresponding optional formatting functions
|
||||
that are called to turn names and values into strings. The ninth
|
||||
argument is an optional function to format the sequence of arguments."""
|
||||
def convert(name, locals=locals,
|
||||
formatarg=formatarg, formatvalue=formatvalue):
|
||||
return formatarg(name) + formatvalue(locals[name])
|
||||
specs = []
|
||||
for i in range(len(args)):
|
||||
specs.append(strseq(args[i], convert, join))
|
||||
if varargs:
|
||||
specs.append(formatvarargs(varargs) + formatvalue(locals[varargs]))
|
||||
if varkw:
|
||||
specs.append(formatvarkw(varkw) + formatvalue(locals[varkw]))
|
||||
return '(' + string.join(specs, ', ') + ')'
|
||||
|
||||
# -------------------------------------------------- stack frame extraction
|
||||
def getframeinfo(frame, context=1):
|
||||
"""Get information about a frame or traceback object.
|
||||
|
||||
A tuple of five things is returned: the filename, the line number of
|
||||
the current line, the function name, a list of lines of context from
|
||||
the source code, and the index of the current line within that list.
|
||||
The optional second argument specifies the number of lines of context
|
||||
to return, which are centered around the current line."""
|
||||
raise NotImplementedError
|
||||
# if istraceback(frame):
|
||||
# frame = frame.tb_frame
|
||||
# if not isframe(frame):
|
||||
# raise TypeError, 'arg is not a frame or traceback object'
|
||||
#
|
||||
# filename = getsourcefile(frame)
|
||||
# lineno = getlineno(frame)
|
||||
# if context > 0:
|
||||
# start = lineno - 1 - context//2
|
||||
# try:
|
||||
# lines, lnum = findsource(frame)
|
||||
# except IOError:
|
||||
# lines = index = None
|
||||
# else:
|
||||
# start = max(start, 1)
|
||||
# start = min(start, len(lines) - context)
|
||||
# lines = lines[start:start+context]
|
||||
# index = lineno - 1 - start
|
||||
# else:
|
||||
# lines = index = None
|
||||
#
|
||||
# return (filename, lineno, frame.f_code.co_name, lines, index)
|
||||
|
||||
def getlineno(frame):
|
||||
"""Get the line number from a frame object, allowing for optimization."""
|
||||
# Written by Marc-Andr Lemburg; revised by Jim Hugunin and Fredrik Lundh.
|
||||
lineno = frame.f_lineno
|
||||
code = frame.f_code
|
||||
if hasattr(code, 'co_lnotab'):
|
||||
table = code.co_lnotab
|
||||
lineno = code.co_firstlineno
|
||||
addr = 0
|
||||
for i in range(0, len(table), 2):
|
||||
addr = addr + ord(table[i])
|
||||
if addr > frame.f_lasti: break
|
||||
lineno = lineno + ord(table[i + 1])
|
||||
return lineno
|
||||
|
||||
def getouterframes(frame, context=1):
|
||||
"""Get a list of records for a frame and all higher (calling) frames.
|
||||
|
||||
Each record contains a frame object, filename, line number, function
|
||||
name, a list of lines of context, and index within the context."""
|
||||
framelist = []
|
||||
while frame:
|
||||
framelist.append((frame,) + getframeinfo(frame, context))
|
||||
frame = frame.f_back
|
||||
return framelist
|
||||
|
||||
def getinnerframes(tb, context=1):
|
||||
"""Get a list of records for a traceback's frame and all lower frames.
|
||||
|
||||
Each record contains a frame object, filename, line number, function
|
||||
name, a list of lines of context, and index within the context."""
|
||||
framelist = []
|
||||
while tb:
|
||||
framelist.append((tb.tb_frame,) + getframeinfo(tb, context))
|
||||
tb = tb.tb_next
|
||||
return framelist
|
||||
|
||||
def currentframe():
|
||||
"""Return the frame object for the caller's stack frame."""
|
||||
try:
|
||||
raise 'catch me'
|
||||
except:
|
||||
return sys.exc_traceback.tb_frame.f_back #@UndefinedVariable
|
||||
|
||||
if hasattr(sys, '_getframe'): currentframe = sys._getframe
|
||||
|
||||
def stack(context=1):
|
||||
"""Return a list of records for the stack above the caller's frame."""
|
||||
return getouterframes(currentframe().f_back, context)
|
||||
|
||||
def trace(context=1):
|
||||
"""Return a list of records for the stack below the current exception."""
|
||||
return getinnerframes(sys.exc_traceback, context) #@UndefinedVariable
|
||||
@@ -1 +0,0 @@
|
||||
from select import *
|
||||
@@ -1 +0,0 @@
|
||||
from socket import *
|
||||
@@ -1,978 +1,13 @@
|
||||
"""Thread module emulating a subset of Java's threading model."""
|
||||
try:
|
||||
from threading import settrace
|
||||
except:
|
||||
pass
|
||||
|
||||
import sys as _sys
|
||||
|
||||
from _pydev_imps import _pydev_thread as thread
|
||||
import warnings
|
||||
|
||||
from _pydev_imps._pydev_time import time as _time, sleep as _sleep
|
||||
from traceback import format_exc as _format_exc
|
||||
|
||||
# Note regarding PEP 8 compliant aliases
|
||||
# This threading model was originally inspired by Java, and inherited
|
||||
# the convention of camelCase function and method names from that
|
||||
# language. While those names are not in any imminent danger of being
|
||||
# deprecated, starting with Python 2.6, the module now provides a
|
||||
# PEP 8 compliant alias for any such method name.
|
||||
# Using the new PEP 8 compliant names also facilitates substitution
|
||||
# with the multiprocessing module, which doesn't provide the old
|
||||
# Java inspired names.
|
||||
|
||||
|
||||
# Rename some stuff so "from threading import *" is safe
|
||||
__all__ = ['activeCount', 'active_count', 'Condition', 'currentThread',
|
||||
'current_thread', 'enumerate', 'Event',
|
||||
'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread',
|
||||
'Timer', 'setprofile', 'settrace', 'local', 'stack_size']
|
||||
|
||||
_start_new_thread = thread.start_new_thread
|
||||
_allocate_lock = thread.allocate_lock
|
||||
_get_ident = thread.get_ident
|
||||
ThreadError = thread.error
|
||||
del thread
|
||||
|
||||
|
||||
# sys.exc_clear is used to work around the fact that except blocks
|
||||
# don't fully clear the exception until 3.0.
|
||||
warnings.filterwarnings('ignore', category=DeprecationWarning,
|
||||
module='threading', message='sys.exc_clear')
|
||||
|
||||
# Debug support (adapted from ihooks.py).
|
||||
# All the major classes here derive from _Verbose. We force that to
|
||||
# be a new-style class so that all the major classes here are new-style.
|
||||
# This helps debugging (type(instance) is more revealing for instances
|
||||
# of new-style classes).
|
||||
|
||||
_VERBOSE = False
|
||||
|
||||
if __debug__:
|
||||
|
||||
class _Verbose(object):
|
||||
|
||||
def __init__(self, verbose=None):
|
||||
if verbose is None:
|
||||
verbose = _VERBOSE
|
||||
self.__verbose = verbose
|
||||
|
||||
def _note(self, format, *args):
|
||||
if self.__verbose:
|
||||
format = format % args
|
||||
# Issue #4188: calling current_thread() can incur an infinite
|
||||
# recursion if it has to create a DummyThread on the fly.
|
||||
ident = _get_ident()
|
||||
try:
|
||||
name = _active[ident].name
|
||||
except KeyError:
|
||||
name = "<OS thread %d>" % ident
|
||||
format = "%s: %s\n" % (name, format)
|
||||
_sys.stderr.write(format)
|
||||
|
||||
else:
|
||||
# Disable this when using "python -O"
|
||||
class _Verbose(object):
|
||||
def __init__(self, verbose=None):
|
||||
pass
|
||||
def _note(self, *args):
|
||||
pass
|
||||
|
||||
# Support for profile and trace hooks
|
||||
|
||||
_profile_hook = None
|
||||
_trace_hook = None
|
||||
|
||||
def setprofile(func):
|
||||
global _profile_hook
|
||||
_profile_hook = func
|
||||
|
||||
def settrace(func):
|
||||
global _trace_hook
|
||||
_trace_hook = func
|
||||
|
||||
# Synchronization classes
|
||||
|
||||
Lock = _allocate_lock
|
||||
|
||||
def RLock(*args, **kwargs):
|
||||
return _RLock(*args, **kwargs)
|
||||
|
||||
class _RLock(_Verbose):
|
||||
|
||||
def __init__(self, verbose=None):
|
||||
_Verbose.__init__(self, verbose)
|
||||
self.__block = _allocate_lock()
|
||||
self.__owner = None
|
||||
self.__count = 0
|
||||
|
||||
def __repr__(self):
|
||||
owner = self.__owner
|
||||
try:
|
||||
owner = _active[owner].name
|
||||
except KeyError:
|
||||
pass
|
||||
return "<%s owner=%r count=%d>" % (
|
||||
self.__class__.__name__, owner, self.__count)
|
||||
|
||||
def acquire(self, blocking=1):
|
||||
me = _get_ident()
|
||||
if self.__owner == me:
|
||||
self.__count = self.__count + 1
|
||||
if __debug__:
|
||||
self._note("%s.acquire(%s): recursive success", self, blocking)
|
||||
return 1
|
||||
rc = self.__block.acquire(blocking)
|
||||
if rc:
|
||||
self.__owner = me
|
||||
self.__count = 1
|
||||
if __debug__:
|
||||
self._note("%s.acquire(%s): initial success", self, blocking)
|
||||
else:
|
||||
if __debug__:
|
||||
self._note("%s.acquire(%s): failure", self, blocking)
|
||||
return rc
|
||||
|
||||
__enter__ = acquire
|
||||
|
||||
def release(self):
|
||||
if self.__owner != _get_ident():
|
||||
raise RuntimeError("cannot release un-acquired lock")
|
||||
self.__count = count = self.__count - 1
|
||||
if not count:
|
||||
self.__owner = None
|
||||
self.__block.release()
|
||||
if __debug__:
|
||||
self._note("%s.release(): final release", self)
|
||||
else:
|
||||
if __debug__:
|
||||
self._note("%s.release(): non-final release", self)
|
||||
|
||||
def __exit__(self, t, v, tb):
|
||||
self.release()
|
||||
|
||||
# Internal methods used by condition variables
|
||||
|
||||
def _acquire_restore(self, count_owner):
|
||||
count, owner = count_owner
|
||||
self.__block.acquire()
|
||||
self.__count = count
|
||||
self.__owner = owner
|
||||
if __debug__:
|
||||
self._note("%s._acquire_restore()", self)
|
||||
|
||||
def _release_save(self):
|
||||
if __debug__:
|
||||
self._note("%s._release_save()", self)
|
||||
count = self.__count
|
||||
self.__count = 0
|
||||
owner = self.__owner
|
||||
self.__owner = None
|
||||
self.__block.release()
|
||||
return (count, owner)
|
||||
|
||||
def _is_owned(self):
|
||||
return self.__owner == _get_ident()
|
||||
|
||||
|
||||
def Condition(*args, **kwargs):
|
||||
return _Condition(*args, **kwargs)
|
||||
|
||||
class _Condition(_Verbose):
|
||||
|
||||
def __init__(self, lock=None, verbose=None):
|
||||
_Verbose.__init__(self, verbose)
|
||||
if lock is None:
|
||||
lock = RLock()
|
||||
self.__lock = lock
|
||||
# Export the lock's acquire() and release() methods
|
||||
self.acquire = lock.acquire
|
||||
self.release = lock.release
|
||||
# If the lock defines _release_save() and/or _acquire_restore(),
|
||||
# these override the default implementations (which just call
|
||||
# release() and acquire() on the lock). Ditto for _is_owned().
|
||||
try:
|
||||
self._release_save = lock._release_save
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
self._acquire_restore = lock._acquire_restore
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
self._is_owned = lock._is_owned
|
||||
except AttributeError:
|
||||
pass
|
||||
self.__waiters = []
|
||||
|
||||
def __enter__(self):
|
||||
return self.__lock.__enter__()
|
||||
|
||||
def __exit__(self, *args):
|
||||
return self.__lock.__exit__(*args)
|
||||
|
||||
def __repr__(self):
|
||||
return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
|
||||
|
||||
def _release_save(self):
|
||||
self.__lock.release() # No state to save
|
||||
|
||||
def _acquire_restore(self, x):
|
||||
self.__lock.acquire() # Ignore saved state
|
||||
|
||||
def _is_owned(self):
|
||||
# Return True if lock is owned by current_thread.
|
||||
# This method is called only if __lock doesn't have _is_owned().
|
||||
if self.__lock.acquire(0):
|
||||
self.__lock.release()
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def wait(self, timeout=None):
|
||||
if not self._is_owned():
|
||||
raise RuntimeError("cannot wait on un-acquired lock")
|
||||
waiter = _allocate_lock()
|
||||
waiter.acquire()
|
||||
self.__waiters.append(waiter)
|
||||
saved_state = self._release_save()
|
||||
try: # restore state no matter what (e.g., KeyboardInterrupt)
|
||||
if timeout is None:
|
||||
waiter.acquire()
|
||||
if __debug__:
|
||||
self._note("%s.wait(): got it", self)
|
||||
else:
|
||||
# Balancing act: We can't afford a pure busy loop, so we
|
||||
# have to sleep; but if we sleep the whole timeout time,
|
||||
# we'll be unresponsive. The scheme here sleeps very
|
||||
# little at first, longer as time goes on, but never longer
|
||||
# than 20 times per second (or the timeout time remaining).
|
||||
endtime = _time() + timeout
|
||||
delay = 0.0005 # 500 us -> initial delay of 1 ms
|
||||
while True:
|
||||
gotit = waiter.acquire(0)
|
||||
if gotit:
|
||||
break
|
||||
remaining = endtime - _time()
|
||||
if remaining <= 0:
|
||||
break
|
||||
delay = min(delay * 2, remaining, .05)
|
||||
_sleep(delay)
|
||||
if not gotit:
|
||||
if __debug__:
|
||||
self._note("%s.wait(%s): timed out", self, timeout)
|
||||
try:
|
||||
self.__waiters.remove(waiter)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
if __debug__:
|
||||
self._note("%s.wait(%s): got it", self, timeout)
|
||||
finally:
|
||||
self._acquire_restore(saved_state)
|
||||
|
||||
def notify(self, n=1):
|
||||
if not self._is_owned():
|
||||
raise RuntimeError("cannot notify on un-acquired lock")
|
||||
__waiters = self.__waiters
|
||||
waiters = __waiters[:n]
|
||||
if not waiters:
|
||||
if __debug__:
|
||||
self._note("%s.notify(): no waiters", self)
|
||||
return
|
||||
self._note("%s.notify(): notifying %d waiter%s", self, n,
|
||||
n!=1 and "s" or "")
|
||||
for waiter in waiters:
|
||||
waiter.release()
|
||||
try:
|
||||
__waiters.remove(waiter)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def notifyAll(self):
|
||||
self.notify(len(self.__waiters))
|
||||
|
||||
notify_all = notifyAll
|
||||
|
||||
|
||||
def Semaphore(*args, **kwargs):
|
||||
return _Semaphore(*args, **kwargs)
|
||||
|
||||
class _Semaphore(_Verbose):
|
||||
|
||||
# After Tim Peters' semaphore class, but not quite the same (no maximum)
|
||||
|
||||
def __init__(self, value=1, verbose=None):
|
||||
if value < 0:
|
||||
raise ValueError("semaphore initial value must be >= 0")
|
||||
_Verbose.__init__(self, verbose)
|
||||
self.__cond = Condition(Lock())
|
||||
self.__value = value
|
||||
|
||||
def acquire(self, blocking=1):
|
||||
rc = False
|
||||
self.__cond.acquire()
|
||||
while self.__value == 0:
|
||||
if not blocking:
|
||||
break
|
||||
if __debug__:
|
||||
self._note("%s.acquire(%s): blocked waiting, value=%s",
|
||||
self, blocking, self.__value)
|
||||
self.__cond.wait()
|
||||
else:
|
||||
self.__value = self.__value - 1
|
||||
if __debug__:
|
||||
self._note("%s.acquire: success, value=%s",
|
||||
self, self.__value)
|
||||
rc = True
|
||||
self.__cond.release()
|
||||
return rc
|
||||
|
||||
__enter__ = acquire
|
||||
|
||||
def release(self):
|
||||
self.__cond.acquire()
|
||||
self.__value = self.__value + 1
|
||||
if __debug__:
|
||||
self._note("%s.release: success, value=%s",
|
||||
self, self.__value)
|
||||
self.__cond.notify()
|
||||
self.__cond.release()
|
||||
|
||||
def __exit__(self, t, v, tb):
|
||||
self.release()
|
||||
|
||||
|
||||
def BoundedSemaphore(*args, **kwargs):
|
||||
return _BoundedSemaphore(*args, **kwargs)
|
||||
|
||||
class _BoundedSemaphore(_Semaphore):
|
||||
"""Semaphore that checks that # releases is <= # acquires"""
|
||||
def __init__(self, value=1, verbose=None):
|
||||
_Semaphore.__init__(self, value, verbose)
|
||||
self._initial_value = value
|
||||
|
||||
def release(self):
|
||||
if self._Semaphore__value >= self._initial_value:
|
||||
raise ValueError, "Semaphore released too many times"
|
||||
return _Semaphore.release(self)
|
||||
|
||||
|
||||
def Event(*args, **kwargs):
|
||||
return _Event(*args, **kwargs)
|
||||
|
||||
class _Event(_Verbose):
|
||||
|
||||
# After Tim Peters' event class (without is_posted())
|
||||
|
||||
def __init__(self, verbose=None):
|
||||
_Verbose.__init__(self, verbose)
|
||||
self.__cond = Condition(Lock())
|
||||
self.__flag = False
|
||||
|
||||
def _reset_internal_locks(self):
|
||||
# private! called by Thread._reset_internal_locks by _after_fork()
|
||||
self.__cond.__init__()
|
||||
|
||||
def isSet(self):
|
||||
return self.__flag
|
||||
|
||||
is_set = isSet
|
||||
|
||||
def set(self):
|
||||
self.__cond.acquire()
|
||||
try:
|
||||
self.__flag = True
|
||||
self.__cond.notify_all()
|
||||
finally:
|
||||
self.__cond.release()
|
||||
|
||||
def clear(self):
|
||||
self.__cond.acquire()
|
||||
try:
|
||||
self.__flag = False
|
||||
finally:
|
||||
self.__cond.release()
|
||||
|
||||
def wait(self, timeout=None):
|
||||
self.__cond.acquire()
|
||||
try:
|
||||
if not self.__flag:
|
||||
self.__cond.wait(timeout)
|
||||
return self.__flag
|
||||
finally:
|
||||
self.__cond.release()
|
||||
|
||||
# Helper to generate new thread names
|
||||
_counter = 0
|
||||
def _newname(template="Thread-%d"):
|
||||
global _counter
|
||||
_counter = _counter + 1
|
||||
return template % _counter
|
||||
|
||||
# Active thread administration
|
||||
_active_limbo_lock = _allocate_lock()
|
||||
_active = {} # maps thread id to Thread object
|
||||
_limbo = {}
|
||||
|
||||
|
||||
# Main class for threads
|
||||
|
||||
class Thread(_Verbose):
|
||||
|
||||
__initialized = False
|
||||
# Need to store a reference to sys.exc_info for printing
|
||||
# out exceptions when a thread tries to use a global var. during interp.
|
||||
# shutdown and thus raises an exception about trying to perform some
|
||||
# operation on/with a NoneType
|
||||
__exc_info = _sys.exc_info
|
||||
# Keep sys.exc_clear too to clear the exception just before
|
||||
# allowing .join() to return.
|
||||
__exc_clear = _sys.exc_clear
|
||||
|
||||
def __init__(self, group=None, target=None, name=None,
|
||||
args=(), kwargs=None, verbose=None):
|
||||
assert group is None, "group argument must be None for now"
|
||||
_Verbose.__init__(self, verbose)
|
||||
if kwargs is None:
|
||||
kwargs = {}
|
||||
self.__target = target
|
||||
self.__name = str(name or _newname())
|
||||
self.__args = args
|
||||
self.__kwargs = kwargs
|
||||
self.__daemonic = self._set_daemon()
|
||||
self.__ident = None
|
||||
self.__started = Event()
|
||||
self.__stopped = False
|
||||
self.__block = Condition(Lock())
|
||||
self.__initialized = True
|
||||
# sys.stderr is not stored in the class like
|
||||
# sys.exc_info since it can be changed between instances
|
||||
self.__stderr = _sys.stderr
|
||||
|
||||
def _reset_internal_locks(self):
|
||||
# private! Called by _after_fork() to reset our internal locks as
|
||||
# they may be in an invalid state leading to a deadlock or crash.
|
||||
if hasattr(self, '_Thread__block'): # DummyThread deletes self.__block
|
||||
self.__block.__init__()
|
||||
self.__started._reset_internal_locks()
|
||||
|
||||
@property
|
||||
def _block(self):
|
||||
# used by a unittest
|
||||
return self.__block
|
||||
|
||||
def _set_daemon(self):
|
||||
# Overridden in _MainThread and _DummyThread
|
||||
return current_thread().daemon
|
||||
|
||||
def __repr__(self):
|
||||
assert self.__initialized, "Thread.__init__() was not called"
|
||||
status = "initial"
|
||||
if self.__started.is_set():
|
||||
status = "started"
|
||||
if self.__stopped:
|
||||
status = "stopped"
|
||||
if self.__daemonic:
|
||||
status += " daemon"
|
||||
if self.__ident is not None:
|
||||
status += " %s" % self.__ident
|
||||
return "<%s(%s, %s)>" % (self.__class__.__name__, self.__name, status)
|
||||
|
||||
def start(self):
|
||||
if not self.__initialized:
|
||||
raise RuntimeError("thread.__init__() not called")
|
||||
if self.__started.is_set():
|
||||
raise RuntimeError("threads can only be started once")
|
||||
if __debug__:
|
||||
self._note("%s.start(): starting thread", self)
|
||||
with _active_limbo_lock:
|
||||
_limbo[self] = self
|
||||
try:
|
||||
_start_new_thread(self.__bootstrap, ())
|
||||
except Exception:
|
||||
with _active_limbo_lock:
|
||||
del _limbo[self]
|
||||
raise
|
||||
self.__started.wait()
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
if self.__target:
|
||||
self.__target(*self.__args, **self.__kwargs)
|
||||
finally:
|
||||
# Avoid a refcycle if the thread is running a function with
|
||||
# an argument that has a member that points to the thread.
|
||||
del self.__target, self.__args, self.__kwargs
|
||||
|
||||
def __bootstrap(self):
|
||||
# Wrapper around the real bootstrap code that ignores
|
||||
# exceptions during interpreter cleanup. Those typically
|
||||
# happen when a daemon thread wakes up at an unfortunate
|
||||
# moment, finds the world around it destroyed, and raises some
|
||||
# random exception *** while trying to report the exception in
|
||||
# __bootstrap_inner() below ***. Those random exceptions
|
||||
# don't help anybody, and they confuse users, so we suppress
|
||||
# them. We suppress them only when it appears that the world
|
||||
# indeed has already been destroyed, so that exceptions in
|
||||
# __bootstrap_inner() during normal business hours are properly
|
||||
# reported. Also, we only suppress them for daemonic threads;
|
||||
# if a non-daemonic encounters this, something else is wrong.
|
||||
try:
|
||||
self.__bootstrap_inner()
|
||||
except:
|
||||
if self.__daemonic and _sys is None:
|
||||
return
|
||||
raise
|
||||
|
||||
def _set_ident(self):
|
||||
self.__ident = _get_ident()
|
||||
|
||||
def __bootstrap_inner(self):
|
||||
try:
|
||||
self._set_ident()
|
||||
self.__started.set()
|
||||
with _active_limbo_lock:
|
||||
_active[self.__ident] = self
|
||||
del _limbo[self]
|
||||
if __debug__:
|
||||
self._note("%s.__bootstrap(): thread started", self)
|
||||
|
||||
if _trace_hook:
|
||||
self._note("%s.__bootstrap(): registering trace hook", self)
|
||||
_sys.settrace(_trace_hook)
|
||||
if _profile_hook:
|
||||
self._note("%s.__bootstrap(): registering profile hook", self)
|
||||
_sys.setprofile(_profile_hook)
|
||||
|
||||
try:
|
||||
self.run()
|
||||
except SystemExit:
|
||||
if __debug__:
|
||||
self._note("%s.__bootstrap(): raised SystemExit", self)
|
||||
except:
|
||||
if __debug__:
|
||||
self._note("%s.__bootstrap(): unhandled exception", self)
|
||||
# If sys.stderr is no more (most likely from interpreter
|
||||
# shutdown) use self.__stderr. Otherwise still use sys (as in
|
||||
# _sys) in case sys.stderr was redefined since the creation of
|
||||
# self.
|
||||
if _sys:
|
||||
_sys.stderr.write("Exception in thread %s:\n%s\n" %
|
||||
(self.name, _format_exc()))
|
||||
else:
|
||||
# Do the best job possible w/o a huge amt. of code to
|
||||
# approximate a traceback (code ideas from
|
||||
# Lib/traceback.py)
|
||||
exc_type, exc_value, exc_tb = self.__exc_info()
|
||||
try:
|
||||
print>>self.__stderr, (
|
||||
"Exception in thread " + self.name +
|
||||
" (most likely raised during interpreter shutdown):")
|
||||
print>>self.__stderr, (
|
||||
"Traceback (most recent call last):")
|
||||
while exc_tb:
|
||||
print>>self.__stderr, (
|
||||
' File "%s", line %s, in %s' %
|
||||
(exc_tb.tb_frame.f_code.co_filename,
|
||||
exc_tb.tb_lineno,
|
||||
exc_tb.tb_frame.f_code.co_name))
|
||||
exc_tb = exc_tb.tb_next
|
||||
print>>self.__stderr, ("%s: %s" % (exc_type, exc_value))
|
||||
# Make sure that exc_tb gets deleted since it is a memory
|
||||
# hog; deleting everything else is just for thoroughness
|
||||
finally:
|
||||
del exc_type, exc_value, exc_tb
|
||||
else:
|
||||
if __debug__:
|
||||
self._note("%s.__bootstrap(): normal return", self)
|
||||
finally:
|
||||
# Prevent a race in
|
||||
# test_threading.test_no_refcycle_through_target when
|
||||
# the exception keeps the target alive past when we
|
||||
# assert that it's dead.
|
||||
self.__exc_clear()
|
||||
finally:
|
||||
with _active_limbo_lock:
|
||||
self.__stop()
|
||||
try:
|
||||
# We don't call self.__delete() because it also
|
||||
# grabs _active_limbo_lock.
|
||||
del _active[_get_ident()]
|
||||
except:
|
||||
pass
|
||||
|
||||
def __stop(self):
|
||||
self.__block.acquire()
|
||||
self.__stopped = True
|
||||
self.__block.notify_all()
|
||||
self.__block.release()
|
||||
|
||||
def __delete(self):
|
||||
"Remove current thread from the dict of currently running threads."
|
||||
|
||||
# Notes about running with dummy_thread:
|
||||
#
|
||||
# Must take care to not raise an exception if dummy_thread is being
|
||||
# used (and thus this module is being used as an instance of
|
||||
# dummy_threading). dummy_thread.get_ident() always returns -1 since
|
||||
# there is only one thread if dummy_thread is being used. Thus
|
||||
# len(_active) is always <= 1 here, and any Thread instance created
|
||||
# overwrites the (if any) thread currently registered in _active.
|
||||
#
|
||||
# An instance of _MainThread is always created by 'threading'. This
|
||||
# gets overwritten the instant an instance of Thread is created; both
|
||||
# threads return -1 from dummy_thread.get_ident() and thus have the
|
||||
# same key in the dict. So when the _MainThread instance created by
|
||||
# 'threading' tries to clean itself up when atexit calls this method
|
||||
# it gets a KeyError if another Thread instance was created.
|
||||
#
|
||||
# This all means that KeyError from trying to delete something from
|
||||
# _active if dummy_threading is being used is a red herring. But
|
||||
# since it isn't if dummy_threading is *not* being used then don't
|
||||
# hide the exception.
|
||||
|
||||
try:
|
||||
with _active_limbo_lock:
|
||||
del _active[_get_ident()]
|
||||
# There must not be any python code between the previous line
|
||||
# and after the lock is released. Otherwise a tracing function
|
||||
# could try to acquire the lock again in the same thread, (in
|
||||
# current_thread()), and would block.
|
||||
except KeyError:
|
||||
if 'dummy_threading' not in _sys.modules:
|
||||
raise
|
||||
|
||||
def join(self, timeout=None):
|
||||
if not self.__initialized:
|
||||
raise RuntimeError("Thread.__init__() not called")
|
||||
if not self.__started.is_set():
|
||||
raise RuntimeError("cannot join thread before it is started")
|
||||
if self is current_thread():
|
||||
raise RuntimeError("cannot join current thread")
|
||||
|
||||
if __debug__:
|
||||
if not self.__stopped:
|
||||
self._note("%s.join(): waiting until thread stops", self)
|
||||
self.__block.acquire()
|
||||
try:
|
||||
if timeout is None:
|
||||
while not self.__stopped:
|
||||
self.__block.wait()
|
||||
if __debug__:
|
||||
self._note("%s.join(): thread stopped", self)
|
||||
else:
|
||||
deadline = _time() + timeout
|
||||
while not self.__stopped:
|
||||
delay = deadline - _time()
|
||||
if delay <= 0:
|
||||
if __debug__:
|
||||
self._note("%s.join(): timed out", self)
|
||||
break
|
||||
self.__block.wait(delay)
|
||||
else:
|
||||
if __debug__:
|
||||
self._note("%s.join(): thread stopped", self)
|
||||
finally:
|
||||
self.__block.release()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
assert self.__initialized, "Thread.__init__() not called"
|
||||
return self.__name
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
assert self.__initialized, "Thread.__init__() not called"
|
||||
self.__name = str(name)
|
||||
|
||||
@property
|
||||
def ident(self):
|
||||
assert self.__initialized, "Thread.__init__() not called"
|
||||
return self.__ident
|
||||
|
||||
def isAlive(self):
|
||||
assert self.__initialized, "Thread.__init__() not called"
|
||||
return self.__started.is_set() and not self.__stopped
|
||||
|
||||
is_alive = isAlive
|
||||
|
||||
@property
|
||||
def daemon(self):
|
||||
assert self.__initialized, "Thread.__init__() not called"
|
||||
return self.__daemonic
|
||||
|
||||
@daemon.setter
|
||||
def daemon(self, daemonic):
|
||||
if not self.__initialized:
|
||||
raise RuntimeError("Thread.__init__() not called")
|
||||
if self.__started.is_set():
|
||||
raise RuntimeError("cannot set daemon status of active thread");
|
||||
self.__daemonic = daemonic
|
||||
|
||||
def isDaemon(self):
|
||||
return self.daemon
|
||||
|
||||
def setDaemon(self, daemonic):
|
||||
self.daemon = daemonic
|
||||
|
||||
def getName(self):
|
||||
return self.name
|
||||
|
||||
def setName(self, name):
|
||||
self.name = name
|
||||
|
||||
# The timer class was contributed by Itamar Shtull-Trauring
|
||||
|
||||
def Timer(*args, **kwargs):
|
||||
return _Timer(*args, **kwargs)
|
||||
|
||||
class _Timer(Thread):
|
||||
"""Call a function after a specified number of seconds:
|
||||
|
||||
t = Timer(30.0, f, args=[], kwargs={})
|
||||
t.start()
|
||||
t.cancel() # stop the timer's action if it's still waiting
|
||||
"""
|
||||
|
||||
def __init__(self, interval, function, args=[], kwargs={}):
|
||||
Thread.__init__(self)
|
||||
self.interval = interval
|
||||
self.function = function
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.finished = Event()
|
||||
|
||||
def cancel(self):
|
||||
"""Stop the timer if it hasn't finished yet"""
|
||||
self.finished.set()
|
||||
|
||||
def run(self):
|
||||
self.finished.wait(self.interval)
|
||||
if not self.finished.is_set():
|
||||
self.function(*self.args, **self.kwargs)
|
||||
self.finished.set()
|
||||
|
||||
# Special thread class to represent the main thread
|
||||
# This is garbage collected through an exit handler
|
||||
|
||||
class _MainThread(Thread):
|
||||
|
||||
def __init__(self):
|
||||
Thread.__init__(self, name="MainThread")
|
||||
self._Thread__started.set()
|
||||
self._set_ident()
|
||||
with _active_limbo_lock:
|
||||
_active[_get_ident()] = self
|
||||
|
||||
def _set_daemon(self):
|
||||
return False
|
||||
|
||||
def _exitfunc(self):
|
||||
self._Thread__stop()
|
||||
t = _pickSomeNonDaemonThread()
|
||||
if t:
|
||||
if __debug__:
|
||||
self._note("%s: waiting for other threads", self)
|
||||
while t:
|
||||
t.join()
|
||||
t = _pickSomeNonDaemonThread()
|
||||
if __debug__:
|
||||
self._note("%s: exiting", self)
|
||||
self._Thread__delete()
|
||||
|
||||
def _pickSomeNonDaemonThread():
|
||||
for t in enumerate():
|
||||
if not t.daemon and t.is_alive():
|
||||
return t
|
||||
return None
|
||||
|
||||
|
||||
# Dummy thread class to represent threads not started here.
|
||||
# These aren't garbage collected when they die, nor can they be waited for.
|
||||
# If they invoke anything in threading.py that calls current_thread(), they
|
||||
# leave an entry in the _active dict forever after.
|
||||
# Their purpose is to return *something* from current_thread().
|
||||
# They are marked as daemon threads so we won't wait for them
|
||||
# when we exit (conform previous semantics).
|
||||
|
||||
class _DummyThread(Thread):
|
||||
|
||||
def __init__(self):
|
||||
Thread.__init__(self, name=_newname("Dummy-%d"))
|
||||
|
||||
# Thread.__block consumes an OS-level locking primitive, which
|
||||
# can never be used by a _DummyThread. Since a _DummyThread
|
||||
# instance is immortal, that's bad, so release this resource.
|
||||
del self._Thread__block
|
||||
|
||||
self._Thread__started.set()
|
||||
self._set_ident()
|
||||
with _active_limbo_lock:
|
||||
_active[_get_ident()] = self
|
||||
|
||||
def _set_daemon(self):
|
||||
return True
|
||||
|
||||
def join(self, timeout=None):
|
||||
assert False, "cannot join a dummy thread"
|
||||
|
||||
|
||||
# Global API functions
|
||||
|
||||
def currentThread():
|
||||
try:
|
||||
return _active[_get_ident()]
|
||||
except KeyError:
|
||||
##print "current_thread(): no current thread for", _get_ident()
|
||||
return _DummyThread()
|
||||
|
||||
current_thread = currentThread
|
||||
|
||||
def activeCount():
|
||||
with _active_limbo_lock:
|
||||
return len(_active) + len(_limbo)
|
||||
|
||||
active_count = activeCount
|
||||
|
||||
def _enumerate():
|
||||
# Same as enumerate(), but without the lock. Internal use only.
|
||||
return _active.values() + _limbo.values()
|
||||
|
||||
def enumerate():
|
||||
with _active_limbo_lock:
|
||||
return _active.values() + _limbo.values()
|
||||
|
||||
# Create the main thread object,
|
||||
# and make it available for the interpreter
|
||||
# (Py_Main) as threading._shutdown.
|
||||
|
||||
_shutdown = _MainThread()._exitfunc
|
||||
|
||||
# get thread-local implementation, either from the thread
|
||||
# module, or from the python fallback
|
||||
|
||||
try:
|
||||
from _pydev_imps._pydev_thread import _local as local
|
||||
except ImportError:
|
||||
from _threading_local import local
|
||||
|
||||
|
||||
def _after_fork():
|
||||
# This function is called by Python/ceval.c:PyEval_ReInitThreads which
|
||||
# is called from PyOS_AfterFork. Here we cleanup threading module state
|
||||
# that should not exist after a fork.
|
||||
|
||||
# Reset _active_limbo_lock, in case we forked while the lock was held
|
||||
# by another (non-forked) thread. http://bugs.python.org/issue874900
|
||||
global _active_limbo_lock
|
||||
_active_limbo_lock = _allocate_lock()
|
||||
|
||||
# fork() only copied the current thread; clear references to others.
|
||||
new_active = {}
|
||||
current = current_thread()
|
||||
with _active_limbo_lock:
|
||||
for thread in _active.itervalues():
|
||||
# Any lock/condition variable may be currently locked or in an
|
||||
# invalid state, so we reinitialize them.
|
||||
if hasattr(thread, '_reset_internal_locks'):
|
||||
thread._reset_internal_locks()
|
||||
if thread is current:
|
||||
# There is only one active thread. We reset the ident to
|
||||
# its new value since it can have changed.
|
||||
ident = _get_ident()
|
||||
thread._Thread__ident = ident
|
||||
new_active[ident] = thread
|
||||
else:
|
||||
# All the others are already stopped.
|
||||
thread._Thread__stop()
|
||||
|
||||
_limbo.clear()
|
||||
_active.clear()
|
||||
_active.update(new_active)
|
||||
assert len(_active) == 1
|
||||
|
||||
|
||||
# Self-test code
|
||||
|
||||
def _test():
|
||||
|
||||
class BoundedQueue(_Verbose):
|
||||
|
||||
def __init__(self, limit):
|
||||
_Verbose.__init__(self)
|
||||
self.mon = RLock()
|
||||
self.rc = Condition(self.mon)
|
||||
self.wc = Condition(self.mon)
|
||||
self.limit = limit
|
||||
self.queue = deque()
|
||||
|
||||
def put(self, item):
|
||||
self.mon.acquire()
|
||||
while len(self.queue) >= self.limit:
|
||||
self._note("put(%s): queue full", item)
|
||||
self.wc.wait()
|
||||
self.queue.append(item)
|
||||
self._note("put(%s): appended, length now %d",
|
||||
item, len(self.queue))
|
||||
self.rc.notify()
|
||||
self.mon.release()
|
||||
|
||||
def get(self):
|
||||
self.mon.acquire()
|
||||
while not self.queue:
|
||||
self._note("get(): queue empty")
|
||||
self.rc.wait()
|
||||
item = self.queue.popleft()
|
||||
self._note("get(): got %s, %d left", item, len(self.queue))
|
||||
self.wc.notify()
|
||||
self.mon.release()
|
||||
return item
|
||||
|
||||
class ProducerThread(Thread):
|
||||
|
||||
def __init__(self, queue, quota):
|
||||
Thread.__init__(self, name="Producer")
|
||||
self.queue = queue
|
||||
self.quota = quota
|
||||
|
||||
def run(self):
|
||||
from random import random
|
||||
counter = 0
|
||||
while counter < self.quota:
|
||||
counter = counter + 1
|
||||
self.queue.put("%s.%d" % (self.name, counter))
|
||||
_sleep(random() * 0.00001)
|
||||
|
||||
|
||||
class ConsumerThread(Thread):
|
||||
|
||||
def __init__(self, queue, count):
|
||||
Thread.__init__(self, name="Consumer")
|
||||
self.queue = queue
|
||||
self.count = count
|
||||
|
||||
def run(self):
|
||||
while self.count > 0:
|
||||
item = self.queue.get()
|
||||
print item
|
||||
self.count = self.count - 1
|
||||
|
||||
NP = 3
|
||||
QL = 4
|
||||
NI = 5
|
||||
|
||||
Q = BoundedQueue(QL)
|
||||
P = []
|
||||
for i in range(NP):
|
||||
t = ProducerThread(Q, NI)
|
||||
t.name = ("Producer-%d" % (i+1))
|
||||
P.append(t)
|
||||
C = ConsumerThread(Q, NI*NP)
|
||||
for t in P:
|
||||
t.start()
|
||||
_sleep(0.000001)
|
||||
C.start()
|
||||
for t in P:
|
||||
t.join()
|
||||
C.join()
|
||||
|
||||
if __name__ == '__main__':
|
||||
_test()
|
||||
from gevent import monkey
|
||||
saved = monkey.saved['threading']
|
||||
for key, val in saved.items():
|
||||
globals()[key] = val
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
from time import *
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,13 @@
|
||||
def fixGetpass():
|
||||
import getpass
|
||||
import warnings
|
||||
fallback = getattr(getpass, 'fallback_getpass', None) # >= 2.6
|
||||
if not fallback:
|
||||
fallback = getpass.default_getpass # <= 2.5
|
||||
getpass.getpass = fallback
|
||||
if hasattr(getpass, 'GetPassWarning'):
|
||||
warnings.simplefilter("ignore", category=getpass.GetPassWarning)
|
||||
try:
|
||||
import getpass
|
||||
except ImportError:
|
||||
return #If we can't import it, we can't fix it
|
||||
import warnings
|
||||
fallback = getattr(getpass, 'fallback_getpass', None) # >= 2.6
|
||||
if not fallback:
|
||||
fallback = getpass.default_getpass # <= 2.5
|
||||
getpass.getpass = fallback
|
||||
if hasattr(getpass, 'GetPassWarning'):
|
||||
warnings.simplefilter("ignore", category=getpass.GetPassWarning)
|
||||
|
||||
|
||||
@@ -35,13 +35,25 @@ Done in the merge (started from the PyCharm version and bringing in things from
|
||||
|
||||
- When the code is interrupted, the buffer in the python side is cleared.
|
||||
|
||||
- GEvent debugging: for remote debugging, one has to import pydevd before doing the gevent patching -- even if
|
||||
pydevd.settrace will only be done later.
|
||||
|
||||
- GEvent debugging: improved PyDev not to use the threading module (uses the thread
|
||||
primitives directly), so, gevent debugging can work even if pydevd is used for
|
||||
remote debugging.
|
||||
|
||||
Also, the gevent debugging should probably be closer to the stackless debugging,
|
||||
where we actually show the live stackless threads -- so, we should show the live
|
||||
gevent greenlets -- which the current version doesn't do.
|
||||
gevent greenlets -- which the current version doesn't do (future work).
|
||||
|
||||
- Supporting Jython 2.2 onwards (note: CPython only tested with 2.7/3.3)
|
||||
|
||||
- When there are big sets/tuples/lists/dicts, the items won't be all shown so that the
|
||||
debugger speed doesn't suffer (the user should use the console if he wants to see
|
||||
those items in this case). The limit was set to show up to 300 items (pydevd_resolver.MAX_ITEMS_TO_HANDLE)
|
||||
|
||||
- Monkey-patching qt (QThread/QRunnable) to enable the debugger to work.
|
||||
Notes:
|
||||
- It must be imported before the user actually runs its code (as the definitions of QThread/QRunnable
|
||||
are monkey-patched), so, for the remote debugger to work, pydevd must be imported at the start of
|
||||
the program, even if pydevd.settrace will only be used later on.
|
||||
|
||||
Things to be fixed in PyCharm:
|
||||
--------------------------------
|
||||
|
||||
@@ -17,28 +17,18 @@ except NameError:
|
||||
setattr(__builtin__, 'True', 1) # Python 3.0 does not accept __builtin__.True = 1 in its syntax
|
||||
setattr(__builtin__, 'False', 0)
|
||||
|
||||
import pydevd_constants
|
||||
from pydevd_constants import IS_JYTHON
|
||||
|
||||
try:
|
||||
from java.lang import Thread
|
||||
IS_JYTHON = True
|
||||
if IS_JYTHON:
|
||||
SERVER_NAME = 'jycompletionserver'
|
||||
import _pydev_jy_imports_tipper # as _pydev_imports_tipper #changed to be backward compatible with 1.5
|
||||
_pydev_imports_tipper = _pydev_jy_imports_tipper
|
||||
|
||||
except ImportError:
|
||||
else:
|
||||
# it is python
|
||||
IS_JYTHON = False
|
||||
SERVER_NAME = 'pycompletionserver'
|
||||
if pydevd_constants.USE_LIB_COPY:
|
||||
from _pydev_threading import Thread
|
||||
else:
|
||||
from threading import Thread
|
||||
import _pydev_imports_tipper
|
||||
|
||||
|
||||
from _pydev_imps import _pydev_socket as socket
|
||||
|
||||
import sys
|
||||
if sys.platform == "darwin":
|
||||
# See: https://sourceforge.net/projects/pydev/forums/forum/293649/topic/3454227
|
||||
@@ -62,8 +52,6 @@ for name, mod in sys.modules.items():
|
||||
|
||||
import traceback
|
||||
|
||||
from _pydev_imps import _pydev_time as time
|
||||
|
||||
try:
|
||||
import StringIO
|
||||
except:
|
||||
@@ -185,14 +173,16 @@ class Processor:
|
||||
|
||||
return '%s(%s)%s' % (MSG_COMPLETIONS, ''.join(compMsg), MSG_END)
|
||||
|
||||
class Exit(Exception):
|
||||
pass
|
||||
|
||||
class T(Thread):
|
||||
class CompletionServer:
|
||||
|
||||
def __init__(self, port):
|
||||
Thread.__init__(self)
|
||||
self.ended = False
|
||||
self.port = port
|
||||
self.socket = None # socket to send messages.
|
||||
self.exit_process_on_kill = True
|
||||
self.processor = Processor()
|
||||
|
||||
|
||||
@@ -266,7 +256,7 @@ class T(Thread):
|
||||
while data.find(MSG_END) == -1:
|
||||
received = self.socket.recv(BUFFER_SIZE)
|
||||
if len(received) == 0:
|
||||
sys.exit(0) # ok, connection ended
|
||||
raise Exit() # ok, connection ended
|
||||
if IS_PYTHON3K:
|
||||
data = data + received.decode('utf-8')
|
||||
else:
|
||||
@@ -278,7 +268,7 @@ class T(Thread):
|
||||
dbg(SERVER_NAME + ' kill message received', INFO1)
|
||||
# break if we received kill message.
|
||||
self.ended = True
|
||||
sys.exit(0)
|
||||
raise Exit()
|
||||
|
||||
dbg(SERVER_NAME + ' starting keep alive thread', INFO2)
|
||||
|
||||
@@ -359,7 +349,7 @@ class T(Thread):
|
||||
|
||||
else:
|
||||
self.send(MSG_INVALID_REQUEST)
|
||||
except SystemExit:
|
||||
except Exit:
|
||||
self.send(self.getCompletionsMessage(None, [('Exit:', 'SystemExit', '')]))
|
||||
raise
|
||||
|
||||
@@ -378,11 +368,12 @@ class T(Thread):
|
||||
|
||||
self.socket.close()
|
||||
self.ended = True
|
||||
sys.exit(0) # connection broken
|
||||
raise Exit() # connection broken
|
||||
|
||||
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exit:
|
||||
if self.exit_process_on_kill:
|
||||
sys.exit(0)
|
||||
# No need to log SystemExit error
|
||||
except:
|
||||
s = StringIO.StringIO()
|
||||
@@ -399,8 +390,6 @@ if __name__ == '__main__':
|
||||
|
||||
port = int(sys.argv[1]) # this is from where we want to receive messages.
|
||||
|
||||
t = T(port)
|
||||
t = CompletionServer(port)
|
||||
dbg(SERVER_NAME + ' will start', INFO1)
|
||||
t.start()
|
||||
time.sleep(5)
|
||||
t.join()
|
||||
t.run()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from pydev_imports import xmlrpclib, _queue, Exec
|
||||
import sys
|
||||
from pydevd_constants import USE_LIB_COPY
|
||||
from pydevd_constants import IS_JYTHON
|
||||
from _pydev_imps import _pydev_thread as thread
|
||||
import pydevd_xml
|
||||
@@ -418,10 +417,7 @@ class BaseInterpreterInterface:
|
||||
try:
|
||||
# Try to import the packages needed to attach the debugger
|
||||
import pydevd
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
import _pydev_threading as threading
|
||||
|
||||
except:
|
||||
# This happens on Jython embedded in host eclipse
|
||||
|
||||
@@ -53,9 +53,9 @@ except:
|
||||
from pydevd_exec2 import Exec
|
||||
|
||||
try:
|
||||
from urllib import quote
|
||||
from urllib import quote, quote_plus, unquote_plus
|
||||
except:
|
||||
from urllib.parse import quote #@UnresolvedImport
|
||||
from urllib.parse import quote, quote_plus, unquote_plus #@UnresolvedImport
|
||||
|
||||
|
||||
import os
|
||||
|
||||
@@ -19,11 +19,7 @@ Author: Christian Boos
|
||||
import os
|
||||
import signal
|
||||
|
||||
from pydevd_constants import USE_LIB_COPY
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
import threading
|
||||
|
||||
|
||||
from pydev_ipython.qt_for_kernel import QtCore, QtGui
|
||||
|
||||
@@ -9,7 +9,7 @@ os.environ['TERM'] = 'emacs' #to use proper page_more() for paging
|
||||
# Uncomment to force PyDev standard shell.
|
||||
# raise ImportError()
|
||||
|
||||
from pydev_ipython_console_011 import PyDevFrontEnd
|
||||
from pydev_ipython_console_011 import get_pydev_frontend
|
||||
|
||||
#=======================================================================================================================
|
||||
# InterpreterInterface
|
||||
@@ -23,7 +23,7 @@ class InterpreterInterface(BaseInterpreterInterface):
|
||||
BaseInterpreterInterface.__init__(self, mainThread)
|
||||
self.client_port = client_port
|
||||
self.host = host
|
||||
self.interpreter = PyDevFrontEnd(host, client_port)
|
||||
self.interpreter = get_pydev_frontend(host, client_port)
|
||||
self._input_error_printed = False
|
||||
self.notification_succeeded = False
|
||||
self.notification_tries = 0
|
||||
|
||||
@@ -19,7 +19,6 @@ import os
|
||||
import codeop
|
||||
|
||||
from IPython.core.error import UsageError
|
||||
from IPython.core.inputsplitter import IPythonInputSplitter
|
||||
from IPython.core.completer import IPCompleter
|
||||
from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
|
||||
from IPython.core.usage import default_banner_parts
|
||||
@@ -53,7 +52,8 @@ def show_in_pager(self, strng):
|
||||
print(strng)
|
||||
|
||||
def create_editor_hook(pydev_host, pydev_client_port):
|
||||
def call_editor(self, filename, line=0, wait=True):
|
||||
|
||||
def call_editor(filename, line=0, wait=True):
|
||||
""" Open an editor in PyDev """
|
||||
if line is None:
|
||||
line = 0
|
||||
@@ -62,6 +62,9 @@ def create_editor_hook(pydev_host, pydev_client_port):
|
||||
# we don't launch a process. This is more like what happens in the zmqshell
|
||||
filename = os.path.abspath(filename)
|
||||
|
||||
# import sys
|
||||
# sys.__stderr__.write('Calling editor at: %s:%s\n' % (pydev_host, pydev_client_port))
|
||||
|
||||
# Tell PyDev to open the editor
|
||||
server = xmlrpclib.Server('http://%s:%s' % (pydev_host, pydev_client_port))
|
||||
server.IPythonEditor(filename, str(line))
|
||||
@@ -291,22 +294,17 @@ class PyDevTerminalInteractiveShell(TerminalInteractiveShell):
|
||||
InteractiveShellABC.register(PyDevTerminalInteractiveShell) # @UndefinedVariable
|
||||
|
||||
#=======================================================================================================================
|
||||
# PyDevFrontEnd
|
||||
# _PyDevFrontEnd
|
||||
#=======================================================================================================================
|
||||
class PyDevFrontEnd:
|
||||
class _PyDevFrontEnd:
|
||||
|
||||
version = release.__version__
|
||||
|
||||
def __init__(self, pydev_host, pydev_client_port, *args, **kwarg):
|
||||
def __init__(self, *args, **kwarg):
|
||||
|
||||
# Create and initialize our IPython instance.
|
||||
self.ipython = PyDevTerminalInteractiveShell.instance()
|
||||
|
||||
# Back channel to PyDev to open editors (in the future other
|
||||
# info may go back this way. This is the same channel that is
|
||||
# used to get stdin, see StdIn in pydev_console_utils)
|
||||
self.ipython.set_hook('editor', create_editor_hook(pydev_host, pydev_client_port))
|
||||
|
||||
# Display the IPython banner, this has version info and
|
||||
# help info
|
||||
self.ipython.show_banner()
|
||||
@@ -412,6 +410,9 @@ class PyDevFrontEnd:
|
||||
|
||||
def getNamespace(self):
|
||||
return self.ipython.user_ns
|
||||
|
||||
def clearBuffer(self):
|
||||
del self._curr_exec_lines[:]
|
||||
|
||||
def addExec(self, line):
|
||||
if self._curr_exec_lines:
|
||||
@@ -463,3 +464,28 @@ IPython.lib.inputhook.enable_gui = pydev_ipython.inputhook.enable_gui
|
||||
# rely on using the inputhooks directly.
|
||||
for name in pydev_ipython.inputhook.__all__:
|
||||
setattr(IPython.lib.inputhook, name, getattr(pydev_ipython.inputhook, name))
|
||||
|
||||
|
||||
class _PyDevFrontEndContainer:
|
||||
_instance = None
|
||||
_last_host_port = None
|
||||
|
||||
def get_pydev_frontend(pydev_host, pydev_client_port):
|
||||
if _PyDevFrontEndContainer._instance is None:
|
||||
_PyDevFrontEndContainer._instance = _PyDevFrontEnd()
|
||||
|
||||
if _PyDevFrontEndContainer._last_host_port != (pydev_host, pydev_client_port):
|
||||
_PyDevFrontEndContainer._last_host_port = pydev_host, pydev_client_port
|
||||
|
||||
# Back channel to PyDev to open editors (in the future other
|
||||
# info may go back this way. This is the same channel that is
|
||||
# used to get stdin, see StdIn in pydev_console_utils)
|
||||
_PyDevFrontEndContainer._instance.ipython.hooks['editor'] = create_editor_hook(pydev_host, pydev_client_port)
|
||||
|
||||
# Note: setting the callback directly because setting it with set_hook would actually create a chain instead
|
||||
# of ovewriting at each new call).
|
||||
# _PyDevFrontEndContainer._instance.ipython.set_hook('editor', create_editor_hook(pydev_host, pydev_client_port))
|
||||
|
||||
return _PyDevFrontEndContainer._instance
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
from _pydev_imps import _pydev_socket as socket
|
||||
|
||||
_cache = None
|
||||
|
||||
@@ -392,40 +392,42 @@ def patch_new_process_functions_with_warning():
|
||||
|
||||
class _NewThreadStartupWithTrace:
|
||||
|
||||
def __init__(self, original_func):
|
||||
def __init__(self, original_func, args, kwargs):
|
||||
self.original_func = original_func
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
def __call__(self):
|
||||
from pydevd_comm import GetGlobalDebugger
|
||||
global_debugger = GetGlobalDebugger()
|
||||
if global_debugger is not None:
|
||||
global_debugger.SetTrace(global_debugger.trace_dispatch)
|
||||
|
||||
return self.original_func(*args, **kwargs)
|
||||
return self.original_func(*self.args, **self.kwargs)
|
||||
|
||||
class _NewThreadStartupWithoutTrace:
|
||||
|
||||
def __init__(self, original_func):
|
||||
def __init__(self, original_func, args, kwargs):
|
||||
self.original_func = original_func
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.original_func(*args, **kwargs)
|
||||
def __call__(self):
|
||||
return self.original_func(*self.args, **self.kwargs)
|
||||
|
||||
_UseNewThreadStartup = _NewThreadStartupWithTrace
|
||||
|
||||
def _get_threading_modules():
|
||||
threading_modules = []
|
||||
from _pydev_imps import _pydev_thread
|
||||
threading_modules.append(_pydev_thread)
|
||||
def _get_threading_modules_to_patch():
|
||||
threading_modules_to_patch = []
|
||||
try:
|
||||
import thread as _thread
|
||||
threading_modules.append(_thread)
|
||||
threading_modules_to_patch.append(_thread)
|
||||
except:
|
||||
import _thread
|
||||
threading_modules.append(_thread)
|
||||
return threading_modules
|
||||
threading_modules_to_patch.append(_thread)
|
||||
return threading_modules_to_patch
|
||||
|
||||
threading_modules = _get_threading_modules()
|
||||
threading_modules_to_patch = _get_threading_modules_to_patch()
|
||||
|
||||
|
||||
|
||||
@@ -439,12 +441,12 @@ def patch_thread_module(thread):
|
||||
|
||||
class ClassWithPydevStartNewThread:
|
||||
|
||||
def pydev_start_new_thread(self, function, args, kwargs={}):
|
||||
def pydev_start_new_thread(self, function, args=(), kwargs={}):
|
||||
'''
|
||||
We need to replace the original thread.start_new_thread with this function so that threads started
|
||||
through it and not through the threading module are properly traced.
|
||||
'''
|
||||
return _original_start_new_thread(_UseNewThreadStartup(function), args, kwargs)
|
||||
return _original_start_new_thread(_UseNewThreadStartup(function, args, kwargs), ())
|
||||
|
||||
# This is a hack for the situation where the thread.start_new_thread is declared inside a class, such as the one below
|
||||
# class F(object):
|
||||
@@ -465,11 +467,11 @@ def patch_thread_module(thread):
|
||||
pass
|
||||
|
||||
def patch_thread_modules():
|
||||
for t in threading_modules:
|
||||
for t in threading_modules_to_patch:
|
||||
patch_thread_module(t)
|
||||
|
||||
def undo_patch_thread_modules():
|
||||
for t in threading_modules:
|
||||
for t in threading_modules_to_patch:
|
||||
try:
|
||||
t.start_new_thread = t._original_start_new_thread
|
||||
except:
|
||||
@@ -494,3 +496,9 @@ def enable_trace_thread_modules():
|
||||
'''
|
||||
global _UseNewThreadStartup
|
||||
_UseNewThreadStartup = _NewThreadStartupWithTrace
|
||||
|
||||
def get_original_start_new_thread(threading_module):
|
||||
try:
|
||||
return threading_module._original_start_new_thread
|
||||
except:
|
||||
return threading_module.start_new_thread
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
from __future__ import nested_scopes
|
||||
|
||||
def set_trace_in_qt():
|
||||
import pydevd_tracing
|
||||
from pydevd_comm import GetGlobalDebugger
|
||||
debugger = GetGlobalDebugger()
|
||||
if debugger is not None:
|
||||
pydevd_tracing.SetTrace(debugger.trace_dispatch)
|
||||
|
||||
|
||||
_patched_qt = False
|
||||
def patch_qt():
|
||||
'''
|
||||
This method patches qt (PySide or PyQt4) so that we have hooks to set the tracing for QThread.
|
||||
'''
|
||||
|
||||
# Avoid patching more than once
|
||||
global _patched_qt
|
||||
if _patched_qt:
|
||||
return
|
||||
|
||||
_patched_qt = True
|
||||
|
||||
try:
|
||||
from PySide import QtCore
|
||||
except:
|
||||
try:
|
||||
from PyQt4 import QtCore
|
||||
except:
|
||||
return
|
||||
|
||||
_original_thread_init = QtCore.QThread.__init__
|
||||
_original_runnable_init = QtCore.QRunnable.__init__
|
||||
|
||||
|
||||
class FuncWrapper:
|
||||
|
||||
def __init__(self, original):
|
||||
self._original = original
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
set_trace_in_qt()
|
||||
return self._original(*args, **kwargs)
|
||||
|
||||
class StartedSignalWrapper: # Wrapper for the QThread.started signal
|
||||
|
||||
def __init__(self, thread, original_started):
|
||||
self.thread = thread
|
||||
self.original_started = original_started
|
||||
|
||||
def connect(self, func, *args, **kwargs):
|
||||
return self.original_started.connect(FuncWrapper(func), *args, **kwargs)
|
||||
|
||||
def disconnect(self, *args, **kwargs):
|
||||
return self.original_started.disconnect(*args, **kwargs)
|
||||
|
||||
def emit(self, *args, **kwargs):
|
||||
return self.original_started.emit(*args, **kwargs)
|
||||
|
||||
|
||||
class ThreadWrapper(QtCore.QThread): # Wrapper for QThread
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
_original_thread_init(self)
|
||||
|
||||
self._original_run = self.run
|
||||
self.run = self._new_run
|
||||
self._original_started = self.started
|
||||
self.started = StartedSignalWrapper(self, self.started)
|
||||
|
||||
def _new_run(self):
|
||||
set_trace_in_qt()
|
||||
return self._original_run()
|
||||
|
||||
class RunnableWrapper(QtCore.QRunnable): # Wrapper for QRunnable
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
_original_runnable_init(self)
|
||||
|
||||
self._original_run = self.run
|
||||
self.run = self._new_run
|
||||
|
||||
|
||||
def _new_run(self):
|
||||
set_trace_in_qt()
|
||||
return self._original_run()
|
||||
|
||||
QtCore.QThread = ThreadWrapper
|
||||
QtCore.QRunnable = RunnableWrapper
|
||||
@@ -1,4 +1,5 @@
|
||||
import unittest
|
||||
from _pydev_imps import _pydev_thread
|
||||
try:
|
||||
import Queue
|
||||
except:
|
||||
@@ -282,13 +283,9 @@ class ClientThread(threading.Thread):
|
||||
if False:
|
||||
proc = subprocess.Popen(args, env=os.environ, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
stdout_thread = threading.Thread(target=self._reader_thread,args=(proc.stdout, sys.stdout))
|
||||
stdout_thread.setDaemon(True)
|
||||
stdout_thread.start()
|
||||
_pydev_thread.start_new_thread(self._reader_thread,(proc.stdout, sys.stdout))
|
||||
|
||||
stderr_thread = threading.Thread(target=self._reader_thread,args=(proc.stderr, sys.stderr))
|
||||
stderr_thread.setDaemon(True)
|
||||
stderr_thread.start()
|
||||
_pydev_thread.start_new_thread(target=self._reader_thread,args=(proc.stderr, sys.stderr))
|
||||
else:
|
||||
proc = subprocess.Popen(args, env=os.environ, shell=False)
|
||||
proc.wait()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
import traceback
|
||||
import warnings
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from _pydev_imps._pydev_thread import start_new_thread
|
||||
|
||||
try:
|
||||
from code import InteractiveConsole
|
||||
except ImportError:
|
||||
@@ -9,12 +11,7 @@ from code import InteractiveInterpreter
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pydevd_constants import USE_LIB_COPY
|
||||
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
import _pydev_threading as threading
|
||||
|
||||
import traceback
|
||||
import fix_getpass
|
||||
@@ -61,13 +58,14 @@ class Command:
|
||||
self.code_fragment = code_fragment
|
||||
self.more = None
|
||||
|
||||
@staticmethod
|
||||
|
||||
def symbol_for_fragment(code_fragment):
|
||||
if code_fragment.is_single_line:
|
||||
symbol = 'single'
|
||||
else:
|
||||
symbol = 'exec' # Jython doesn't support this
|
||||
return symbol
|
||||
symbol_for_fragment = staticmethod(symbol_for_fragment)
|
||||
|
||||
def run(self):
|
||||
text = self.code_fragment.text
|
||||
@@ -304,11 +302,7 @@ def StartServer(host, port, client_port):
|
||||
|
||||
interpreter = InterpreterInterface(host, client_port, threading.currentThread())
|
||||
|
||||
server_thread = threading.Thread(target=start_server,
|
||||
name='ServerThread',
|
||||
args=(host, port, interpreter))
|
||||
server_thread.setDaemon(True)
|
||||
server_thread.start()
|
||||
start_new_thread(start_server,(host, port, interpreter))
|
||||
|
||||
process_exec_queue(interpreter)
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#IMPORTANT: pydevd_constants must be the 1st thing defined because it'll keep a reference to the original sys._getframe
|
||||
from __future__ import nested_scopes # Jython 2.1 support
|
||||
|
||||
import pydev_monkey_qt
|
||||
pydev_monkey_qt.patch_qt()
|
||||
|
||||
import traceback
|
||||
|
||||
from django_debug import DjangoLineBreakpoint
|
||||
from pydevd_signature import SignatureFactory
|
||||
from pydevd_frame import add_exception_to_frame
|
||||
import pydev_imports
|
||||
from pydevd_breakpoints import * #@UnusedWildImport
|
||||
@@ -81,14 +83,12 @@ from pydevd_custom_frames import CustomFramesContainer, CustomFramesContainerIni
|
||||
import pydevd_dont_trace
|
||||
import pydevd_traceproperty
|
||||
|
||||
from _pydev_imps import _pydev_time as time
|
||||
from _pydev_imps import _pydev_time as time, _pydev_thread
|
||||
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
import _pydev_threading as threading
|
||||
|
||||
import os
|
||||
import atexit
|
||||
|
||||
|
||||
threadingEnumerate = threading.enumerate
|
||||
@@ -112,9 +112,11 @@ DONT_TRACE = {
|
||||
'_pydev_execfile.py':1,
|
||||
'_pydev_jython_execfile.py':1,
|
||||
'_pydev_threading':1,
|
||||
'_pydev_Queue':1,
|
||||
'django_debug.py':1,
|
||||
'django_frame.py':1,
|
||||
'pydev_log.py':1,
|
||||
'pydev_monkey.py':1 ,
|
||||
'pydevd.py':1 ,
|
||||
'pydevd_additional_thread_info.py':1,
|
||||
'pydevd_comm.py':1,
|
||||
@@ -219,7 +221,7 @@ class PyDBCommandThread(PyDBDaemonThread):
|
||||
|
||||
|
||||
def killAllPydevThreads():
|
||||
threads = threadingEnumerate()
|
||||
threads = DictKeys(PyDBDaemonThread.created_pydb_daemon_threads)
|
||||
for t in threads:
|
||||
if hasattr(t, 'doKillPydevThread'):
|
||||
t.doKillPydevThread()
|
||||
@@ -233,12 +235,23 @@ class PyDBCheckAliveThread(PyDBDaemonThread):
|
||||
def __init__(self, pyDb):
|
||||
PyDBDaemonThread.__init__(self)
|
||||
self.pyDb = pyDb
|
||||
self.setDaemon(False)
|
||||
self.setName('pydevd.CheckAliveThread')
|
||||
|
||||
def OnRun(self):
|
||||
if self.dontTraceMe:
|
||||
self.pyDb.SetTrace(None) # no debugging on this thread
|
||||
|
||||
disable_tracing = True
|
||||
|
||||
if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON and sys.hexversion <= 0x020201f0:
|
||||
# don't run untraced threads if we're in jython 2.2.1 or lower
|
||||
# jython bug: if we start a thread and another thread changes the tracing facility
|
||||
# it affects other threads (it's not set only for the thread but globally)
|
||||
# Bug: http://sourceforge.net/tracker/index.php?func=detail&aid=1870039&group_id=12867&atid=112867
|
||||
disable_tracing = False
|
||||
|
||||
if disable_tracing:
|
||||
pydevd_tracing.SetTrace(None) # no debugging on this thread
|
||||
|
||||
while not self.killReceived:
|
||||
if not self.pyDb.haveAliveThreads():
|
||||
try:
|
||||
@@ -298,8 +311,8 @@ class PyDB:
|
||||
|
||||
self.django_exception_break = {}
|
||||
self.readyToRun = False
|
||||
self._main_lock = threading.Lock()
|
||||
self._lock_running_thread_ids = threading.Lock()
|
||||
self._main_lock = _pydev_thread.allocate_lock()
|
||||
self._lock_running_thread_ids = _pydev_thread.allocate_lock()
|
||||
self._py_db_command_thread_event = threading.Event()
|
||||
CustomFramesContainer._py_db_command_thread_event = self._py_db_command_thread_event
|
||||
self._finishDebuggingSession = False
|
||||
@@ -332,7 +345,11 @@ class PyDB:
|
||||
|
||||
def haveAliveThreads(self):
|
||||
for t in threadingEnumerate():
|
||||
if not isinstance(t, PyDBDaemonThread) and isThreadAlive(t) and not t.isDaemon():
|
||||
if isinstance(t, PyDBDaemonThread):
|
||||
pydev_log.error_once(
|
||||
'Error in debugger: Found PyDBDaemonThread through threading.enumerate().\n')
|
||||
|
||||
if isThreadAlive(t) and not t.isDaemon():
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -387,11 +404,9 @@ class PyDB:
|
||||
if thread_id == "*":
|
||||
threads = threadingEnumerate()
|
||||
for t in threads:
|
||||
thread_name = t.getName()
|
||||
if not thread_name.startswith('pydevd.') or thread_name == 'pydevd.CommandThread':
|
||||
thread_id = GetThreadId(t)
|
||||
queue = self.getInternalQueue(thread_id)
|
||||
queue.put(int_cmd)
|
||||
thread_id = GetThreadId(t)
|
||||
queue = self.getInternalQueue(thread_id)
|
||||
queue.put(int_cmd)
|
||||
|
||||
else:
|
||||
queue = self.getInternalQueue(thread_id)
|
||||
@@ -442,7 +457,13 @@ class PyDB:
|
||||
for t in all_threads:
|
||||
thread_id = GetThreadId(t)
|
||||
|
||||
if not isinstance(t, PyDBDaemonThread) and isThreadAlive(t):
|
||||
if isinstance(t, PyDBDaemonThread):
|
||||
pydev_log.error_once('Found PyDBDaemonThread in threading.enumerate.')
|
||||
|
||||
elif getattr(t, 'is_pydev_daemon_thread', False):
|
||||
pass # I.e.: skip the DummyThreads created from pydev daemon threads
|
||||
|
||||
elif isThreadAlive(t):
|
||||
program_threads_alive[thread_id] = t
|
||||
|
||||
if not DictContains(self._running_thread_ids, thread_id):
|
||||
@@ -505,19 +526,18 @@ class PyDB:
|
||||
threads = threadingEnumerate()
|
||||
try:
|
||||
for t in threads:
|
||||
if not t.getName().startswith('pydevd.'):
|
||||
# TODO: optimize so that we only actually add that tracing if it's in
|
||||
# the new breakpoint context.
|
||||
additionalInfo = None
|
||||
try:
|
||||
additionalInfo = t.additionalInfo
|
||||
except AttributeError:
|
||||
pass # that's ok, no info currently set
|
||||
# TODO: optimize so that we only actually add that tracing if it's in
|
||||
# the new breakpoint context.
|
||||
additionalInfo = None
|
||||
try:
|
||||
additionalInfo = t.additionalInfo
|
||||
except AttributeError:
|
||||
pass # that's ok, no info currently set
|
||||
|
||||
if additionalInfo is not None:
|
||||
for frame in additionalInfo.IterFrames():
|
||||
if frame is not ignore_frame:
|
||||
self.SetTraceForFrameAndParents(frame, overwrite_prev_trace=overwrite_prev_trace)
|
||||
if additionalInfo is not None:
|
||||
for frame in additionalInfo.IterFrames():
|
||||
if frame is not ignore_frame:
|
||||
self.SetTraceForFrameAndParents(frame, overwrite_prev_trace=overwrite_prev_trace)
|
||||
finally:
|
||||
frame = None
|
||||
t = None
|
||||
@@ -592,7 +612,7 @@ class PyDB:
|
||||
it may be worth refactoring it (actually, reordering the ifs so that the ones used mostly come before
|
||||
probably will give better performance).
|
||||
'''
|
||||
#print ID_TO_MEANING[str(cmd_id)], repr(text)
|
||||
#print(ID_TO_MEANING[str(cmd_id)], repr(text))
|
||||
|
||||
self._main_lock.acquire()
|
||||
try:
|
||||
@@ -834,7 +854,7 @@ class PyDB:
|
||||
id_to_pybreakpoint[breakpoint_id] = breakpoint
|
||||
self.consolidate_breakpoints(file, id_to_pybreakpoint, breakpoints)
|
||||
|
||||
self.setTracingForUntracedContexts()
|
||||
self.setTracingForUntracedContexts(overwrite_prev_trace=True)
|
||||
|
||||
elif cmd_id == CMD_REMOVE_BREAK:
|
||||
#command to remove some breakpoint
|
||||
@@ -862,7 +882,7 @@ class PyDB:
|
||||
raise NameError(breakpoint_type)
|
||||
|
||||
try:
|
||||
id_to_pybreakpoint = file_to_id_to_breakpoint[file]
|
||||
id_to_pybreakpoint = file_to_id_to_breakpoint.get(file, {})
|
||||
if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
|
||||
existing = id_to_pybreakpoint[breakpoint_id]
|
||||
sys.stderr.write('Removed breakpoint:%s - line:%s - func_name:%s (id: %s)\n' % (
|
||||
@@ -1319,7 +1339,7 @@ class PyDB:
|
||||
if self._finishDebuggingSession and not self._terminationEventSent:
|
||||
#that was not working very well because jython gave some socket errors
|
||||
try:
|
||||
threads = threadingEnumerate()
|
||||
threads = DictKeys(PyDBDaemonThread.created_pydb_daemon_threads)
|
||||
for t in threads:
|
||||
if hasattr(t, 'doKillPydevThread'):
|
||||
t.doKillPydevThread()
|
||||
@@ -1332,10 +1352,10 @@ class PyDB:
|
||||
|
||||
is_file_to_ignore = DictContains(DONT_TRACE, base) #we don't want to debug threading or anything related to pydevd
|
||||
|
||||
#print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, is_file_to_ignore)
|
||||
if is_file_to_ignore:
|
||||
return None
|
||||
|
||||
#print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name)
|
||||
try:
|
||||
#this shouldn't give an exception, but it could happen... (python bug)
|
||||
#see http://mail.python.org/pipermail/python-bugs-list/2007-June/038796.html
|
||||
@@ -1401,8 +1421,9 @@ class PyDB:
|
||||
|
||||
|
||||
|
||||
def SetTraceForFrameAndParents(self, frame, also_add_to_passed_frame=True, overwrite_prev_trace=False):
|
||||
dispatch_func = self.trace_dispatch
|
||||
def SetTraceForFrameAndParents(self, frame, also_add_to_passed_frame=True, overwrite_prev_trace=False, dispatch_func=None):
|
||||
if dispatch_func is None:
|
||||
dispatch_func = self.trace_dispatch
|
||||
|
||||
if also_add_to_passed_frame:
|
||||
self.update_trace(frame, dispatch_func, overwrite_prev_trace)
|
||||
@@ -1439,8 +1460,8 @@ class PyDB:
|
||||
net = NetCommand(str(CMD_THREAD_CREATE), 0, '<xml><thread name="pydevd.writer" id="-1"/></xml>')
|
||||
self.writer.addCommand(net)
|
||||
|
||||
pydevd_tracing.SetTrace(self.trace_dispatch)
|
||||
self.patch_threads()
|
||||
pydevd_tracing.SetTrace(self.trace_dispatch)
|
||||
|
||||
|
||||
PyDBCommandThread(self).start()
|
||||
@@ -1543,43 +1564,47 @@ def processCommandLine(argv):
|
||||
setup['multiproc'] = False #Used by PyCharm (reuses connection: ssh tunneling)
|
||||
setup['multiprocess'] = False # Used by PyDev (creates new connection to ide)
|
||||
setup['save-signatures'] = False
|
||||
setup['print-in-debugger-startup'] = False
|
||||
i = 0
|
||||
del argv[0]
|
||||
while (i < len(argv)):
|
||||
if (argv[i] == '--port'):
|
||||
if argv[i] == '--port':
|
||||
del argv[i]
|
||||
setup['port'] = int(argv[i])
|
||||
del argv[i]
|
||||
elif (argv[i] == '--vm_type'):
|
||||
elif argv[i] == '--vm_type':
|
||||
del argv[i]
|
||||
setup['vm_type'] = argv[i]
|
||||
del argv[i]
|
||||
elif (argv[i] == '--client'):
|
||||
elif argv[i] == '--client':
|
||||
del argv[i]
|
||||
setup['client'] = argv[i]
|
||||
del argv[i]
|
||||
elif (argv[i] == '--server'):
|
||||
elif argv[i] == '--server':
|
||||
del argv[i]
|
||||
setup['server'] = True
|
||||
elif (argv[i] == '--file'):
|
||||
elif argv[i] == '--file':
|
||||
del argv[i]
|
||||
setup['file'] = argv[i]
|
||||
i = len(argv) # pop out, file is our last argument
|
||||
elif (argv[i] == '--DEBUG_RECORD_SOCKET_READS'):
|
||||
elif argv[i] == '--DEBUG_RECORD_SOCKET_READS':
|
||||
del argv[i]
|
||||
setup['DEBUG_RECORD_SOCKET_READS'] = True
|
||||
elif (argv[i] == '--DEBUG'):
|
||||
elif argv[i] == '--DEBUG':
|
||||
del argv[i]
|
||||
set_debug(setup)
|
||||
elif (argv[i] == '--multiproc'):
|
||||
elif argv[i] == '--multiproc':
|
||||
del argv[i]
|
||||
setup['multiproc'] = True
|
||||
elif (argv[i] == '--multiprocess'):
|
||||
elif argv[i] == '--multiprocess':
|
||||
del argv[i]
|
||||
setup['multiprocess'] = True
|
||||
elif (argv[i] == '--save-signatures'):
|
||||
elif argv[i] == '--save-signatures':
|
||||
del argv[i]
|
||||
setup['save-signatures'] = True
|
||||
elif argv[i] == '--print-in-debugger-startup':
|
||||
del argv[i]
|
||||
setup['print-in-debugger-startup'] = True
|
||||
else:
|
||||
raise ValueError("unexpected option " + argv[i])
|
||||
return setup
|
||||
@@ -1590,18 +1615,6 @@ def usage(doExit=0):
|
||||
if doExit:
|
||||
sys.exit(0)
|
||||
|
||||
def SetTraceForParents(frame, dispatch_func):
|
||||
frame = frame.f_back
|
||||
while frame:
|
||||
if frame.f_trace is None:
|
||||
frame.f_trace = dispatch_func
|
||||
|
||||
frame = frame.f_back
|
||||
del frame
|
||||
|
||||
def exit_hook():
|
||||
debugger = GetGlobalDebugger()
|
||||
debugger.exiting()
|
||||
|
||||
def initStdoutRedirect():
|
||||
if not getattr(sys, 'stdoutBuf', None):
|
||||
@@ -1666,7 +1679,7 @@ def settrace(
|
||||
|
||||
|
||||
|
||||
_set_trace_lock = threading.Lock()
|
||||
_set_trace_lock = _pydev_thread.allocate_lock()
|
||||
|
||||
def _locked_settrace(
|
||||
host,
|
||||
@@ -1748,7 +1761,9 @@ def _locked_settrace(
|
||||
# As this is the first connection, also set tracing for any untraced threads
|
||||
debugger.setTracingForUntracedContexts(ignore_frame=GetFrame(), overwrite_prev_trace=overwrite_prev_trace)
|
||||
|
||||
sys.exitfunc = exit_hook
|
||||
# Stop the tracing as the last thing before the actual shutdown for a clean exit.
|
||||
atexit.register(stoptrace)
|
||||
|
||||
#Suspend as the last thing after all tracing is in place.
|
||||
if suspend:
|
||||
debugger.setSuspend(t, CMD_SET_BREAK)
|
||||
@@ -1793,16 +1808,15 @@ def stoptrace():
|
||||
|
||||
from pydev_monkey import undo_patch_thread_modules
|
||||
undo_patch_thread_modules()
|
||||
|
||||
|
||||
debugger = GetGlobalDebugger()
|
||||
|
||||
|
||||
if debugger:
|
||||
debugger.trace_dispatch = None
|
||||
|
||||
debugger.SetTraceForFrameAndParents(GetFrame(), False)
|
||||
|
||||
|
||||
debugger.SetTraceForFrameAndParents(
|
||||
GetFrame(), also_add_to_passed_frame=True, overwrite_prev_trace=True, dispatch_func=lambda *args:None)
|
||||
debugger.exiting()
|
||||
|
||||
|
||||
killAllPydevThreads()
|
||||
|
||||
connected = False
|
||||
@@ -1893,6 +1907,7 @@ class SetupHolder:
|
||||
# main
|
||||
#=======================================================================================================================
|
||||
if __name__ == '__main__':
|
||||
|
||||
# parse the command line. --file is our last argument that is required
|
||||
try:
|
||||
sys.original_argv = sys.argv[:]
|
||||
@@ -1902,6 +1917,12 @@ if __name__ == '__main__':
|
||||
traceback.print_exc()
|
||||
usage(1)
|
||||
|
||||
if setup['print-in-debugger-startup']:
|
||||
try:
|
||||
pid = ' (pid: %s)' % os.getpid()
|
||||
except:
|
||||
pid = ''
|
||||
sys.stderr.write("pydev debugger: starting%s\n" % pid)
|
||||
|
||||
fix_getpass.fixGetpass()
|
||||
|
||||
@@ -2047,6 +2068,8 @@ if __name__ == '__main__':
|
||||
if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON:
|
||||
sys.stderr.write("Collecting run-time type information is not supported for Jython\n")
|
||||
else:
|
||||
# Only import it if we're going to use it!
|
||||
from pydevd_signature import SignatureFactory
|
||||
debugger.signature_factory = SignatureFactory()
|
||||
|
||||
try:
|
||||
@@ -2059,3 +2082,4 @@ if __name__ == '__main__':
|
||||
connected = True # Mark that we're connected when started from inside ide.
|
||||
|
||||
debugger.run(setup['file'], None, None)
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import sys
|
||||
from pydevd_constants import * #@UnusedWildImport
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
from _pydev_imps import _pydev_thread
|
||||
from pydevd_frame import PyDBFrame
|
||||
import weakref
|
||||
|
||||
@@ -62,7 +59,7 @@ class PyDBAdditionalThreadInfoWithoutCurrentFramesSupport(AbstractPyDBAdditional
|
||||
#Or if the user compiled threadframe (from http://www.majid.info/mylos/stories/2004/06/10/threadframe.html)
|
||||
|
||||
#NOT RLock!! (could deadlock if it was)
|
||||
self.lock = threading.Lock()
|
||||
self.lock = _pydev_thread.allocate_lock()
|
||||
self._acquire_lock = self.lock.acquire
|
||||
self._release_lock = self.lock.release
|
||||
|
||||
|
||||
@@ -8,10 +8,7 @@ _original_excepthook = None
|
||||
_handle_exceptions = None
|
||||
|
||||
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
import _pydev_threading as threading
|
||||
|
||||
threadingCurrentThread = threading.currentThread
|
||||
|
||||
|
||||
@@ -61,12 +61,8 @@ from pydevd_constants import * #@UnusedWildImport
|
||||
|
||||
import sys
|
||||
|
||||
from _pydev_imps import _pydev_time as time
|
||||
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
from _pydev_imps import _pydev_time as time, _pydev_thread
|
||||
import _pydev_threading as threading
|
||||
from _pydev_imps._pydev_socket import socket, AF_INET, SOCK_STREAM, SHUT_RD, SHUT_WR
|
||||
from pydev_imports import _queue
|
||||
|
||||
@@ -246,22 +242,42 @@ def SetGlobalDebugger(dbg):
|
||||
#=======================================================================================================================
|
||||
# PyDBDaemonThread
|
||||
#=======================================================================================================================
|
||||
class PyDBDaemonThread(threading.Thread):
|
||||
class PyDBDaemonThread:
|
||||
|
||||
created_pydb_daemon_threads = {}
|
||||
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
# Note: subclasses are always daemon threads.
|
||||
self.killReceived = False
|
||||
self.dontTraceMe = True
|
||||
|
||||
def run(self):
|
||||
if sys.platform.startswith("java"):
|
||||
import org.python.core as PyCore #@UnresolvedImport
|
||||
ss = PyCore.PySystemState()
|
||||
# Note: Py.setSystemState() affects only the current thread.
|
||||
PyCore.Py.setSystemState(ss)
|
||||
def setName(self, name):
|
||||
self.name = name
|
||||
|
||||
self.OnRun()
|
||||
def start(self):
|
||||
import pydev_monkey
|
||||
start_new_thread = pydev_monkey.get_original_start_new_thread(_pydev_thread)
|
||||
start_new_thread(self.run, ())
|
||||
|
||||
def run(self):
|
||||
created_pydb_daemon = self.created_pydb_daemon_threads
|
||||
created_pydb_daemon[self] = 1
|
||||
dummy_thread = threading.currentThread()
|
||||
dummy_thread.is_pydev_daemon_thread = True
|
||||
try:
|
||||
try:
|
||||
if IS_JYTHON:
|
||||
import org.python.core as PyCore #@UnresolvedImport
|
||||
ss = PyCore.PySystemState()
|
||||
# Note: Py.setSystemState() affects only the current thread.
|
||||
PyCore.Py.setSystemState(ss)
|
||||
|
||||
self.OnRun()
|
||||
except:
|
||||
if sys is not None and traceback is not None:
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
del created_pydb_daemon[self]
|
||||
|
||||
def OnRun(self):
|
||||
raise NotImplementedError('Should be reimplemented by: %s' % self.__class__)
|
||||
@@ -272,7 +288,18 @@ class PyDBDaemonThread(threading.Thread):
|
||||
|
||||
def stopTrace(self):
|
||||
if self.dontTraceMe:
|
||||
pydevd_tracing.SetTrace(None) # no debugging on this thread
|
||||
|
||||
disable_tracing = True
|
||||
|
||||
if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON and sys.hexversion <= 0x020201f0:
|
||||
# don't run untraced threads if we're in jython 2.2.1 or lower
|
||||
# jython bug: if we start a thread and another thread changes the tracing facility
|
||||
# it affects other threads (it's not set only for the thread but globally)
|
||||
# Bug: http://sourceforge.net/tracker/index.php?func=detail&aid=1870039&group_id=12867&atid=112867
|
||||
disable_tracing = False
|
||||
|
||||
if disable_tracing:
|
||||
pydevd_tracing.SetTrace(None) # no debugging on this thread
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
@@ -355,7 +382,6 @@ class WriterThread(PyDBDaemonThread):
|
||||
""" writer thread writes out the commands in an infinite loop """
|
||||
def __init__(self, sock):
|
||||
PyDBDaemonThread.__init__(self)
|
||||
self.setDaemon(False) #writer isn't daemon to be able to deliver all messages after main thread terminated
|
||||
self.sock = sock
|
||||
self.setName("pydevd.Writer")
|
||||
self.cmdQueue = _queue.Queue()
|
||||
@@ -373,11 +399,16 @@ class WriterThread(PyDBDaemonThread):
|
||||
""" just loop and write responses """
|
||||
|
||||
self.stopTrace()
|
||||
get_has_timeout = sys.hexversion >= 0x02030000 # 2.3 onwards have it.
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
try:
|
||||
cmd = self.cmdQueue.get(1, 0.1)
|
||||
if get_has_timeout:
|
||||
cmd = self.cmdQueue.get(1, 0.1)
|
||||
else:
|
||||
time.sleep(.01)
|
||||
cmd = self.cmdQueue.get(0)
|
||||
except _queue.Empty:
|
||||
if self.killReceived:
|
||||
try:
|
||||
@@ -774,7 +805,7 @@ class ReloadCodeCommand(InternalThreadCommand):
|
||||
self.thread_id = thread_id
|
||||
self.module_name = module_name
|
||||
self.executed = False
|
||||
self.lock = threading.Lock()
|
||||
self.lock = _pydev_thread.allocate_lock()
|
||||
|
||||
|
||||
def canBeExecutedBy(self, thread_id):
|
||||
@@ -1155,8 +1186,8 @@ class InternalEvaluateConsoleExpression(InternalThreadCommand):
|
||||
from pydevd_console import ConsoleMessage
|
||||
console_message = ConsoleMessage()
|
||||
console_message.add_console_message(
|
||||
pydevd_console.CONSOLE_ERROR,
|
||||
"Select the valid frame in the debug view (thread: %s, frame: %s invalid)" % (self.thread_id, self.frame_id),
|
||||
pydevd_console.CONSOLE_ERROR,
|
||||
"Select the valid frame in the debug view (thread: %s, frame: %s invalid)" % (self.thread_id, self.frame_id),
|
||||
)
|
||||
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, console_message.toXML())
|
||||
except:
|
||||
|
||||
@@ -73,12 +73,8 @@ SUPPORT_GEVENT = os.getenv('GEVENT_SUPPORT', 'False') == 'True'
|
||||
|
||||
USE_LIB_COPY = SUPPORT_GEVENT and not IS_PY3K and sys.version_info[1] >= 6
|
||||
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
|
||||
_nextThreadIdLock = threading.Lock()
|
||||
from _pydev_imps import _pydev_thread
|
||||
_nextThreadIdLock = _pydev_thread.allocate_lock()
|
||||
|
||||
#=======================================================================================================================
|
||||
# Jython?
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from pydevd_constants import * #@UnusedWildImport
|
||||
from pydevd_file_utils import GetFilenameAndBase
|
||||
from _pydev_imps import _pydev_thread
|
||||
threadingCurrentThread = threading.currentThread
|
||||
|
||||
DEBUG = False
|
||||
@@ -13,7 +14,7 @@ class CustomFramesContainer:
|
||||
|
||||
def CustomFramesContainerInit(): #Note: no staticmethod on jython 2.1 (so, use free-function)
|
||||
|
||||
CustomFramesContainer.custom_frames_lock = threading.Lock()
|
||||
CustomFramesContainer.custom_frames_lock = _pydev_thread.allocate_lock()
|
||||
|
||||
# custom_frames can only be accessed if properly locked with custom_frames_lock!
|
||||
# Key is a string identifying the frame (as well as the thread it belongs to).
|
||||
|
||||
@@ -15,7 +15,11 @@ from pydevd_comm import CMD_ADD_DJANGO_EXCEPTION_BREAK, \
|
||||
CMD_STEP_INTO, CMD_SMART_STEP_INTO, CMD_RUN_TO_LINE, CMD_SET_NEXT_STATEMENT
|
||||
from pydevd_constants import * # @UnusedWildImport
|
||||
from pydevd_file_utils import GetFilenameAndBase
|
||||
from pydevd_signature import sendSignatureCallTrace
|
||||
try:
|
||||
from pydevd_signature import sendSignatureCallTrace
|
||||
except ImportError:
|
||||
def sendSignatureCallTrace(*args, **kwargs):
|
||||
pass
|
||||
import pydevd_vars
|
||||
import pydevd_dont_trace
|
||||
|
||||
|
||||
@@ -13,10 +13,13 @@ except:
|
||||
setattr(__builtin__, 'False', 0)
|
||||
|
||||
import pydevd_constants
|
||||
from pydevd_constants import DictIterItems, xrange, izip
|
||||
from pydevd_constants import DictIterItems, xrange
|
||||
|
||||
|
||||
MAX_ITEMS_TO_HANDLE = 500
|
||||
# Note: 300 is already a lot to see in the outline (after that the user should really use the shell to get things)
|
||||
# and this also means we'll pass less information to the client side (which makes debugging faster).
|
||||
MAX_ITEMS_TO_HANDLE = 300
|
||||
|
||||
TOO_LARGE_MSG = 'Too large to show contents. Max items to show: ' + str(MAX_ITEMS_TO_HANDLE)
|
||||
TOO_LARGE_ATTR = 'Unable to handle:'
|
||||
|
||||
@@ -272,19 +275,20 @@ class TupleResolver: #to enumerate tuples and lists
|
||||
return var[int(attribute)]
|
||||
|
||||
def getDictionary(self, var):
|
||||
#return dict( [ (i, x) for i, x in enumerate(var) ] )
|
||||
# modified 'cause jython does not have enumerate support
|
||||
l = len(var)
|
||||
d = {}
|
||||
|
||||
if l < MAX_ITEMS_TO_HANDLE:
|
||||
format = '%0' + str(int(len(str(l)))) + 'd'
|
||||
format_str = '%0' + str(int(len(str(l)))) + 'd'
|
||||
|
||||
|
||||
for i, item in izip(xrange(l), var):
|
||||
d[ format % i ] = item
|
||||
else:
|
||||
d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
|
||||
i = 0
|
||||
for item in var:
|
||||
d[format_str % i] = item
|
||||
i += 1
|
||||
|
||||
if i > MAX_ITEMS_TO_HANDLE:
|
||||
d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
|
||||
break
|
||||
|
||||
d['__len__'] = len(var)
|
||||
return d
|
||||
|
||||
@@ -381,13 +385,24 @@ class NdArrayResolver:
|
||||
This resolves a numpy ndarray returning some metadata about the NDArray
|
||||
'''
|
||||
|
||||
def is_numeric(self, obj):
|
||||
if not hasattr(obj, 'dtype'):
|
||||
return False
|
||||
return obj.dtype.kind in 'biufc'
|
||||
|
||||
def resolve(self, obj, attribute):
|
||||
if attribute == '__internals__':
|
||||
return defaultResolver.getDictionary(obj)
|
||||
if attribute == 'min':
|
||||
return obj.min()
|
||||
if self.is_numeric(obj):
|
||||
return obj.min()
|
||||
else:
|
||||
return None
|
||||
if attribute == 'max':
|
||||
return obj.max()
|
||||
if self.is_numeric(obj):
|
||||
return obj.max()
|
||||
else:
|
||||
return None
|
||||
if attribute == 'shape':
|
||||
return obj.shape
|
||||
if attribute == 'dtype':
|
||||
@@ -403,8 +418,12 @@ class NdArrayResolver:
|
||||
ret['min'] = 'ndarray too big, calculating min would slow down debugging'
|
||||
ret['max'] = 'ndarray too big, calculating max would slow down debugging'
|
||||
else:
|
||||
ret['min'] = obj.min()
|
||||
ret['max'] = obj.max()
|
||||
if self.is_numeric(obj):
|
||||
ret['min'] = obj.min()
|
||||
ret['max'] = obj.max()
|
||||
else:
|
||||
ret['min'] = 'not a numeric object'
|
||||
ret['max'] = 'not a numeric object'
|
||||
ret['shape'] = obj.shape
|
||||
ret['dtype'] = obj.dtype
|
||||
ret['size'] = obj.size
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import inspect
|
||||
import trace
|
||||
import os
|
||||
|
||||
trace._warn = lambda *args: None # workaround for http://bugs.python.org/issue17143 (PY-8706)
|
||||
try:
|
||||
import trace
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
trace._warn = lambda *args: None # workaround for http://bugs.python.org/issue17143 (PY-8706)
|
||||
|
||||
import gc
|
||||
from pydevd_comm import CMD_SIGNATURE_CALL_TRACE, NetCommand
|
||||
import pydevd_vars
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from pydevd_constants import * #@UnusedWildImport
|
||||
from _pydev_imps import _pydev_thread
|
||||
|
||||
try:
|
||||
import cStringIO as StringIO #may not always be available @UnusedImport
|
||||
@@ -8,10 +9,6 @@ except:
|
||||
except:
|
||||
import io as StringIO
|
||||
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
|
||||
import sys #@Reimport
|
||||
import traceback
|
||||
@@ -21,7 +18,7 @@ class TracingFunctionHolder:
|
||||
'''
|
||||
_original_tracing = None
|
||||
_warn = True
|
||||
_lock = threading.Lock()
|
||||
_lock = _pydev_thread.allocate_lock()
|
||||
_traceback_limit = 1
|
||||
_warnings_shown = {}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from types import * #@UnusedWildImport
|
||||
|
||||
from pydevd_custom_frames import getCustomFrame
|
||||
from pydevd_xml import *
|
||||
from _pydev_imps import _pydev_thread
|
||||
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
@@ -14,10 +15,7 @@ except ImportError:
|
||||
from io import StringIO
|
||||
import sys #@Reimport
|
||||
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
import _pydev_threading as threading
|
||||
import traceback
|
||||
import pydevd_save_locals
|
||||
from pydev_imports import Exec, execfile
|
||||
@@ -69,7 +67,7 @@ def dumpFrames(thread_id):
|
||||
# AdditionalFramesContainer
|
||||
#===============================================================================
|
||||
class AdditionalFramesContainer:
|
||||
lock = threading.Lock()
|
||||
lock = _pydev_thread.allocate_lock()
|
||||
additional_frames = {} #dict of dicts
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Django==1.6.5
|
||||
nose==1.3.3
|
||||
ipython==2.1.0
|
||||
numpy==1.8.2
|
||||
@@ -75,7 +75,7 @@ def main():
|
||||
|
||||
if test_framework == 0:
|
||||
|
||||
pydev_runfiles.main(configuration)
|
||||
return pydev_runfiles.main(configuration) #Note: still doesn't return a proper value.
|
||||
|
||||
else:
|
||||
#We'll convert the parameters to what nose or py.test expects.
|
||||
@@ -144,7 +144,8 @@ def main():
|
||||
import pydev_runfiles_nose
|
||||
PYDEV_NOSE_PLUGIN_SINGLETON = pydev_runfiles_nose.StartPydevNosePluginSingleton(configuration)
|
||||
argv.append('--with-pydevplugin')
|
||||
nose.run(argv=argv, addplugins=[PYDEV_NOSE_PLUGIN_SINGLETON])
|
||||
# Return 'not' because it will return 'success' (so, exit == 0 if success)
|
||||
return not nose.run(argv=argv, addplugins=[PYDEV_NOSE_PLUGIN_SINGLETON])
|
||||
|
||||
elif test_framework == PY_TEST_FRAMEWORK:
|
||||
if DEBUG:
|
||||
@@ -189,7 +190,7 @@ def main():
|
||||
|
||||
argv.append('-p')
|
||||
argv.append('pydev_runfiles_pytest2')
|
||||
pytest.main(argv)
|
||||
return pytest.main(argv)
|
||||
|
||||
else:
|
||||
raise AssertionError('Cannot handle test framework: %s at this point.' % (test_framework,))
|
||||
|
||||
+40
-26
@@ -1,19 +1,10 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
#Put pydevconsole in the path.
|
||||
sys.argv[0] = os.path.dirname(sys.argv[0])
|
||||
sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
|
||||
|
||||
print('Running tests with:', sys.executable)
|
||||
print('PYTHONPATH:')
|
||||
print('\n'.join(sorted(sys.path)))
|
||||
|
||||
import threading
|
||||
import unittest
|
||||
|
||||
import pydevconsole
|
||||
from pydev_imports import xmlrpclib, SimpleXMLRPCServer
|
||||
from pydev_localhost import get_localhost
|
||||
from pydev_ipython_console_011 import get_pydev_frontend
|
||||
|
||||
try:
|
||||
raw_input
|
||||
@@ -36,17 +27,28 @@ class Test(unittest.TestCase):
|
||||
def run(self):
|
||||
class HandleRequestInput:
|
||||
def RequestInput(self):
|
||||
client_thread.requested_input = True
|
||||
return 'RequestInput: OK'
|
||||
|
||||
def NotifyFinished(self, *args, **kwargs):
|
||||
client_thread.notified_finished += 1
|
||||
return 1
|
||||
|
||||
handle_request_input = HandleRequestInput()
|
||||
|
||||
import pydev_localhost
|
||||
print('Starting client with:', pydev_localhost.get_localhost(), self.client_port)
|
||||
client_server = SimpleXMLRPCServer((pydev_localhost.get_localhost(), self.client_port), logRequests=False)
|
||||
self.client_server = client_server = SimpleXMLRPCServer((pydev_localhost.get_localhost(), self.client_port), logRequests=False)
|
||||
client_server.register_function(handle_request_input.RequestInput)
|
||||
client_server.register_function(handle_request_input.NotifyFinished)
|
||||
client_server.serve_forever()
|
||||
|
||||
|
||||
def shutdown(self):
|
||||
return
|
||||
self.client_server.shutdown()
|
||||
|
||||
client_thread = ClientThread(client_port)
|
||||
client_thread.requested_input = False
|
||||
client_thread.notified_finished = 0
|
||||
client_thread.setDaemon(True)
|
||||
client_thread.start()
|
||||
return client_thread
|
||||
@@ -67,6 +69,9 @@ class Test(unittest.TestCase):
|
||||
|
||||
|
||||
def testServer(self):
|
||||
# Just making sure that the singleton is created in this thread.
|
||||
get_pydev_frontend(get_localhost(), 0)
|
||||
|
||||
client_port, server_port = self.getFreeAddresses()
|
||||
class ServerThread(threading.Thread):
|
||||
def __init__(self, client_port, server_port):
|
||||
@@ -84,18 +89,27 @@ class Test(unittest.TestCase):
|
||||
|
||||
client_thread = self.startClientThread(client_port) #@UnusedVariable
|
||||
|
||||
import time
|
||||
time.sleep(.3) #let's give it some time to start the threads
|
||||
|
||||
import pydev_localhost
|
||||
server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), server_port))
|
||||
server.addExec("import sys; print('Running with: %s %s' % (sys.executable or sys.platform, sys.version))")
|
||||
server.addExec('class Foo:')
|
||||
server.addExec(' pass')
|
||||
server.addExec('')
|
||||
server.addExec('foo = Foo()')
|
||||
server.addExec('a = %s()' % raw_input_name)
|
||||
server.addExec('print (a)')
|
||||
try:
|
||||
import time
|
||||
time.sleep(.3) #let's give it some time to start the threads
|
||||
|
||||
import pydev_localhost
|
||||
server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), server_port))
|
||||
server.execLine("import sys; print('Running with: %s %s' % (sys.executable or sys.platform, sys.version))")
|
||||
server.execLine('class Foo:')
|
||||
server.execLine(' pass')
|
||||
server.execLine('')
|
||||
server.execLine('foo = Foo()')
|
||||
server.execLine('a = %s()' % raw_input_name)
|
||||
initial = time.time()
|
||||
while not client_thread.requested_input:
|
||||
if time.time() - initial > 2:
|
||||
raise AssertionError('Did not get the return asked before the timeout.')
|
||||
time.sleep(.1)
|
||||
frame_xml = server.getFrame()
|
||||
self.assert_('RequestInput' in frame_xml, 'Did not fid RequestInput in:\n%s' % (frame_xml,))
|
||||
finally:
|
||||
client_thread.shutdown()
|
||||
|
||||
#=======================================================================================================================
|
||||
# main
|
||||
@@ -1,21 +1,7 @@
|
||||
import os.path
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
IS_JYTHON = sys.platform.find('java') != -1
|
||||
|
||||
try:
|
||||
this_file_name = __file__
|
||||
except NameError:
|
||||
# stupid jython. plain old __file__ isnt working for some reason
|
||||
import test_runfiles #@UnresolvedImport - importing the module itself
|
||||
this_file_name = test_runfiles.__file__
|
||||
|
||||
|
||||
desired_runfiles_path = os.path.normpath(os.path.dirname(this_file_name) + "/..")
|
||||
sys.path.insert(0, desired_runfiles_path)
|
||||
|
||||
import unittest
|
||||
import pydevd_referrers
|
||||
from pydev_imports import StringIO
|
||||
|
||||
@@ -36,10 +36,15 @@ class Test(unittest.TestCase):
|
||||
unittest.TestCase.tearDown(self)
|
||||
|
||||
def testIt(self):
|
||||
if not IS_JYTHON:
|
||||
return
|
||||
dbg('ok')
|
||||
|
||||
def testMessage(self):
|
||||
if not IS_JYTHON:
|
||||
return
|
||||
t = jycompletionserver.T(0)
|
||||
t.exit_process_on_kill = False
|
||||
|
||||
l = []
|
||||
l.append(('Def', 'description' , 'args'))
|
||||
@@ -65,6 +70,8 @@ class Test(unittest.TestCase):
|
||||
|
||||
|
||||
def testCompletionSocketsAndMessages(self):
|
||||
if not IS_JYTHON:
|
||||
return
|
||||
dbg('testCompletionSocketsAndMessages')
|
||||
t, socket = self.createConnections()
|
||||
self.socket = socket
|
||||
@@ -121,6 +128,7 @@ class Test(unittest.TestCase):
|
||||
Creates the connections needed for testing.
|
||||
'''
|
||||
t = jycompletionserver.T(p1)
|
||||
t.exit_process_on_kill = False
|
||||
|
||||
t.start()
|
||||
|
||||
|
||||
@@ -4,17 +4,14 @@
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
#make it as if we were executing from the directory above this one (so that we can use pycompletionserver
|
||||
#without the need for it being in the pythonpath)
|
||||
sys.argv[0] = os.path.dirname(sys.argv[0])
|
||||
#twice the dirname to get the previous level from this file.
|
||||
sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
|
||||
|
||||
#this does not work (they must be in the system pythonpath)
|
||||
#sys.path.insert(1, r"D:\bin\eclipse321\plugins\org.junit_3.8.1\junit.jar" ) #some late loading jar tests
|
||||
#sys.path.insert(1, r"D:\bin\eclipse331_1\plugins\org.apache.ant_1.7.0.v200706080842\lib\ant.jar" ) #some late loading jar tests
|
||||
|
||||
IS_JYTHON = 0
|
||||
if sys.platform.find('java') != -1:
|
||||
IS_JYTHON = 1
|
||||
from _pydev_jy_imports_tipper import ismethod
|
||||
from _pydev_jy_imports_tipper import isclass
|
||||
from _pydev_jy_imports_tipper import dirObj
|
||||
@@ -234,22 +231,24 @@ class TestCompl(unittest.TestCase):
|
||||
assert isMet[1][0].basicAsStr() == "function:met2 args=['arg1', 'arg2'], varargs=vararg, kwargs=kwarg, docs:docmet2"
|
||||
assert not isclass(met2)
|
||||
|
||||
|
||||
if not IS_JYTHON:
|
||||
# Disable tests if not running under Jython
|
||||
class TestMod(unittest.TestCase):
|
||||
pass
|
||||
class TestCompl(TestMod):
|
||||
pass
|
||||
class TestSearch(TestMod):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.platform.find('java') != -1:
|
||||
#Only run if jython
|
||||
suite = unittest.makeSuite(TestCompl)
|
||||
suite2 = unittest.makeSuite(TestMod)
|
||||
suite3 = unittest.makeSuite(TestSearch)
|
||||
#Only run if jython
|
||||
suite = unittest.makeSuite(TestCompl)
|
||||
suite2 = unittest.makeSuite(TestMod)
|
||||
suite3 = unittest.makeSuite(TestSearch)
|
||||
|
||||
unittest.TextTestRunner(verbosity=1).run(suite)
|
||||
unittest.TextTestRunner(verbosity=1).run(suite2)
|
||||
unittest.TextTestRunner(verbosity=1).run(suite3)
|
||||
|
||||
unittest.TextTestRunner(verbosity=1).run(suite)
|
||||
unittest.TextTestRunner(verbosity=1).run(suite2)
|
||||
unittest.TextTestRunner(verbosity=1).run(suite3)
|
||||
|
||||
# suite.addTest(Test('testCase12'))
|
||||
# suite = unittest.TestSuite()
|
||||
# unittest.TextTestRunner(verbosity=1).run(suite)
|
||||
|
||||
else:
|
||||
sys.stdout.write('Not running jython tests for non-java platform: %s' % sys.platform)
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
#TODO: This test no longer works (check if it should be fixed or removed altogether).
|
||||
|
||||
#import unittest
|
||||
#import sys
|
||||
#import os
|
||||
##make it as if we were executing from the directory above this one
|
||||
#sys.argv[0] = os.path.dirname(sys.argv[0])
|
||||
##twice the dirname to get the previous level from this file.
|
||||
#sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
|
||||
#
|
||||
#from pydev_localhost import get_localhost
|
||||
#
|
||||
#
|
||||
#IS_JYTHON = sys.platform.find('java') != -1
|
||||
#
|
||||
##=======================================================================================================================
|
||||
## TestCase
|
||||
##=======================================================================================================================
|
||||
#class TestCase(unittest.TestCase):
|
||||
#
|
||||
# def setUp(self):
|
||||
# unittest.TestCase.setUp(self)
|
||||
#
|
||||
# def tearDown(self):
|
||||
# unittest.TestCase.tearDown(self)
|
||||
#
|
||||
# def testIPython(self):
|
||||
# try:
|
||||
# from pydev_ipython_console import PyDevFrontEnd
|
||||
# except:
|
||||
# if IS_JYTHON:
|
||||
# return
|
||||
# front_end = PyDevFrontEnd(get_localhost(), 0)
|
||||
#
|
||||
# front_end.input_buffer = 'if True:'
|
||||
# self.assert_(not front_end._on_enter())
|
||||
#
|
||||
# front_end.input_buffer = 'if True:\n' + \
|
||||
# front_end.continuation_prompt() + ' a = 10\n'
|
||||
# self.assert_(not front_end._on_enter())
|
||||
#
|
||||
#
|
||||
# front_end.input_buffer = 'if True:\n' + \
|
||||
# front_end.continuation_prompt() + ' a = 10\n\n'
|
||||
# self.assert_(front_end._on_enter())
|
||||
#
|
||||
#
|
||||
## front_end.input_buffer = ' print a'
|
||||
## self.assert_(not front_end._on_enter())
|
||||
## front_end.input_buffer = ''
|
||||
## self.assert_(front_end._on_enter())
|
||||
#
|
||||
#
|
||||
## front_end.input_buffer = 'a.'
|
||||
## front_end.complete_current_input()
|
||||
## front_end.input_buffer = 'if True:'
|
||||
## front_end._on_enter()
|
||||
# front_end.input_buffer = 'a = 30'
|
||||
# front_end._on_enter()
|
||||
# front_end.input_buffer = 'print a'
|
||||
# front_end._on_enter()
|
||||
# front_end.input_buffer = 'a?'
|
||||
# front_end._on_enter()
|
||||
# print front_end.complete('%')
|
||||
# print front_end.complete('%e')
|
||||
# print front_end.complete('cd c:/t')
|
||||
# print front_end.complete('cd c:/temp/')
|
||||
## front_end.input_buffer = 'print raw_input("press enter\\n")'
|
||||
## front_end._on_enter()
|
||||
##
|
||||
#
|
||||
##=======================================================================================================================
|
||||
## main
|
||||
##=======================================================================================================================
|
||||
#if __name__ == '__main__':
|
||||
# if sys.platform.find('java') == -1:
|
||||
# #IPython not available for Jython
|
||||
# unittest.main()
|
||||
# else:
|
||||
# print('not supported on Jython')
|
||||
@@ -7,81 +7,87 @@ from pydev_imports import StringIO, SimpleXMLRPCServer
|
||||
from pydev_localhost import get_localhost
|
||||
from pydev_console_utils import StdIn
|
||||
import socket
|
||||
from pydev_ipython_console_011 import get_pydev_frontend
|
||||
import time
|
||||
|
||||
# make it as if we were executing from the directory above this one
|
||||
sys.argv[0] = os.path.dirname(sys.argv[0])
|
||||
# twice the dirname to get the previous level from this file.
|
||||
sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
|
||||
try:
|
||||
xrange
|
||||
except:
|
||||
xrange = range
|
||||
|
||||
# PyDevFrontEnd depends on singleton in IPython, so you
|
||||
# can't make multiple versions. So we reuse front_end for
|
||||
# all the tests
|
||||
|
||||
orig_stdout = sys.stdout
|
||||
orig_stderr = sys.stderr
|
||||
|
||||
stdout = sys.stdout = StringIO()
|
||||
stderr = sys.stderr = StringIO()
|
||||
|
||||
from pydev_ipython_console_011 import PyDevFrontEnd
|
||||
s = socket.socket()
|
||||
s.bind(('', 0))
|
||||
client_port = s.getsockname()[1]
|
||||
s.close()
|
||||
front_end = PyDevFrontEnd(get_localhost(), client_port)
|
||||
|
||||
|
||||
def addExec(code, expected_more=False):
|
||||
more = front_end.addExec(code)
|
||||
eq_(expected_more, more)
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
front_end.input_splitter.reset()
|
||||
stdout.truncate(0)
|
||||
stdout.seek(0)
|
||||
stderr.truncate(0)
|
||||
stderr.seek(0)
|
||||
# PyDevFrontEnd depends on singleton in IPython, so you
|
||||
# can't make multiple versions. So we reuse self.front_end for
|
||||
# all the tests
|
||||
self.front_end = get_pydev_frontend(get_localhost(), 0)
|
||||
|
||||
from pydev_ipython.inputhook import set_return_control_callback
|
||||
set_return_control_callback(lambda:True)
|
||||
self.front_end.clearBuffer()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def addExec(self, code, expected_more=False):
|
||||
more = self.front_end.addExec(code)
|
||||
eq_(expected_more, more)
|
||||
|
||||
def redirectStdout(self):
|
||||
from IPython.utils import io
|
||||
|
||||
self.original_stdout = sys.stdout
|
||||
sys.stdout = io.stdout = StringIO()
|
||||
|
||||
def restoreStdout(self):
|
||||
from IPython.utils import io
|
||||
io.stdout = sys.stdout = self.original_stdout
|
||||
|
||||
|
||||
class TestPyDevFrontEnd(TestBase):
|
||||
|
||||
def testAddExec_1(self):
|
||||
addExec('if True:', True)
|
||||
self.addExec('if True:', True)
|
||||
|
||||
def testAddExec_2(self):
|
||||
addExec('if True:\n testAddExec_a = 10\n', True)
|
||||
#Change: 'more' must now be controlled in the client side after the initial 'True' returned.
|
||||
self.addExec('if True:\n testAddExec_a = 10\n', False)
|
||||
assert 'testAddExec_a' in self.front_end.getNamespace()
|
||||
|
||||
def testAddExec_3(self):
|
||||
assert 'testAddExec_a' not in front_end.getNamespace()
|
||||
addExec('if True:\n testAddExec_a = 10\n\n')
|
||||
assert 'testAddExec_a' in front_end.getNamespace()
|
||||
eq_(front_end.getNamespace()['testAddExec_a'], 10)
|
||||
assert 'testAddExec_x' not in self.front_end.getNamespace()
|
||||
self.addExec('if True:\n testAddExec_x = 10\n\n')
|
||||
assert 'testAddExec_x' in self.front_end.getNamespace()
|
||||
eq_(self.front_end.getNamespace()['testAddExec_x'], 10)
|
||||
|
||||
def testGetNamespace(self):
|
||||
assert 'testGetNamespace_a' not in front_end.getNamespace()
|
||||
addExec('testGetNamespace_a = 10')
|
||||
assert 'testGetNamespace_a' in front_end.getNamespace()
|
||||
eq_(front_end.getNamespace()['testGetNamespace_a'], 10)
|
||||
assert 'testGetNamespace_a' not in self.front_end.getNamespace()
|
||||
self.addExec('testGetNamespace_a = 10')
|
||||
assert 'testGetNamespace_a' in self.front_end.getNamespace()
|
||||
eq_(self.front_end.getNamespace()['testGetNamespace_a'], 10)
|
||||
|
||||
def testComplete(self):
|
||||
unused_text, matches = front_end.complete('%')
|
||||
unused_text, matches = self.front_end.complete('%')
|
||||
assert len(matches) > 1, 'at least one magic should appear in completions'
|
||||
|
||||
def testCompleteDoesNotDoPythonMatches(self):
|
||||
# Test that IPython's completions do not do the things that
|
||||
# PyDev's completions will handle
|
||||
addExec('testComplete_a = 5')
|
||||
addExec('testComplete_b = 10')
|
||||
addExec('testComplete_c = 15')
|
||||
unused_text, matches = front_end.complete('testComplete_')
|
||||
self.addExec('testComplete_a = 5')
|
||||
self.addExec('testComplete_b = 10')
|
||||
self.addExec('testComplete_c = 15')
|
||||
unused_text, matches = self.front_end.complete('testComplete_')
|
||||
assert len(matches) == 0
|
||||
|
||||
def testGetCompletions_1(self):
|
||||
# Test the merged completions include the standard completions
|
||||
addExec('testComplete_a = 5')
|
||||
addExec('testComplete_b = 10')
|
||||
addExec('testComplete_c = 15')
|
||||
res = front_end.getCompletions('testComplete_', 'testComplete_')
|
||||
self.addExec('testComplete_a = 5')
|
||||
self.addExec('testComplete_b = 10')
|
||||
self.addExec('testComplete_c = 15')
|
||||
res = self.front_end.getCompletions('testComplete_', 'testComplete_')
|
||||
matches = [f[0] for f in res]
|
||||
assert len(matches) == 3
|
||||
eq_(set(['testComplete_a', 'testComplete_b', 'testComplete_c']), set(matches))
|
||||
@@ -90,60 +96,80 @@ class TestPyDevFrontEnd(TestBase):
|
||||
# Test that we get IPython completions in results
|
||||
# we do this by checking kw completion which PyDev does
|
||||
# not do by default
|
||||
addExec('def ccc(ABC=123): pass')
|
||||
res = front_end.getCompletions('ccc(', '')
|
||||
self.addExec('def ccc(ABC=123): pass')
|
||||
res = self.front_end.getCompletions('ccc(', '')
|
||||
matches = [f[0] for f in res]
|
||||
assert 'ABC=' in matches
|
||||
|
||||
def testGetCompletions_3(self):
|
||||
# Test that magics return IPYTHON magic as type
|
||||
res = front_end.getCompletions('%cd', '%cd')
|
||||
res = self.front_end.getCompletions('%cd', '%cd')
|
||||
assert len(res) == 1
|
||||
eq_(res[0][3], '12') # '12' == IToken.TYPE_IPYTHON_MAGIC
|
||||
assert len(res[0][1]) > 100, 'docstring for %cd should be a reasonably long string'
|
||||
|
||||
class TestRunningCode(TestBase):
|
||||
def testPrint(self):
|
||||
addExec('print("output")')
|
||||
eq_(stdout.getvalue(), 'output\n')
|
||||
self.redirectStdout()
|
||||
try:
|
||||
self.addExec('print("output")')
|
||||
eq_(sys.stdout.getvalue(), 'output\n')
|
||||
finally:
|
||||
self.restoreStdout()
|
||||
|
||||
def testQuestionMark_1(self):
|
||||
addExec('?')
|
||||
assert len(stdout.getvalue()) > 1000, 'IPython help should be pretty big'
|
||||
self.redirectStdout()
|
||||
try:
|
||||
self.addExec('?')
|
||||
assert len(sys.stdout.getvalue()) > 1000, 'IPython help should be pretty big'
|
||||
finally:
|
||||
self.restoreStdout()
|
||||
|
||||
def testQuestionMark_2(self):
|
||||
addExec('int?')
|
||||
assert stdout.getvalue().find('Convert') != -1
|
||||
self.redirectStdout()
|
||||
try:
|
||||
self.addExec('int?')
|
||||
assert sys.stdout.getvalue().find('Convert') != -1
|
||||
finally:
|
||||
self.restoreStdout()
|
||||
|
||||
|
||||
def testGui(self):
|
||||
from pydev_ipython.inputhook import get_inputhook, set_stdin_file
|
||||
set_stdin_file(sys.stdin)
|
||||
assert get_inputhook() is None
|
||||
addExec('%gui tk')
|
||||
# we can't test the GUI works here because we aren't connected to XML-RPC so
|
||||
# nowhere for hook to run
|
||||
assert get_inputhook() is not None
|
||||
addExec('%gui none')
|
||||
assert get_inputhook() is None
|
||||
try:
|
||||
import Tkinter
|
||||
except:
|
||||
return
|
||||
else:
|
||||
from pydev_ipython.inputhook import get_inputhook
|
||||
assert get_inputhook() is None
|
||||
self.addExec('%gui tk')
|
||||
# we can't test the GUI works here because we aren't connected to XML-RPC so
|
||||
# nowhere for hook to run
|
||||
assert get_inputhook() is not None
|
||||
self.addExec('%gui none')
|
||||
assert get_inputhook() is None
|
||||
|
||||
def testHistory(self):
|
||||
''' Make sure commands are added to IPython's history '''
|
||||
addExec('a=1')
|
||||
addExec('b=2')
|
||||
_ih = front_end.getNamespace()['_ih']
|
||||
eq_(_ih[-1], 'b=2')
|
||||
eq_(_ih[-2], 'a=1')
|
||||
|
||||
addExec('history')
|
||||
hist = stdout.getvalue().split('\n')
|
||||
eq_(hist[-1], '')
|
||||
eq_(hist[-2], 'history')
|
||||
eq_(hist[-3], 'b=2')
|
||||
eq_(hist[-4], 'a=1')
|
||||
self.redirectStdout()
|
||||
try:
|
||||
self.addExec('a=1')
|
||||
self.addExec('b=2')
|
||||
_ih = self.front_end.getNamespace()['_ih']
|
||||
eq_(_ih[-1], 'b=2')
|
||||
eq_(_ih[-2], 'a=1')
|
||||
|
||||
self.addExec('history')
|
||||
hist = sys.stdout.getvalue().split('\n')
|
||||
eq_(hist[-1], '')
|
||||
eq_(hist[-2], 'history')
|
||||
eq_(hist[-3], 'b=2')
|
||||
eq_(hist[-4], 'a=1')
|
||||
finally:
|
||||
self.restoreStdout()
|
||||
|
||||
def testEdit(self):
|
||||
''' Make sure we can issue an edit command '''
|
||||
''' Make sure we can issue an edit command'''
|
||||
called_RequestInput = [False]
|
||||
called_IPythonEditor = [False]
|
||||
def startClientThread(client_port):
|
||||
@@ -163,26 +189,47 @@ class TestRunningCode(TestBase):
|
||||
handle_request_input = HandleRequestInput()
|
||||
|
||||
import pydev_localhost
|
||||
client_server = SimpleXMLRPCServer((pydev_localhost.get_localhost(), self.client_port), logRequests=False)
|
||||
self.client_server = client_server = SimpleXMLRPCServer(
|
||||
(pydev_localhost.get_localhost(), self.client_port), logRequests=False)
|
||||
client_server.register_function(handle_request_input.RequestInput)
|
||||
client_server.register_function(handle_request_input.IPythonEditor)
|
||||
client_server.serve_forever()
|
||||
|
||||
def shutdown(self):
|
||||
return
|
||||
self.client_server.shutdown()
|
||||
|
||||
client_thread = ClientThread(client_port)
|
||||
client_thread.setDaemon(True)
|
||||
client_thread.start()
|
||||
return client_thread
|
||||
|
||||
startClientThread(client_port)
|
||||
# PyDevFrontEnd depends on singleton in IPython, so you
|
||||
# can't make multiple versions. So we reuse self.front_end for
|
||||
# all the tests
|
||||
s = socket.socket()
|
||||
s.bind(('', 0))
|
||||
self.client_port = client_port = s.getsockname()[1]
|
||||
s.close()
|
||||
self.front_end = get_pydev_frontend(get_localhost(), client_port)
|
||||
|
||||
client_thread = startClientThread(self.client_port)
|
||||
orig_stdin = sys.stdin
|
||||
sys.stdin = StdIn(self, get_localhost(), client_port)
|
||||
sys.stdin = StdIn(self, get_localhost(), self.client_port)
|
||||
try:
|
||||
filename = 'made_up_file.py'
|
||||
addExec('%edit ' + filename)
|
||||
eq_(called_IPythonEditor[0], (os.path.abspath(filename), 0))
|
||||
self.addExec('%edit ' + filename)
|
||||
|
||||
for i in xrange(10):
|
||||
if called_IPythonEditor[0] == (os.path.abspath(filename), '0'):
|
||||
break
|
||||
time.sleep(.1)
|
||||
|
||||
eq_(called_IPythonEditor[0], (os.path.abspath(filename), '0'))
|
||||
assert called_RequestInput[0], "Make sure the 'wait' parameter has been respected"
|
||||
finally:
|
||||
sys.stdin = orig_stdin
|
||||
client_thread.shutdown()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import threading
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.argv[0] = os.path.dirname(sys.argv[0])
|
||||
sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
|
||||
import pydevconsole
|
||||
from pydev_imports import xmlrpclib, SimpleXMLRPCServer, StringIO
|
||||
|
||||
@@ -19,104 +15,112 @@ except NameError:
|
||||
#=======================================================================================================================
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
def testConsoleHello(self):
|
||||
self.original_stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
ret = sys.stdout #@UnusedVariable
|
||||
sys.stdout = self.original_stdout
|
||||
#print_ ret.getvalue() -- use to see test output
|
||||
|
||||
def testConsoleHello(self):
|
||||
client_port, _server_port = self.getFreeAddresses()
|
||||
client_thread = self.startClientThread(client_port) #@UnusedVariable
|
||||
import time
|
||||
time.sleep(.3) #let's give it some time to start the threads
|
||||
|
||||
import pydev_localhost
|
||||
interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, server=None)
|
||||
|
||||
(result,) = interpreter.hello("Hello pydevconsole")
|
||||
self.assertEqual(result, "Hello eclipse")
|
||||
|
||||
try:
|
||||
client_port, _server_port = self.getFreeAddresses()
|
||||
client_thread = self.startClientThread(client_port) #@UnusedVariable
|
||||
import time
|
||||
time.sleep(.3) #let's give it some time to start the threads
|
||||
|
||||
import pydev_localhost
|
||||
interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, threading.currentThread())
|
||||
|
||||
(result,) = interpreter.hello("Hello pydevconsole")
|
||||
self.assertEqual(result, "Hello eclipse")
|
||||
finally:
|
||||
sys.stdout = self.original_stdout
|
||||
|
||||
|
||||
def testConsoleRequests(self):
|
||||
client_port, _server_port = self.getFreeAddresses()
|
||||
client_thread = self.startClientThread(client_port) #@UnusedVariable
|
||||
import time
|
||||
time.sleep(.3) #let's give it some time to start the threads
|
||||
|
||||
import pydev_localhost
|
||||
interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, server=None)
|
||||
interpreter.addExec('class Foo:')
|
||||
interpreter.addExec(' CONSTANT=1')
|
||||
interpreter.addExec('')
|
||||
interpreter.addExec('foo=Foo()')
|
||||
interpreter.addExec('foo.__doc__=None')
|
||||
interpreter.addExec('val = %s()' % (raw_input_name,))
|
||||
interpreter.addExec('50')
|
||||
interpreter.addExec('print (val)')
|
||||
found = sys.stdout.getvalue().split()
|
||||
self.original_stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
|
||||
try:
|
||||
self.assertEqual(['50', 'input_request'], found)
|
||||
except:
|
||||
self.assertEqual(['input_request'], found) #IPython
|
||||
|
||||
comps = interpreter.getCompletions('foo.', 'foo.')
|
||||
self.assert_(
|
||||
('CONSTANT', '', '', '3') in comps or ('CONSTANT', '', '', '4') in comps, \
|
||||
'Found: %s' % comps
|
||||
)
|
||||
|
||||
comps = interpreter.getCompletions('"".', '"".')
|
||||
self.assert_(
|
||||
('__add__', 'x.__add__(y) <==> x+y', '', '3') in comps or
|
||||
('__add__', '', '', '4') in comps or
|
||||
('__add__', 'x.__add__(y) <==> x+y\r\nx.__add__(y) <==> x+y', '()', '2') in comps or
|
||||
('__add__', 'x.\n__add__(y) <==> x+yx.\n__add__(y) <==> x+y', '()', '2'),
|
||||
'Did not find __add__ in : %s' % (comps,)
|
||||
)
|
||||
|
||||
|
||||
completions = interpreter.getCompletions('', '')
|
||||
for c in completions:
|
||||
if c[0] == 'AssertionError':
|
||||
break
|
||||
else:
|
||||
self.fail('Could not find AssertionError')
|
||||
|
||||
completions = interpreter.getCompletions('Assert', 'Assert')
|
||||
for c in completions:
|
||||
if c[0] == 'RuntimeError':
|
||||
self.fail('Did not expect to find RuntimeError there')
|
||||
|
||||
self.assert_(('__doc__', None, '', '3') not in interpreter.getCompletions('foo.CO', 'foo.'))
|
||||
|
||||
comps = interpreter.getCompletions('va', 'va')
|
||||
self.assert_(('val', '', '', '3') in comps or ('val', '', '', '4') in comps)
|
||||
|
||||
interpreter.addExec('s = "mystring"')
|
||||
|
||||
desc = interpreter.getDescription('val')
|
||||
self.assert_(desc.find('str(object) -> string') >= 0 or
|
||||
desc == "'input_request'" or
|
||||
desc.find('str(string[, encoding[, errors]]) -> str') >= 0 or
|
||||
desc.find('str(Char* value)') >= 0 or
|
||||
desc.find('str(value: Char*)') >= 0,
|
||||
'Could not find what was needed in %s' % desc)
|
||||
|
||||
desc = interpreter.getDescription('val.join')
|
||||
self.assert_(desc.find('S.join(sequence) -> string') >= 0 or
|
||||
desc.find('S.join(sequence) -> str') >= 0 or
|
||||
desc.find('S.join(iterable) -> string') >= 0 or
|
||||
desc == "<builtin method 'join'>" or
|
||||
desc == "<built-in method join of str object>" or
|
||||
desc.find('str join(str self, list sequence)') >= 0 or
|
||||
desc.find('S.join(iterable) -> str') >= 0 or
|
||||
desc.find('join(self: str, sequence: list) -> str') >= 0,
|
||||
"Could not recognize: %s" % (desc,))
|
||||
client_port, _server_port = self.getFreeAddresses()
|
||||
client_thread = self.startClientThread(client_port) #@UnusedVariable
|
||||
import time
|
||||
time.sleep(.3) #let's give it some time to start the threads
|
||||
|
||||
import pydev_localhost
|
||||
from pydev_console_utils import CodeFragment
|
||||
|
||||
interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, threading.currentThread())
|
||||
sys.stdout = StringIO()
|
||||
interpreter.addExec(CodeFragment('class Foo:'))
|
||||
interpreter.addExec(CodeFragment(' CONSTANT=1'))
|
||||
interpreter.addExec(CodeFragment(''))
|
||||
interpreter.addExec(CodeFragment('foo=Foo()'))
|
||||
interpreter.addExec(CodeFragment('foo.__doc__=None'))
|
||||
interpreter.addExec(CodeFragment('val = %s()' % (raw_input_name,)))
|
||||
interpreter.addExec(CodeFragment('50'))
|
||||
interpreter.addExec(CodeFragment('print (val)'))
|
||||
found = sys.stdout.getvalue().split()
|
||||
try:
|
||||
self.assertEqual(['50', 'input_request'], found)
|
||||
except:
|
||||
self.assertEqual(['input_request'], found) #IPython
|
||||
|
||||
comps = interpreter.getCompletions('foo.', 'foo.')
|
||||
self.assert_(
|
||||
('CONSTANT', '', '', '3') in comps or ('CONSTANT', '', '', '4') in comps, \
|
||||
'Found: %s' % comps
|
||||
)
|
||||
|
||||
comps = interpreter.getCompletions('"".', '"".')
|
||||
self.assert_(
|
||||
('__add__', 'x.__add__(y) <==> x+y', '', '3') in comps or
|
||||
('__add__', '', '', '4') in comps or
|
||||
('__add__', 'x.__add__(y) <==> x+y\r\nx.__add__(y) <==> x+y', '()', '2') in comps or
|
||||
('__add__', 'x.\n__add__(y) <==> x+yx.\n__add__(y) <==> x+y', '()', '2'),
|
||||
'Did not find __add__ in : %s' % (comps,)
|
||||
)
|
||||
|
||||
|
||||
completions = interpreter.getCompletions('', '')
|
||||
for c in completions:
|
||||
if c[0] == 'AssertionError':
|
||||
break
|
||||
else:
|
||||
self.fail('Could not find AssertionError')
|
||||
|
||||
completions = interpreter.getCompletions('Assert', 'Assert')
|
||||
for c in completions:
|
||||
if c[0] == 'RuntimeError':
|
||||
self.fail('Did not expect to find RuntimeError there')
|
||||
|
||||
self.assert_(('__doc__', None, '', '3') not in interpreter.getCompletions('foo.CO', 'foo.'))
|
||||
|
||||
comps = interpreter.getCompletions('va', 'va')
|
||||
self.assert_(('val', '', '', '3') in comps or ('val', '', '', '4') in comps)
|
||||
|
||||
interpreter.addExec(CodeFragment('s = "mystring"'))
|
||||
|
||||
desc = interpreter.getDescription('val')
|
||||
self.assert_(desc.find('str(object) -> string') >= 0 or
|
||||
desc == "'input_request'" or
|
||||
desc.find('str(string[, encoding[, errors]]) -> str') >= 0 or
|
||||
desc.find('str(Char* value)') >= 0 or
|
||||
desc.find('str(object=\'\') -> string') >= 0 or
|
||||
desc.find('str(value: Char*)') >= 0 or
|
||||
desc.find('str(object=\'\') -> str') >= 0
|
||||
,
|
||||
'Could not find what was needed in %s' % desc)
|
||||
|
||||
desc = interpreter.getDescription('val.join')
|
||||
self.assert_(desc.find('S.join(sequence) -> string') >= 0 or
|
||||
desc.find('S.join(sequence) -> str') >= 0 or
|
||||
desc.find('S.join(iterable) -> string') >= 0 or
|
||||
desc == "<builtin method 'join'>" or
|
||||
desc == "<built-in method join of str object>" or
|
||||
desc.find('str join(str self, list sequence)') >= 0 or
|
||||
desc.find('S.join(iterable) -> str') >= 0 or
|
||||
desc.find('join(self: str, sequence: list) -> str') >= 0,
|
||||
"Could not recognize: %s" % (desc,))
|
||||
finally:
|
||||
sys.stdout = self.original_stdout
|
||||
|
||||
|
||||
def startClientThread(self, client_port):
|
||||
@@ -124,19 +128,28 @@ class Test(unittest.TestCase):
|
||||
def __init__(self, client_port):
|
||||
threading.Thread.__init__(self)
|
||||
self.client_port = client_port
|
||||
|
||||
def run(self):
|
||||
class HandleRequestInput:
|
||||
def RequestInput(self):
|
||||
client_thread.requested_input = True
|
||||
return 'input_request'
|
||||
|
||||
|
||||
def NotifyFinished(self, *args, **kwargs):
|
||||
client_thread.notified_finished += 1
|
||||
return 1
|
||||
|
||||
handle_request_input = HandleRequestInput()
|
||||
|
||||
|
||||
import pydev_localhost
|
||||
client_server = SimpleXMLRPCServer((pydev_localhost.get_localhost(), self.client_port), logRequests=False)
|
||||
client_server.register_function(handle_request_input.RequestInput)
|
||||
client_server.register_function(handle_request_input.NotifyFinished)
|
||||
client_server.serve_forever()
|
||||
|
||||
|
||||
client_thread = ClientThread(client_port)
|
||||
client_thread.requested_input = False
|
||||
client_thread.notified_finished = 0
|
||||
client_thread.setDaemon(True)
|
||||
client_thread.start()
|
||||
return client_thread
|
||||
@@ -194,34 +207,50 @@ class Test(unittest.TestCase):
|
||||
|
||||
|
||||
def testServer(self):
|
||||
client_port, server_port = self.getFreeAddresses()
|
||||
class ServerThread(threading.Thread):
|
||||
def __init__(self, client_port, server_port):
|
||||
threading.Thread.__init__(self)
|
||||
self.client_port = client_port
|
||||
self.server_port = server_port
|
||||
|
||||
def run(self):
|
||||
import pydev_localhost
|
||||
pydevconsole.StartServer(pydev_localhost.get_localhost(), self.server_port, self.client_port)
|
||||
server_thread = ServerThread(client_port, server_port)
|
||||
server_thread.setDaemon(True)
|
||||
server_thread.start()
|
||||
|
||||
client_thread = self.startClientThread(client_port) #@UnusedVariable
|
||||
|
||||
import time
|
||||
time.sleep(.3) #let's give it some time to start the threads
|
||||
|
||||
import pydev_localhost
|
||||
server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), server_port))
|
||||
server.addExec('class Foo:')
|
||||
server.addExec(' pass')
|
||||
server.addExec('')
|
||||
server.addExec('foo = Foo()')
|
||||
server.addExec('a = %s()' % (raw_input_name,))
|
||||
server.addExec('print (a)')
|
||||
self.assertEqual(['input_request'], sys.stdout.getvalue().split())
|
||||
self.original_stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
try:
|
||||
client_port, server_port = self.getFreeAddresses()
|
||||
class ServerThread(threading.Thread):
|
||||
def __init__(self, client_port, server_port):
|
||||
threading.Thread.__init__(self)
|
||||
self.client_port = client_port
|
||||
self.server_port = server_port
|
||||
|
||||
def run(self):
|
||||
import pydev_localhost
|
||||
pydevconsole.StartServer(pydev_localhost.get_localhost(), self.server_port, self.client_port)
|
||||
server_thread = ServerThread(client_port, server_port)
|
||||
server_thread.setDaemon(True)
|
||||
server_thread.start()
|
||||
|
||||
client_thread = self.startClientThread(client_port) #@UnusedVariable
|
||||
|
||||
import time
|
||||
time.sleep(.3) #let's give it some time to start the threads
|
||||
sys.stdout = StringIO()
|
||||
|
||||
import pydev_localhost
|
||||
server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), server_port))
|
||||
server.execLine('class Foo:')
|
||||
server.execLine(' pass')
|
||||
server.execLine('')
|
||||
server.execLine('foo = Foo()')
|
||||
server.execLine('a = %s()' % (raw_input_name,))
|
||||
server.execLine('print (a)')
|
||||
initial = time.time()
|
||||
while not client_thread.requested_input:
|
||||
if time.time() - initial > 2:
|
||||
raise AssertionError('Did not get the return asked before the timeout.')
|
||||
time.sleep(.1)
|
||||
|
||||
while ['input_request'] != sys.stdout.getvalue().split():
|
||||
if time.time() - initial > 2:
|
||||
break
|
||||
time.sleep(.1)
|
||||
self.assertEqual(['input_request'], sys.stdout.getvalue().split())
|
||||
finally:
|
||||
sys.stdout = self.original_stdout
|
||||
|
||||
#=======================================================================================================================
|
||||
# main
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
'''
|
||||
import sys
|
||||
import os
|
||||
from _pydev_imps._pydev_thread import start_new_thread
|
||||
|
||||
#make it as if we were executing from the directory above this one (so that we can use pycompletionserver
|
||||
#without the need for it being in the pythonpath)
|
||||
@@ -13,6 +14,13 @@ sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))
|
||||
IS_PYTHON_3K = 0
|
||||
if sys.platform.find('java') == -1:
|
||||
|
||||
try:
|
||||
import __builtin__ #@UnusedImport
|
||||
BUILTIN_MOD = '__builtin__'
|
||||
except ImportError:
|
||||
BUILTIN_MOD = 'builtins'
|
||||
|
||||
|
||||
|
||||
try:
|
||||
import inspect
|
||||
@@ -41,7 +49,7 @@ if sys.platform.find('java') == -1:
|
||||
unittest.TestCase.tearDown(self)
|
||||
|
||||
def testMessage(self):
|
||||
t = pycompletionserver.T(0)
|
||||
t = pycompletionserver.CompletionServer(0)
|
||||
|
||||
l = []
|
||||
l.append(('Def', 'description' , 'args'))
|
||||
@@ -62,14 +70,14 @@ if sys.platform.find('java') == -1:
|
||||
'''
|
||||
Creates the connections needed for testing.
|
||||
'''
|
||||
t = pycompletionserver.T(p1)
|
||||
|
||||
t.start()
|
||||
|
||||
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server.bind((pycompletionserver.HOST, p1))
|
||||
server.listen(1) #socket to receive messages.
|
||||
|
||||
t = pycompletionserver.CompletionServer(p1)
|
||||
t.exit_process_on_kill = False
|
||||
start_new_thread(t.run, ())
|
||||
|
||||
s, addr = server.accept()
|
||||
|
||||
return t, s
|
||||
@@ -106,6 +114,8 @@ if sys.platform.find('java') == -1:
|
||||
#math is a builtin and because of that, it starts with None as a file
|
||||
start = '@@COMPLETIONS(None,(__doc__,'
|
||||
start_2 = '@@COMPLETIONS(None,(__name__,'
|
||||
if '/math.so,' in completions or '/math.cpython-33m.so,' in completions or '/math.cpython-34m.so,' in completions:
|
||||
return
|
||||
self.assert_(completions.startswith(start) or completions.startswith(start_2), '%s DOESNT START WITH %s' % (completions, (start, start_2)))
|
||||
|
||||
self.assert_('@@COMPLETIONS' in completions)
|
||||
@@ -113,7 +123,7 @@ if sys.platform.find('java') == -1:
|
||||
|
||||
|
||||
#now, test i
|
||||
msg = quote_plus('__builtin__.list')
|
||||
msg = quote_plus('%s.list' % BUILTIN_MOD)
|
||||
send(socket, "@@IMPORTS:%s\nEND@@" % msg)
|
||||
found = self.readMsg()
|
||||
self.assert_('sort' in found, 'Could not find sort in: %s' % (found,))
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
'''
|
||||
@author Fabio Zadrozny
|
||||
'''
|
||||
import os
|
||||
import sys
|
||||
#make it as if we were executing from the directory above this one (so that we can use pycompletionserver
|
||||
#without the need for it being in the pythonpath)
|
||||
#twice the dirname to get the previous level from this file.
|
||||
sys.path.insert(1, os.path.split(os.path.split(__file__)[0])[0])
|
||||
|
||||
try:
|
||||
import __builtin__ #@UnusedImport
|
||||
@@ -50,13 +45,14 @@ if sys.platform.find('java') == -1:
|
||||
pass
|
||||
|
||||
def testImports5(self):
|
||||
tip = _pydev_imports_tipper.GenerateTip('__builtin__.list')
|
||||
tip = _pydev_imports_tipper.GenerateTip('%s.list' % BUILTIN_MOD)
|
||||
s = self.assertIn('sort', tip)
|
||||
self.CheckArgs(
|
||||
s,
|
||||
'(cmp=None, key=None, reverse=False)',
|
||||
'(self, object cmp, object key, bool reverse)',
|
||||
'(self, cmp: object, key: object, reverse: bool)'
|
||||
'(self, cmp: object, key: object, reverse: bool)',
|
||||
'(key=None, reverse=False)',
|
||||
)
|
||||
|
||||
def testImports2a(self):
|
||||
@@ -64,14 +60,24 @@ if sys.platform.find('java') == -1:
|
||||
self.assertIn('__doc__', tips)
|
||||
|
||||
def testImports2b(self):
|
||||
tips = _pydev_imports_tipper.GenerateTip('%s' % BUILTIN_MOD)
|
||||
t = self.assertIn('file' , tips)
|
||||
self.assert_('->' in t[1].strip() or 'file' in t[1])
|
||||
try:
|
||||
file
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
tips = _pydev_imports_tipper.GenerateTip('%s' % BUILTIN_MOD)
|
||||
t = self.assertIn('file' , tips)
|
||||
self.assert_('->' in t[1].strip() or 'file' in t[1])
|
||||
|
||||
def testImports2c(self):
|
||||
tips = _pydev_imports_tipper.GenerateTip('%s.file' % BUILTIN_MOD)
|
||||
t = self.assertIn('readlines' , tips)
|
||||
self.assert_('->' in t[1] or 'sizehint' in t[1])
|
||||
try:
|
||||
file # file is not available on py 3
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
tips = _pydev_imports_tipper.GenerateTip('%s.file' % BUILTIN_MOD)
|
||||
t = self.assertIn('readlines' , tips)
|
||||
self.assert_('->' in t[1] or 'sizehint' in t[1])
|
||||
|
||||
def testImports(self):
|
||||
'''
|
||||
@@ -110,9 +116,9 @@ if sys.platform.find('java') == -1:
|
||||
self.assertIn('RuntimeError' , tip)
|
||||
self.assertIn('RuntimeWarning' , tip)
|
||||
|
||||
t = self.assertIn('cmp' , tip)
|
||||
|
||||
self.CheckArgs(t, '(x, y)', '(object x, object y)', '(x: object, y: object)') #args
|
||||
# Remove cmp as it's not available on py 3
|
||||
#t = self.assertIn('cmp' , tip)
|
||||
#self.CheckArgs(t, '(x, y)', '(object x, object y)', '(x: object, y: object)') #args
|
||||
|
||||
t = self.assertIn('isinstance' , tip)
|
||||
self.CheckArgs(t, '(object, class_or_type_or_tuple)', '(object o, type typeinfo)', '(o: object, typeinfo: type)') #args
|
||||
|
||||
@@ -9,42 +9,44 @@ To run this:
|
||||
4) run: gl.glClearColor(1,1,1,1)
|
||||
"""
|
||||
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
import OpenGL.GL as gl
|
||||
import OpenGL.GLUT as glut
|
||||
if __name__ == '__main__':
|
||||
|
||||
def close():
|
||||
glut.glutDestroyWindow(glut.glutGetWindow())
|
||||
|
||||
def display():
|
||||
gl.glClear (gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
|
||||
glut.glutSwapBuffers()
|
||||
|
||||
def resize(width,height):
|
||||
gl.glViewport(0, 0, width, height+4)
|
||||
gl.glMatrixMode(gl.GL_PROJECTION)
|
||||
gl.glLoadIdentity()
|
||||
gl.glOrtho(0, width, 0, height+4, -1, 1)
|
||||
gl.glMatrixMode(gl.GL_MODELVIEW)
|
||||
|
||||
if glut.glutGetWindow() > 0:
|
||||
interactive = True
|
||||
glut.glutInit(sys.argv)
|
||||
glut.glutInitDisplayMode(glut.GLUT_DOUBLE |
|
||||
glut.GLUT_RGBA |
|
||||
glut.GLUT_DEPTH)
|
||||
else:
|
||||
interactive = False
|
||||
|
||||
glut.glutCreateWindow('gui-glut')
|
||||
glut.glutDisplayFunc(display)
|
||||
glut.glutReshapeFunc(resize)
|
||||
# This is necessary on osx to be able to close the window
|
||||
# (else the close button is disabled)
|
||||
if sys.platform == 'darwin' and not bool(glut.HAVE_FREEGLUT):
|
||||
glut.glutWMCloseFunc(close)
|
||||
gl.glClearColor(0,0,0,1)
|
||||
|
||||
if not interactive:
|
||||
glut.glutMainLoop()
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
import OpenGL.GL as gl
|
||||
import OpenGL.GLUT as glut
|
||||
|
||||
def close():
|
||||
glut.glutDestroyWindow(glut.glutGetWindow())
|
||||
|
||||
def display():
|
||||
gl.glClear (gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
|
||||
glut.glutSwapBuffers()
|
||||
|
||||
def resize(width,height):
|
||||
gl.glViewport(0, 0, width, height+4)
|
||||
gl.glMatrixMode(gl.GL_PROJECTION)
|
||||
gl.glLoadIdentity()
|
||||
gl.glOrtho(0, width, 0, height+4, -1, 1)
|
||||
gl.glMatrixMode(gl.GL_MODELVIEW)
|
||||
|
||||
if glut.glutGetWindow() > 0:
|
||||
interactive = True
|
||||
glut.glutInit(sys.argv)
|
||||
glut.glutInitDisplayMode(glut.GLUT_DOUBLE |
|
||||
glut.GLUT_RGBA |
|
||||
glut.GLUT_DEPTH)
|
||||
else:
|
||||
interactive = False
|
||||
|
||||
glut.glutCreateWindow('gui-glut')
|
||||
glut.glutDisplayFunc(display)
|
||||
glut.glutReshapeFunc(resize)
|
||||
# This is necessary on osx to be able to close the window
|
||||
# (else the close button is disabled)
|
||||
if sys.platform == 'darwin' and not bool(glut.HAVE_FREEGLUT):
|
||||
glut.glutWMCloseFunc(close)
|
||||
gl.glClearColor(0,0,0,1)
|
||||
|
||||
if not interactive:
|
||||
glut.glutMainLoop()
|
||||
|
||||
@@ -8,27 +8,28 @@ To run this:
|
||||
interactive console
|
||||
"""
|
||||
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
|
||||
|
||||
def hello_world(wigdet, data=None):
|
||||
print("Hello World")
|
||||
|
||||
def delete_event(widget, event, data=None):
|
||||
return False
|
||||
|
||||
def destroy(widget, data=None):
|
||||
gtk.main_quit()
|
||||
|
||||
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||
window.connect("delete_event", delete_event)
|
||||
window.connect("destroy", destroy)
|
||||
button = gtk.Button("Hello World")
|
||||
button.connect("clicked", hello_world, None)
|
||||
|
||||
window.add(button)
|
||||
button.show()
|
||||
window.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
|
||||
|
||||
def hello_world(wigdet, data=None):
|
||||
print("Hello World")
|
||||
|
||||
def delete_event(widget, event, data=None):
|
||||
return False
|
||||
|
||||
def destroy(widget, data=None):
|
||||
gtk.main_quit()
|
||||
|
||||
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||
window.connect("delete_event", delete_event)
|
||||
window.connect("destroy", destroy)
|
||||
button = gtk.Button("Hello World")
|
||||
button.connect("clicked", hello_world, None)
|
||||
|
||||
window.add(button)
|
||||
button.show()
|
||||
window.show()
|
||||
|
||||
|
||||
@@ -8,25 +8,26 @@ To run this:
|
||||
interactive console
|
||||
"""
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
def hello_world(wigdet, data=None):
|
||||
print("Hello World")
|
||||
|
||||
def delete_event(widget, event, data=None):
|
||||
return False
|
||||
|
||||
def destroy(widget, data=None):
|
||||
Gtk.main_quit()
|
||||
|
||||
window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
|
||||
window.connect("delete_event", delete_event)
|
||||
window.connect("destroy", destroy)
|
||||
button = Gtk.Button("Hello World")
|
||||
button.connect("clicked", hello_world, None)
|
||||
|
||||
window.add(button)
|
||||
button.show()
|
||||
window.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
def hello_world(wigdet, data=None):
|
||||
print("Hello World")
|
||||
|
||||
def delete_event(widget, event, data=None):
|
||||
return False
|
||||
|
||||
def destroy(widget, data=None):
|
||||
Gtk.main_quit()
|
||||
|
||||
window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
|
||||
window.connect("delete_event", delete_event)
|
||||
window.connect("destroy", destroy)
|
||||
button = Gtk.Button("Hello World")
|
||||
button.connect("clicked", hello_world, None)
|
||||
|
||||
window.add(button)
|
||||
button.show()
|
||||
window.show()
|
||||
|
||||
|
||||
@@ -8,20 +8,21 @@ To run this:
|
||||
interactive console
|
||||
"""
|
||||
|
||||
import pyglet
|
||||
|
||||
|
||||
window = pyglet.window.Window()
|
||||
label = pyglet.text.Label('Hello, world',
|
||||
font_name='Times New Roman',
|
||||
font_size=36,
|
||||
x=window.width//2, y=window.height//2,
|
||||
anchor_x='center', anchor_y='center')
|
||||
@window.event
|
||||
def on_close():
|
||||
window.close()
|
||||
|
||||
@window.event
|
||||
def on_draw():
|
||||
window.clear()
|
||||
label.draw()
|
||||
if __name__ == '__main__':
|
||||
import pyglet
|
||||
|
||||
|
||||
window = pyglet.window.Window()
|
||||
label = pyglet.text.Label('Hello, world',
|
||||
font_name='Times New Roman',
|
||||
font_size=36,
|
||||
x=window.width//2, y=window.height//2,
|
||||
anchor_x='center', anchor_y='center')
|
||||
@window.event
|
||||
def on_close():
|
||||
window.close()
|
||||
|
||||
@window.event
|
||||
def on_draw():
|
||||
window.clear()
|
||||
label.draw()
|
||||
|
||||
@@ -10,26 +10,26 @@ To run this:
|
||||
Ref: Modified from http://zetcode.com/tutorials/pyqt4/firstprograms/
|
||||
"""
|
||||
|
||||
import sys
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
class SimpleWindow(QtGui.QWidget):
|
||||
def __init__(self, parent=None):
|
||||
QtGui.QWidget.__init__(self, parent)
|
||||
|
||||
self.setGeometry(300, 300, 200, 80)
|
||||
self.setWindowTitle('Hello World')
|
||||
|
||||
quit = QtGui.QPushButton('Close', self)
|
||||
quit.setGeometry(10, 10, 60, 35)
|
||||
|
||||
self.connect(quit, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('close()'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QtCore.QCoreApplication.instance()
|
||||
if app is None:
|
||||
app = QtGui.QApplication([])
|
||||
|
||||
sw = SimpleWindow()
|
||||
sw.show()
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
class SimpleWindow(QtGui.QWidget):
|
||||
def __init__(self, parent=None):
|
||||
QtGui.QWidget.__init__(self, parent)
|
||||
|
||||
self.setGeometry(300, 300, 200, 80)
|
||||
self.setWindowTitle('Hello World')
|
||||
|
||||
quit = QtGui.QPushButton('Close', self)
|
||||
quit.setGeometry(10, 10, 60, 35)
|
||||
|
||||
self.connect(quit, QtCore.SIGNAL('clicked()'),
|
||||
self, QtCore.SLOT('close()'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QtCore.QCoreApplication.instance()
|
||||
if app is None:
|
||||
app = QtGui.QApplication([])
|
||||
|
||||
sw = SimpleWindow()
|
||||
sw.show()
|
||||
|
||||
@@ -8,24 +8,26 @@ To run this:
|
||||
interactive console
|
||||
"""
|
||||
|
||||
try:
|
||||
from Tkinter import *
|
||||
except:
|
||||
# Python 3
|
||||
from tkinter import *
|
||||
|
||||
class MyApp:
|
||||
|
||||
def __init__(self, root):
|
||||
frame = Frame(root)
|
||||
frame.pack()
|
||||
|
||||
self.button = Button(frame, text="Hello", command=self.hello_world)
|
||||
self.button.pack(side=LEFT)
|
||||
|
||||
def hello_world(self):
|
||||
print("Hello World!")
|
||||
|
||||
root = Tk()
|
||||
|
||||
app = MyApp(root)
|
||||
if __name__ == '__main__':
|
||||
|
||||
try:
|
||||
from Tkinter import *
|
||||
except:
|
||||
# Python 3
|
||||
from tkinter import *
|
||||
|
||||
class MyApp:
|
||||
|
||||
def __init__(self, root):
|
||||
frame = Frame(root)
|
||||
frame.pack()
|
||||
|
||||
self.button = Button(frame, text="Hello", command=self.hello_world)
|
||||
self.button.pack(side=LEFT)
|
||||
|
||||
def hello_world(self):
|
||||
print("Hello World!")
|
||||
|
||||
root = Tk()
|
||||
|
||||
app = MyApp(root)
|
||||
|
||||
@@ -11,91 +11,93 @@ To run this:
|
||||
Ref: Modified from wxPython source code wxPython/samples/simple/simple.py
|
||||
"""
|
||||
|
||||
import wx
|
||||
|
||||
|
||||
class MyFrame(wx.Frame):
|
||||
"""
|
||||
This is MyFrame. It just shows a few controls on a wxPanel,
|
||||
and has a simple menu.
|
||||
"""
|
||||
def __init__(self, parent, title):
|
||||
wx.Frame.__init__(self, parent, -1, title,
|
||||
pos=(150, 150), size=(350, 200))
|
||||
|
||||
# Create the menubar
|
||||
menuBar = wx.MenuBar()
|
||||
|
||||
# and a menu
|
||||
menu = wx.Menu()
|
||||
|
||||
# add an item to the menu, using \tKeyName automatically
|
||||
# creates an accelerator, the third param is some help text
|
||||
# that will show up in the statusbar
|
||||
menu.Append(wx.ID_EXIT, "E&xit\tAlt-X", "Exit this simple sample")
|
||||
|
||||
# bind the menu event to an event handler
|
||||
self.Bind(wx.EVT_MENU, self.OnTimeToClose, id=wx.ID_EXIT)
|
||||
|
||||
# and put the menu on the menubar
|
||||
menuBar.Append(menu, "&File")
|
||||
self.SetMenuBar(menuBar)
|
||||
|
||||
self.CreateStatusBar()
|
||||
|
||||
# Now create the Panel to put the other controls on.
|
||||
panel = wx.Panel(self)
|
||||
|
||||
# and a few controls
|
||||
text = wx.StaticText(panel, -1, "Hello World!")
|
||||
text.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD))
|
||||
text.SetSize(text.GetBestSize())
|
||||
btn = wx.Button(panel, -1, "Close")
|
||||
funbtn = wx.Button(panel, -1, "Just for fun...")
|
||||
|
||||
# bind the button events to handlers
|
||||
self.Bind(wx.EVT_BUTTON, self.OnTimeToClose, btn)
|
||||
self.Bind(wx.EVT_BUTTON, self.OnFunButton, funbtn)
|
||||
|
||||
# Use a sizer to layout the controls, stacked vertically and with
|
||||
# a 10 pixel border around each
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.Add(text, 0, wx.ALL, 10)
|
||||
sizer.Add(btn, 0, wx.ALL, 10)
|
||||
sizer.Add(funbtn, 0, wx.ALL, 10)
|
||||
panel.SetSizer(sizer)
|
||||
panel.Layout()
|
||||
|
||||
|
||||
def OnTimeToClose(self, evt):
|
||||
"""Event handler for the button click."""
|
||||
print("See ya later!")
|
||||
self.Close()
|
||||
|
||||
def OnFunButton(self, evt):
|
||||
"""Event handler for the button click."""
|
||||
print("Having fun yet?")
|
||||
|
||||
|
||||
class MyApp(wx.App):
|
||||
def OnInit(self):
|
||||
frame = MyFrame(None, "Simple wxPython App")
|
||||
self.SetTopWindow(frame)
|
||||
|
||||
print("Print statements go to this stdout window by default.")
|
||||
|
||||
frame.Show(True)
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
app = wx.GetApp()
|
||||
if app is None:
|
||||
app = MyApp(redirect=False, clearSigInt=False)
|
||||
else:
|
||||
frame = MyFrame(None, "Simple wxPython App")
|
||||
app.SetTopWindow(frame)
|
||||
print("Print statements go to this stdout window by default.")
|
||||
frame.Show(True)
|
||||
|
||||
import wx
|
||||
|
||||
|
||||
class MyFrame(wx.Frame):
|
||||
"""
|
||||
This is MyFrame. It just shows a few controls on a wxPanel,
|
||||
and has a simple menu.
|
||||
"""
|
||||
def __init__(self, parent, title):
|
||||
wx.Frame.__init__(self, parent, -1, title,
|
||||
pos=(150, 150), size=(350, 200))
|
||||
|
||||
# Create the menubar
|
||||
menuBar = wx.MenuBar()
|
||||
|
||||
# and a menu
|
||||
menu = wx.Menu()
|
||||
|
||||
# add an item to the menu, using \tKeyName automatically
|
||||
# creates an accelerator, the third param is some help text
|
||||
# that will show up in the statusbar
|
||||
menu.Append(wx.ID_EXIT, "E&xit\tAlt-X", "Exit this simple sample")
|
||||
|
||||
# bind the menu event to an event handler
|
||||
self.Bind(wx.EVT_MENU, self.OnTimeToClose, id=wx.ID_EXIT)
|
||||
|
||||
# and put the menu on the menubar
|
||||
menuBar.Append(menu, "&File")
|
||||
self.SetMenuBar(menuBar)
|
||||
|
||||
self.CreateStatusBar()
|
||||
|
||||
# Now create the Panel to put the other controls on.
|
||||
panel = wx.Panel(self)
|
||||
|
||||
# and a few controls
|
||||
text = wx.StaticText(panel, -1, "Hello World!")
|
||||
text.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD))
|
||||
text.SetSize(text.GetBestSize())
|
||||
btn = wx.Button(panel, -1, "Close")
|
||||
funbtn = wx.Button(panel, -1, "Just for fun...")
|
||||
|
||||
# bind the button events to handlers
|
||||
self.Bind(wx.EVT_BUTTON, self.OnTimeToClose, btn)
|
||||
self.Bind(wx.EVT_BUTTON, self.OnFunButton, funbtn)
|
||||
|
||||
# Use a sizer to layout the controls, stacked vertically and with
|
||||
# a 10 pixel border around each
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.Add(text, 0, wx.ALL, 10)
|
||||
sizer.Add(btn, 0, wx.ALL, 10)
|
||||
sizer.Add(funbtn, 0, wx.ALL, 10)
|
||||
panel.SetSizer(sizer)
|
||||
panel.Layout()
|
||||
|
||||
|
||||
def OnTimeToClose(self, evt):
|
||||
"""Event handler for the button click."""
|
||||
print("See ya later!")
|
||||
self.Close()
|
||||
|
||||
def OnFunButton(self, evt):
|
||||
"""Event handler for the button click."""
|
||||
print("Having fun yet?")
|
||||
|
||||
|
||||
class MyApp(wx.App):
|
||||
def OnInit(self):
|
||||
frame = MyFrame(None, "Simple wxPython App")
|
||||
self.SetTopWindow(frame)
|
||||
|
||||
print("Print statements go to this stdout window by default.")
|
||||
|
||||
frame.Show(True)
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
app = wx.GetApp()
|
||||
if app is None:
|
||||
app = MyApp(redirect=False, clearSigInt=False)
|
||||
else:
|
||||
frame = MyFrame(None, "Simple wxPython App")
|
||||
app.SetTopWindow(frame)
|
||||
print("Print statements go to this stdout window by default.")
|
||||
frame.Show(True)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ def m2(a):
|
||||
c = 30
|
||||
|
||||
def function2():
|
||||
print a
|
||||
print(a)
|
||||
|
||||
return a
|
||||
|
||||
|
||||
@@ -5,6 +5,6 @@ class A:
|
||||
|
||||
if __name__ == '__main__':
|
||||
a = A()
|
||||
print a._A__var
|
||||
print(a._A__var)
|
||||
# Evaluate 'a.__var' should give a._A__var_
|
||||
print('TEST SUCEEDED')
|
||||
|
||||
@@ -5,4 +5,4 @@ def Call():
|
||||
|
||||
if __name__ == '__main__':
|
||||
Call()
|
||||
print 'TEST SUCEEDED!'
|
||||
print('TEST SUCEEDED!')
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
def Method1():
|
||||
print 'm1'
|
||||
print('m1')
|
||||
|
||||
def Method2():
|
||||
print 'm2 before'
|
||||
print('m2 before')
|
||||
Method1()
|
||||
print 'm2 after'
|
||||
print('m2 after')
|
||||
|
||||
def Method3():
|
||||
print 'm3 before'
|
||||
print('m3 before')
|
||||
Method2()
|
||||
print 'm3 after'
|
||||
print('m3 after')
|
||||
|
||||
if __name__ == '__main__':
|
||||
Method3()
|
||||
print 'TEST SUCEEDED!'
|
||||
print('TEST SUCEEDED!')
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import time
|
||||
import sys
|
||||
|
||||
try:
|
||||
from PySide import QtCore
|
||||
except:
|
||||
from PyQt4 import QtCore
|
||||
|
||||
# Subclassing QThread
|
||||
# http://doc.qt.nokia.com/latest/qthread.html
|
||||
class AThread(QtCore.QThread):
|
||||
|
||||
def run(self):
|
||||
count = 0
|
||||
while count < 5:
|
||||
time.sleep(.5)
|
||||
print("Increasing", count);sys.stdout.flush()
|
||||
count += 1
|
||||
|
||||
app = QtCore.QCoreApplication([])
|
||||
thread = AThread()
|
||||
thread.finished.connect(app.exit)
|
||||
thread.start()
|
||||
app.exec_()
|
||||
print('TEST SUCEEDED!')
|
||||
@@ -0,0 +1,31 @@
|
||||
import time
|
||||
|
||||
try:
|
||||
from PySide import QtCore
|
||||
except:
|
||||
from PyQt4 import QtCore
|
||||
|
||||
# Subclassing QObject and using moveToThread
|
||||
# http://labs.qt.nokia.com/2007/07/05/qthreads-no-longer-abstract/
|
||||
class SomeObject(QtCore.QObject):
|
||||
|
||||
finished = QtCore.Signal()
|
||||
|
||||
def longRunning(self):
|
||||
count = 0
|
||||
while count < 5:
|
||||
time.sleep(.5)
|
||||
print "Increasing"
|
||||
count += 1
|
||||
self.finished.emit()
|
||||
|
||||
app = QtCore.QCoreApplication([])
|
||||
objThread = QtCore.QThread()
|
||||
obj = SomeObject()
|
||||
obj.moveToThread(objThread)
|
||||
obj.finished.connect(objThread.quit)
|
||||
objThread.started.connect(obj.longRunning)
|
||||
objThread.finished.connect(app.exit)
|
||||
objThread.start()
|
||||
app.exec_()
|
||||
print('TEST SUCEEDED!')
|
||||
@@ -0,0 +1,28 @@
|
||||
import time
|
||||
|
||||
try:
|
||||
from PySide import QtCore
|
||||
except:
|
||||
from PyQt4 import QtCore
|
||||
|
||||
# Using a QRunnable
|
||||
# http://doc.qt.nokia.com/latest/qthreadpool.html
|
||||
# Note that a QRunnable isn't a subclass of QObject and therefore does
|
||||
# not provide signals and slots.
|
||||
class Runnable(QtCore.QRunnable):
|
||||
|
||||
def run(self):
|
||||
count = 0
|
||||
app = QtCore.QCoreApplication.instance()
|
||||
while count < 5:
|
||||
print "Increasing"
|
||||
time.sleep(.5)
|
||||
count += 1
|
||||
app.quit()
|
||||
|
||||
|
||||
app = QtCore.QCoreApplication([])
|
||||
runnable = Runnable()
|
||||
QtCore.QThreadPool.globalInstance().start(runnable)
|
||||
app.exec_()
|
||||
print('TEST SUCEEDED!')
|
||||
@@ -1,10 +1,21 @@
|
||||
import sys
|
||||
import os
|
||||
import pydev_monkey
|
||||
sys.path.insert(0, os.path.split(os.path.split(__file__)[0])[0])
|
||||
|
||||
from pydevd_constants import Null
|
||||
import unittest
|
||||
|
||||
try:
|
||||
import thread
|
||||
except:
|
||||
import _thread as thread
|
||||
|
||||
try:
|
||||
xrange
|
||||
except:
|
||||
xrange = range
|
||||
|
||||
#=======================================================================================================================
|
||||
# TestCase
|
||||
#=======================================================================================================================
|
||||
@@ -40,10 +51,7 @@ class TestCase(unittest.TestCase):
|
||||
|
||||
|
||||
def testStartNewThread(self):
|
||||
import pydevd
|
||||
import thread
|
||||
original = thread.start_new_thread
|
||||
thread.start_new_thread = pydevd.pydev_start_new_thread
|
||||
pydev_monkey.patch_thread_modules()
|
||||
try:
|
||||
found = {}
|
||||
def function(a, b, *args, **kwargs):
|
||||
@@ -62,15 +70,11 @@ class TestCase(unittest.TestCase):
|
||||
|
||||
self.assertEqual({'a': 1, 'b': 2, 'args': (3, 4), 'kwargs': {'e': 2, 'd': 1}}, found)
|
||||
finally:
|
||||
thread.start_new_thread = original
|
||||
pydev_monkey.undo_patch_thread_modules()
|
||||
|
||||
|
||||
def testStartNewThread2(self):
|
||||
import pydevd
|
||||
import thread
|
||||
|
||||
original = thread.start_new_thread
|
||||
thread.start_new_thread = pydevd.pydev_start_new_thread
|
||||
pydev_monkey.patch_thread_modules()
|
||||
try:
|
||||
found = {}
|
||||
|
||||
@@ -101,7 +105,7 @@ class TestCase(unittest.TestCase):
|
||||
|
||||
self.assertEqual({'a': 1, 'b': 2, 'args': (3, 4), 'kwargs': {'e': 2, 'd': 1}}, found)
|
||||
finally:
|
||||
thread.start_new_thread = original
|
||||
pydev_monkey.undo_patch_thread_modules()
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
|
||||
@@ -5,6 +5,17 @@
|
||||
|
||||
Note that it's a python script but it'll spawn a process to run as jython, ironpython and as python.
|
||||
'''
|
||||
SHOW_WRITES_AND_READS = False
|
||||
SHOW_OTHER_DEBUG_INFO = False
|
||||
SHOW_STDOUT = False
|
||||
|
||||
|
||||
|
||||
from pydevd_constants import IS_PY3K
|
||||
try:
|
||||
from thread import start_new_thread
|
||||
except:
|
||||
from _thread import start_new_thread
|
||||
CMD_SET_PROPERTY_TRACE, CMD_EVALUATE_CONSOLE_EXPRESSION, CMD_RUN_CUSTOM_OPERATION, CMD_ENABLE_DONT_TRACE = 133, 134, 135, 141
|
||||
PYTHON_EXE = None
|
||||
IRONPYTHON_EXE = None
|
||||
@@ -14,9 +25,13 @@ JAVA_LOCATION = None
|
||||
|
||||
import unittest
|
||||
import pydev_localhost
|
||||
|
||||
port = None
|
||||
|
||||
try:
|
||||
xrange
|
||||
except:
|
||||
xrange = range
|
||||
|
||||
def UpdatePort():
|
||||
global port
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
@@ -25,29 +40,26 @@ def UpdatePort():
|
||||
s.close()
|
||||
|
||||
import os
|
||||
def NormFile(filename):
|
||||
def _get_debugger_test_file(filename):
|
||||
try:
|
||||
rPath = os.path.realpath # @UndefinedVariable
|
||||
except:
|
||||
# jython does not support os.path.realpath
|
||||
# realpath is a no-op on systems without islink support
|
||||
rPath = os.path.abspath
|
||||
return os.path.normcase(rPath(filename))
|
||||
|
||||
return os.path.normcase(rPath(os.path.join(os.path.dirname(__file__), filename)))
|
||||
|
||||
import pydevd
|
||||
PYDEVD_FILE = pydevd.__file__
|
||||
|
||||
PYDEVD_FILE = NormFile('../pydevd.py')
|
||||
import sys
|
||||
sys.path.append(os.path.dirname(PYDEVD_FILE))
|
||||
|
||||
SHOW_WRITES_AND_READS = False
|
||||
SHOW_RESULT_STR = False
|
||||
SHOW_OTHER_DEBUG_INFO = False
|
||||
|
||||
|
||||
import subprocess
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
from urllib import quote_plus, quote, unquote_plus
|
||||
from pydev_imports import quote_plus, quote, unquote_plus
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
@@ -59,13 +71,16 @@ class ReaderThread(threading.Thread):
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
self.sock = sock
|
||||
self.lastReceived = None
|
||||
self.lastReceived = ''
|
||||
|
||||
def run(self):
|
||||
last_printed = None
|
||||
try:
|
||||
buf = ''
|
||||
while True:
|
||||
l = self.sock.recv(1024)
|
||||
if IS_PY3K:
|
||||
l = l.decode('utf-8')
|
||||
buf += l
|
||||
|
||||
if '\n' in buf:
|
||||
@@ -73,7 +88,9 @@ class ReaderThread(threading.Thread):
|
||||
buf = ''
|
||||
|
||||
if SHOW_WRITES_AND_READS:
|
||||
print 'Test Reader Thread Received %s' % self.lastReceived.strip()
|
||||
if last_printed != self.lastReceived.strip():
|
||||
last_printed = self.lastReceived.strip()
|
||||
print('Test Reader Thread Received %s' % last_printed)
|
||||
except:
|
||||
pass # ok, finished it
|
||||
|
||||
@@ -90,6 +107,8 @@ class AbstractWriterThread(threading.Thread):
|
||||
self.setDaemon(True)
|
||||
self.finishedOk = False
|
||||
self._next_breakpoint_id = 0
|
||||
self.log = []
|
||||
|
||||
|
||||
def DoKill(self):
|
||||
if hasattr(self, 'readerThread'):
|
||||
@@ -98,10 +117,14 @@ class AbstractWriterThread(threading.Thread):
|
||||
self.sock.close()
|
||||
|
||||
def Write(self, s):
|
||||
|
||||
last = self.readerThread.lastReceived
|
||||
if SHOW_WRITES_AND_READS:
|
||||
print 'Test Writer Thread Written %s' % (s,)
|
||||
self.sock.send(s + '\n')
|
||||
print('Test Writer Thread Written %s' % (s,))
|
||||
msg = s + '\n'
|
||||
if IS_PY3K:
|
||||
msg = msg.encode('utf-8')
|
||||
self.sock.send(msg)
|
||||
time.sleep(0.2)
|
||||
|
||||
i = 0
|
||||
@@ -112,16 +135,16 @@ class AbstractWriterThread(threading.Thread):
|
||||
|
||||
def StartSocket(self):
|
||||
if SHOW_WRITES_AND_READS:
|
||||
print 'StartSocket'
|
||||
print('StartSocket')
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.bind(('', port))
|
||||
s.listen(1)
|
||||
if SHOW_WRITES_AND_READS:
|
||||
print 'Waiting in socket.accept()'
|
||||
print('Waiting in socket.accept()')
|
||||
newSock, addr = s.accept()
|
||||
if SHOW_WRITES_AND_READS:
|
||||
print 'Test Writer Thread Socket:', newSock, addr
|
||||
print('Test Writer Thread Socket:', newSock, addr)
|
||||
|
||||
readerThread = self.readerThread = ReaderThread(newSock)
|
||||
readerThread.start()
|
||||
@@ -130,6 +153,7 @@ class AbstractWriterThread(threading.Thread):
|
||||
self._sequence = -1
|
||||
# initial command is always the version
|
||||
self.WriteVersion()
|
||||
self.log.append('StartSocket')
|
||||
|
||||
def NextBreakpointId(self):
|
||||
self._next_breakpoint_id += 1
|
||||
@@ -160,22 +184,27 @@ class AbstractWriterThread(threading.Thread):
|
||||
109 is return
|
||||
111 is breakpoint
|
||||
'''
|
||||
self.log.append('Start: WaitForBreakpointHit')
|
||||
i = 0
|
||||
# wait for hit breakpoint
|
||||
while not ('stop_reason="%s"' % reason) in self.readerThread.lastReceived:
|
||||
last = self.readerThread.lastReceived
|
||||
while not ('stop_reason="%s"' % reason) in last:
|
||||
i += 1
|
||||
time.sleep(1)
|
||||
last = self.readerThread.lastReceived
|
||||
if i >= 10:
|
||||
raise AssertionError('After %s seconds, a break with reason: %s was not hit. Found: %s' % \
|
||||
(i, reason, self.readerThread.lastReceived))
|
||||
(i, reason, last))
|
||||
|
||||
# we have something like <xml><thread id="12152656" stop_reason="111"><frame id="12453120" ...
|
||||
splitted = self.readerThread.lastReceived.split('"')
|
||||
splitted = last.split('"')
|
||||
threadId = splitted[1]
|
||||
frameId = splitted[7]
|
||||
if get_line:
|
||||
self.log.append('End(0): WaitForBreakpointHit')
|
||||
return threadId, frameId, int(splitted[13])
|
||||
|
||||
self.log.append('End(1): WaitForBreakpointHit')
|
||||
return threadId, frameId
|
||||
|
||||
def WaitForCustomOperation(self, expected):
|
||||
@@ -264,6 +293,7 @@ class AbstractWriterThread(threading.Thread):
|
||||
|
||||
def WriteMakeInitialRun(self):
|
||||
self.Write("101\t%s\t" % self.NextSeq())
|
||||
self.log.append('WriteMakeInitialRun')
|
||||
|
||||
def WriteVersion(self):
|
||||
self.Write("501\t%s\t1.0\tWINDOWS\tID" % self.NextSeq())
|
||||
@@ -274,6 +304,7 @@ class AbstractWriterThread(threading.Thread):
|
||||
'''
|
||||
breakpoint_id = self.NextBreakpointId()
|
||||
self.Write("111\t%s\t%s\t%s\t%s\t%s\t%s\tNone\tNone" % (self.NextSeq(), breakpoint_id, 'python-line', self.TEST_FILE, line, func))
|
||||
self.log.append('WriteAddBreakpoint: %s line: %s func: %s' % (breakpoint_id, line, func))
|
||||
return breakpoint_id
|
||||
|
||||
def WriteRemoveBreakpoint(self, breakpoint_id):
|
||||
@@ -284,6 +315,7 @@ class AbstractWriterThread(threading.Thread):
|
||||
|
||||
def WriteGetFrame(self, threadId, frameId):
|
||||
self.Write("114\t%s\t%s\t%s\tFRAME" % (self.NextSeq(), threadId, frameId))
|
||||
self.log.append('WriteGetFrame')
|
||||
|
||||
def WriteGetVariable(self, threadId, frameId, var_attrs):
|
||||
self.Write("110\t%s\t%s\t%s\tFRAME\t%s" % (self.NextSeq(), threadId, frameId, var_attrs))
|
||||
@@ -301,6 +333,7 @@ class AbstractWriterThread(threading.Thread):
|
||||
self.Write("105\t%s\t%s" % (self.NextSeq(), threadId,))
|
||||
|
||||
def WriteRunThread(self, threadId):
|
||||
self.log.append('WriteRunThread')
|
||||
self.Write("106\t%s\t%s" % (self.NextSeq(), threadId,))
|
||||
|
||||
def WriteKillThread(self, threadId):
|
||||
@@ -328,7 +361,7 @@ class AbstractWriterThread(threading.Thread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase19(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case19.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case19.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -352,7 +385,7 @@ class WriterThreadCase19(AbstractWriterThread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase18(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case18.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case18.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -372,7 +405,7 @@ class WriterThreadCase18(AbstractWriterThread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase17(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case17.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case17.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -400,7 +433,7 @@ class WriterThreadCase17(AbstractWriterThread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase16(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case16.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case16.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -430,8 +463,16 @@ class WriterThreadCase16(AbstractWriterThread):
|
||||
self.WaitForVar('<var name="%27size%27')
|
||||
|
||||
self.WriteGetVariable(threadId, frameId, 'bigarray')
|
||||
self.WaitForVar(['<var name="min" type="int64" value="int64%253A 0" />', '<var name="size" type="int" value="int%3A 100000" />']) # TODO: When on a 32 bit python we get an int32 (which makes this test fail).
|
||||
self.WaitForVar(['<var name="max" type="int64" value="int64%253A 99999" />', '<var name="max" type="int32" value="int32%253A 99999" />'])
|
||||
self.WaitForVar([
|
||||
'<var name="min" type="int64" value="int64%253A 0" />',
|
||||
'<var name="min" type="int64" value="int64%3A 0" />',
|
||||
'<var name="size" type="int" value="int%3A 100000" />',
|
||||
])
|
||||
self.WaitForVar([
|
||||
'<var name="max" type="int64" value="int64%253A 99999" />',
|
||||
'<var name="max" type="int32" value="int32%253A 99999" />',
|
||||
'<var name="max" type="int64" value="int64%3A 99999"'
|
||||
])
|
||||
self.WaitForVar('<var name="shape" type="tuple"')
|
||||
self.WaitForVar('<var name="dtype" type="dtype"')
|
||||
self.WaitForVar('<var name="size" type="int"')
|
||||
@@ -441,8 +482,14 @@ class WriterThreadCase16(AbstractWriterThread):
|
||||
# this one is different because it crosses the magic threshold where we don't calculate
|
||||
# the min/max
|
||||
self.WriteGetVariable(threadId, frameId, 'hugearray')
|
||||
self.WaitForVar('<var name="min" type="str" value="str%253A ndarray too big%252C calculating min would slow down debugging" />')
|
||||
self.WaitForVar('<var name="max" type="str" value="str%253A ndarray too big%252C calculating max would slow down debugging" />')
|
||||
self.WaitForVar([
|
||||
'<var name="min" type="str" value="str%253A ndarray too big%252C calculating min would slow down debugging" />',
|
||||
'<var name="min" type="str" value="str%3A ndarray too big%252C calculating min would slow down debugging" />',
|
||||
])
|
||||
self.WaitForVar([
|
||||
'<var name="max" type="str" value="str%253A ndarray too big%252C calculating max would slow down debugging" />',
|
||||
'<var name="max" type="str" value="str%3A ndarray too big%252C calculating max would slow down debugging" />',
|
||||
])
|
||||
self.WaitForVar('<var name="shape" type="tuple"')
|
||||
self.WaitForVar('<var name="dtype" type="dtype"')
|
||||
self.WaitForVar('<var name="size" type="int"')
|
||||
@@ -458,7 +505,7 @@ class WriterThreadCase16(AbstractWriterThread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase15(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case15.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case15.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -472,7 +519,7 @@ class WriterThreadCase15(AbstractWriterThread):
|
||||
self.WaitForCustomOperation('val=Black')
|
||||
assert 7 == self._sequence, 'Expected 7. Had: %s' % self._sequence
|
||||
|
||||
self.WriteCustomOperation("%s\t%s\tEXPRESSION\tcarObj.color" % (threadId, frameId), "EXECFILE", NormFile('_debugger_case15_execfile.py'), "f")
|
||||
self.WriteCustomOperation("%s\t%s\tEXPRESSION\tcarObj.color" % (threadId, frameId), "EXECFILE", _get_debugger_test_file('_debugger_case15_execfile.py'), "f")
|
||||
self.WaitForCustomOperation('val=Black')
|
||||
assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence
|
||||
|
||||
@@ -486,7 +533,7 @@ class WriterThreadCase15(AbstractWriterThread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase14(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case14.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case14.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -499,21 +546,26 @@ class WriterThreadCase14(AbstractWriterThread):
|
||||
|
||||
# Access some variable
|
||||
self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\tcarObj.color" % (threadId, frameId))
|
||||
self.WaitForMultipleVars(['<more>False</more>', '%27Black%27'])
|
||||
self.WaitForVar(['<more>False</more>', '%27Black%27'])
|
||||
assert 7 == self._sequence, 'Expected 9. Had: %s' % self._sequence
|
||||
|
||||
# Change some variable
|
||||
self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\tcarObj.color='Red'" % (threadId, frameId))
|
||||
self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\tcarObj.color" % (threadId, frameId))
|
||||
self.WaitForMultipleVars(['<more>False</more>', '%27Red%27'])
|
||||
self.WaitForVar(['<more>False</more>', '%27Red%27'])
|
||||
assert 11 == self._sequence, 'Expected 13. Had: %s' % self._sequence
|
||||
|
||||
# Iterate some loop
|
||||
self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\tfor i in range(3):" % (threadId, frameId))
|
||||
self.WaitForVars('<xml><more>True</more></xml>')
|
||||
self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\t print i" % (threadId, frameId))
|
||||
self.WaitForVar(['<xml><more>True</more></xml>', '<xml><more>1</more></xml>'])
|
||||
self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\t print(i)" % (threadId, frameId))
|
||||
self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\t" % (threadId, frameId))
|
||||
self.WaitForVars('<xml><more>False</more><output message="0"></output><output message="1"></output><output message="2"></output></xml>')
|
||||
self.WaitForVar(
|
||||
[
|
||||
'<xml><more>False</more><output message="0"></output><output message="1"></output><output message="2"></output></xml>',
|
||||
'<xml><more>0</more><output message="0"></output><output message="1"></output><output message="2"></output></xml>'
|
||||
]
|
||||
)
|
||||
assert 17 == self._sequence, 'Expected 19. Had: %s' % self._sequence
|
||||
|
||||
self.WriteRunThread(threadId)
|
||||
@@ -525,7 +577,7 @@ class WriterThreadCase14(AbstractWriterThread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase13(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case13.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case13.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -575,7 +627,7 @@ class WriterThreadCase13(AbstractWriterThread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase12(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case10.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case10.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -607,7 +659,7 @@ class WriterThreadCase12(AbstractWriterThread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase11(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case10.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case10.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -648,7 +700,7 @@ class WriterThreadCase11(AbstractWriterThread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase10(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case10.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case10.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -682,7 +734,7 @@ class WriterThreadCase10(AbstractWriterThread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase9(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case89.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case89.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -715,7 +767,7 @@ class WriterThreadCase9(AbstractWriterThread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase8(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case89.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case89.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -744,7 +796,7 @@ class WriterThreadCase8(AbstractWriterThread):
|
||||
#======================================================================================================================
|
||||
class WriterThreadCase7(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case7.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case7.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -782,7 +834,7 @@ class WriterThreadCase7(AbstractWriterThread):
|
||||
#=======================================================================================================================
|
||||
class WriterThreadCase6(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case56.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case56.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -817,7 +869,7 @@ class WriterThreadCase6(AbstractWriterThread):
|
||||
#=======================================================================================================================
|
||||
class WriterThreadCase5(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case56.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case56.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -855,7 +907,7 @@ class WriterThreadCase5(AbstractWriterThread):
|
||||
#=======================================================================================================================
|
||||
class WriterThreadCase4(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case4.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case4.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -877,12 +929,12 @@ class WriterThreadCase4(AbstractWriterThread):
|
||||
#=======================================================================================================================
|
||||
class WriterThreadCase3(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case3.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case3.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
self.WriteMakeInitialRun()
|
||||
time.sleep(1)
|
||||
time.sleep(.5)
|
||||
breakpoint_id = self.WriteAddBreakpoint(4, '')
|
||||
self.WriteAddBreakpoint(5, 'FuncNotAvailable') # Check that it doesn't get hit in the global when a function is available
|
||||
|
||||
@@ -909,7 +961,7 @@ class WriterThreadCase3(AbstractWriterThread):
|
||||
#=======================================================================================================================
|
||||
class WriterThreadCase2(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case2.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case2.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
@@ -930,8 +982,79 @@ class WriterThreadCase2(AbstractWriterThread):
|
||||
|
||||
self.WriteRunThread(threadId)
|
||||
|
||||
self.log.append('Checking sequence. Found: %s' % (self._sequence))
|
||||
assert 15 == self._sequence, 'Expected 15. Had: %s' % self._sequence
|
||||
|
||||
self.log.append('Marking finished ok.')
|
||||
self.finishedOk = True
|
||||
|
||||
#=======================================================================================================================
|
||||
# WriterThreadCaseQThread1
|
||||
#=======================================================================================================================
|
||||
class WriterThreadCaseQThread1(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case_qthread1.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
breakpoint_id = self.WriteAddBreakpoint(16, 'run')
|
||||
self.WriteMakeInitialRun()
|
||||
|
||||
threadId, frameId = self.WaitForBreakpointHit()
|
||||
|
||||
self.WriteRemoveBreakpoint(breakpoint_id)
|
||||
self.WriteRunThread(threadId)
|
||||
|
||||
self.log.append('Checking sequence. Found: %s' % (self._sequence))
|
||||
assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence
|
||||
|
||||
self.log.append('Marking finished ok.')
|
||||
self.finishedOk = True
|
||||
|
||||
#=======================================================================================================================
|
||||
# WriterThreadCaseQThread2
|
||||
#=======================================================================================================================
|
||||
class WriterThreadCaseQThread2(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case_qthread2.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
breakpoint_id = self.WriteAddBreakpoint(18, 'longRunning')
|
||||
self.WriteMakeInitialRun()
|
||||
|
||||
threadId, frameId = self.WaitForBreakpointHit()
|
||||
|
||||
self.WriteRemoveBreakpoint(breakpoint_id)
|
||||
self.WriteRunThread(threadId)
|
||||
|
||||
self.log.append('Checking sequence. Found: %s' % (self._sequence))
|
||||
assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence
|
||||
|
||||
self.log.append('Marking finished ok.')
|
||||
self.finishedOk = True
|
||||
|
||||
#=======================================================================================================================
|
||||
# WriterThreadCaseQThread3
|
||||
#=======================================================================================================================
|
||||
class WriterThreadCaseQThread3(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case_qthread3.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
breakpoint_id = self.WriteAddBreakpoint(19, 'run')
|
||||
self.WriteMakeInitialRun()
|
||||
|
||||
threadId, frameId = self.WaitForBreakpointHit()
|
||||
|
||||
self.WriteRemoveBreakpoint(breakpoint_id)
|
||||
self.WriteRunThread(threadId)
|
||||
|
||||
self.log.append('Checking sequence. Found: %s' % (self._sequence))
|
||||
assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence
|
||||
|
||||
self.log.append('Marking finished ok.')
|
||||
self.finishedOk = True
|
||||
|
||||
#=======================================================================================================================
|
||||
@@ -939,24 +1062,39 @@ class WriterThreadCase2(AbstractWriterThread):
|
||||
#=======================================================================================================================
|
||||
class WriterThreadCase1(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = NormFile('_debugger_case1.py')
|
||||
TEST_FILE = _get_debugger_test_file('_debugger_case1.py')
|
||||
|
||||
def run(self):
|
||||
self.StartSocket()
|
||||
|
||||
self.log.append('writing add breakpoint')
|
||||
self.WriteAddBreakpoint(6, 'SetUp')
|
||||
|
||||
self.log.append('making initial run')
|
||||
self.WriteMakeInitialRun()
|
||||
|
||||
self.log.append('waiting for breakpoint hit')
|
||||
threadId, frameId = self.WaitForBreakpointHit()
|
||||
|
||||
self.log.append('get frame')
|
||||
self.WriteGetFrame(threadId, frameId)
|
||||
|
||||
self.log.append('step over')
|
||||
self.WriteStepOver(threadId)
|
||||
|
||||
self.log.append('get frame')
|
||||
self.WriteGetFrame(threadId, frameId)
|
||||
|
||||
self.log.append('run thread')
|
||||
self.WriteRunThread(threadId)
|
||||
|
||||
assert 13 == self._sequence, 'Expected 13. Had: %s' % self._sequence
|
||||
self.log.append('asserting')
|
||||
try:
|
||||
assert 13 == self._sequence, 'Expected 13. Had: %s' % self._sequence
|
||||
except:
|
||||
self.log.append('assert failed!')
|
||||
raise
|
||||
self.log.append('asserted')
|
||||
|
||||
self.finishedOk = True
|
||||
|
||||
@@ -972,6 +1110,7 @@ class DebuggerBase(object):
|
||||
UpdatePort()
|
||||
writerThread = writerThreadClass()
|
||||
writerThread.start()
|
||||
time.sleep(1)
|
||||
|
||||
localhost = pydev_localhost.get_localhost()
|
||||
args = self.getCommandLine()
|
||||
@@ -987,60 +1126,74 @@ class DebuggerBase(object):
|
||||
]
|
||||
|
||||
if SHOW_OTHER_DEBUG_INFO:
|
||||
print 'executing', ' '.join(args)
|
||||
print('executing', ' '.join(args))
|
||||
|
||||
# process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=os.path.dirname(PYDEVD_FILE))
|
||||
process = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=os.path.dirname(PYDEVD_FILE))
|
||||
class ProcessReadThread(threading.Thread):
|
||||
def run(self):
|
||||
self.resultStr = None
|
||||
self.resultStr = process.stdout.read()
|
||||
process.stdout.close()
|
||||
process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=os.path.dirname(PYDEVD_FILE))
|
||||
|
||||
def DoKill(self):
|
||||
process.stdout.close()
|
||||
stdout = []
|
||||
stderr = []
|
||||
|
||||
processReadThread = ProcessReadThread()
|
||||
processReadThread.setDaemon(True)
|
||||
processReadThread.start()
|
||||
def read(stream, buffer):
|
||||
for line in stream.readlines():
|
||||
if IS_PY3K:
|
||||
line = line.decode('utf-8')
|
||||
|
||||
if SHOW_STDOUT:
|
||||
print(line)
|
||||
buffer.append(line)
|
||||
|
||||
start_new_thread(read, (process.stdout, stdout))
|
||||
|
||||
|
||||
if SHOW_OTHER_DEBUG_INFO:
|
||||
print 'Both processes started'
|
||||
print('Both processes started')
|
||||
|
||||
# polls can fail (because the process may finish and the thread still not -- so, we give it some more chances to
|
||||
# finish successfully).
|
||||
pools_failed = 0
|
||||
while writerThread.isAlive():
|
||||
check = 0
|
||||
while True:
|
||||
if process.poll() is not None:
|
||||
pools_failed += 1
|
||||
time.sleep(.2)
|
||||
if pools_failed == 10:
|
||||
break
|
||||
|
||||
if process.poll() is None:
|
||||
for i in range(10):
|
||||
if processReadThread.resultStr is None:
|
||||
time.sleep(.5)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
writerThread.DoKill()
|
||||
|
||||
else:
|
||||
if process.poll() < 0:
|
||||
self.fail("The other process exited with error code: " + str(process.poll()) + " result:" + processReadThread.resultStr)
|
||||
if not writerThread.isAlive():
|
||||
check += 1
|
||||
if check == 20:
|
||||
print('Warning: writer thread exited and process still did not.')
|
||||
if check == 100:
|
||||
self.fail_with_message(
|
||||
"The other process should've exited but still didn't (timeout for process to exit).",
|
||||
stdout, stderr, writerThread
|
||||
)
|
||||
time.sleep(.2)
|
||||
|
||||
|
||||
poll = process.poll()
|
||||
if poll < 0:
|
||||
self.fail_with_message(
|
||||
"The other process exited with error code: " + str(poll), stdout, stderr, writerThread)
|
||||
|
||||
|
||||
if SHOW_RESULT_STR:
|
||||
print processReadThread.resultStr
|
||||
if stdout is None:
|
||||
self.fail_with_message(
|
||||
"The other process may still be running -- and didn't give any output.", stdout, stderr, writerThread)
|
||||
|
||||
if processReadThread.resultStr is None:
|
||||
self.fail("The other process may still be running -- and didn't give any output")
|
||||
|
||||
if 'TEST SUCEEDED' not in processReadThread.resultStr:
|
||||
self.fail(processReadThread.resultStr)
|
||||
if 'TEST SUCEEDED' not in ''.join(stdout):
|
||||
self.fail_with_message("TEST SUCEEDED not found in stdout.", stdout, stderr, writerThread)
|
||||
|
||||
for i in xrange(100):
|
||||
if not writerThread.finishedOk:
|
||||
time.sleep(.1)
|
||||
|
||||
if not writerThread.finishedOk:
|
||||
self.fail("The thread that was doing the tests didn't finish successfully. Output: %s" % processReadThread.resultStr)
|
||||
self.fail_with_message(
|
||||
"The thread that was doing the tests didn't finish successfully.", stdout, stderr, writerThread)
|
||||
|
||||
def fail_with_message(self, msg, stdout, stderr, writerThread):
|
||||
self.fail(msg+
|
||||
"\nStdout: \n"+'\n'.join(stdout)+
|
||||
"\nStderr:"+'\n'.join(stderr)+
|
||||
"\nLog:\n"+'\n'.join(getattr(writerThread, 'log', [])))
|
||||
|
||||
|
||||
def testCase1(self):
|
||||
self.CheckCase(WriterThreadCase1)
|
||||
@@ -1098,6 +1251,30 @@ class DebuggerBase(object):
|
||||
|
||||
def testCase19(self):
|
||||
self.CheckCase(WriterThreadCase19)
|
||||
|
||||
def _has_qt(self):
|
||||
try:
|
||||
from PySide import QtCore
|
||||
return True
|
||||
except:
|
||||
try:
|
||||
from PyQt4 import QtCore
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
def testCaseQthread1(self):
|
||||
if self._has_qt():
|
||||
self.CheckCase(WriterThreadCaseQThread1)
|
||||
|
||||
def testCaseQthread2(self):
|
||||
if self._has_qt():
|
||||
self.CheckCase(WriterThreadCaseQThread2)
|
||||
|
||||
def testCaseQthread3(self):
|
||||
if self._has_qt():
|
||||
self.CheckCase(WriterThreadCaseQThread3)
|
||||
|
||||
|
||||
class TestPython(unittest.TestCase, DebuggerBase):
|
||||
@@ -1117,9 +1294,6 @@ class TestJython(unittest.TestCase, DebuggerBase):
|
||||
def testCase13(self):
|
||||
self.skipTest("Unsupported Decorators")
|
||||
|
||||
def testCase16(self):
|
||||
self.skipTest("Unsupported numpy")
|
||||
|
||||
# This case requires decorators to work (which are not present on Jython 2.1), so, this test is just removed from the jython run.
|
||||
def testCase17(self):
|
||||
self.skipTest("Unsupported Decorators")
|
||||
@@ -1127,6 +1301,9 @@ class TestJython(unittest.TestCase, DebuggerBase):
|
||||
def testCase18(self):
|
||||
self.skipTest("Unsupported assign to local")
|
||||
|
||||
def testCase16(self):
|
||||
self.skipTest("Unsupported numpy")
|
||||
|
||||
class TestIronPython(unittest.TestCase, DebuggerBase):
|
||||
def getCommandLine(self):
|
||||
return [
|
||||
@@ -1134,8 +1311,22 @@ class TestIronPython(unittest.TestCase, DebuggerBase):
|
||||
'-X:Frames'
|
||||
]
|
||||
|
||||
def testCase3(self):
|
||||
self.skipTest("Timing issues") # This test fails once in a while due to timing issues on IronPython, so, skipping it.
|
||||
|
||||
def testCase7(self):
|
||||
# This test checks that we start without variables and at each step a new var is created, but on ironpython,
|
||||
# the variables exist all at once (with None values), so, we can't test it properly.
|
||||
self.skipTest("Different behavior on IronPython")
|
||||
|
||||
def testCase13(self):
|
||||
self.skipTest("Unsupported Decorators") # Not sure why it doesn't work on IronPython, but it's not so common, so, leave it be.
|
||||
|
||||
def testCase16(self):
|
||||
self.skipTest("Unsupported numpy")
|
||||
|
||||
def testCase18(self):
|
||||
self.skipTest("Unsupported assign to local")
|
||||
|
||||
|
||||
def GetLocationFromLine(line):
|
||||
@@ -1157,49 +1348,90 @@ def SplitLine(line):
|
||||
|
||||
|
||||
|
||||
|
||||
import platform
|
||||
sysname = platform.system().lower()
|
||||
test_dependent = os.path.join('../../../', 'org.python.pydev.core', 'tests', 'org', 'python', 'pydev', 'core', 'TestDependent.' + sysname + '.properties')
|
||||
f = open(test_dependent)
|
||||
try:
|
||||
for line in f.readlines():
|
||||
var, loc = SplitLine(line)
|
||||
if 'PYTHON_EXE' == var:
|
||||
PYTHON_EXE = loc
|
||||
|
||||
if 'IRONPYTHON_EXE' == var:
|
||||
IRONPYTHON_EXE = loc
|
||||
|
||||
if 'JYTHON_JAR_LOCATION' == var:
|
||||
JYTHON_JAR_LOCATION = loc
|
||||
|
||||
if 'JAVA_LOCATION' == var:
|
||||
JAVA_LOCATION = loc
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
assert PYTHON_EXE, 'PYTHON_EXE not found in %s' % (test_dependent,)
|
||||
assert IRONPYTHON_EXE, 'IRONPYTHON_EXE not found in %s' % (test_dependent,)
|
||||
assert JYTHON_JAR_LOCATION, 'JYTHON_JAR_LOCATION not found in %s' % (test_dependent,)
|
||||
assert JAVA_LOCATION, 'JAVA_LOCATION not found in %s' % (test_dependent,)
|
||||
assert os.path.exists(PYTHON_EXE), 'The location: %s is not valid' % (PYTHON_EXE,)
|
||||
assert os.path.exists(IRONPYTHON_EXE), 'The location: %s is not valid' % (IRONPYTHON_EXE,)
|
||||
assert os.path.exists(JYTHON_JAR_LOCATION), 'The location: %s is not valid' % (JYTHON_JAR_LOCATION,)
|
||||
assert os.path.exists(JAVA_LOCATION), 'The location: %s is not valid' % (JAVA_LOCATION,)
|
||||
|
||||
if False:
|
||||
suite = unittest.TestSuite()
|
||||
#PYTHON_EXE = r'C:\bin\Anaconda\python.exe'
|
||||
# suite.addTest(TestPython('testCase10'))
|
||||
# suite.addTest(TestPython('testCase3'))
|
||||
# suite.addTest(TestPython('testCase16'))
|
||||
# suite.addTest(TestPython('testCase17'))
|
||||
# suite.addTest(TestPython('testCase18'))
|
||||
# suite.addTest(TestPython('testCase19'))
|
||||
suite = unittest.makeSuite(TestPython)
|
||||
unittest.TextTestRunner(verbosity=3).run(suite)
|
||||
if os.path.exists(test_dependent):
|
||||
f = open(test_dependent)
|
||||
try:
|
||||
for line in f.readlines():
|
||||
var, loc = SplitLine(line)
|
||||
if 'PYTHON_EXE' == var:
|
||||
PYTHON_EXE = loc
|
||||
|
||||
# unittest.TextTestRunner(verbosity=3).run(suite)
|
||||
#
|
||||
# suite = unittest.makeSuite(TestJython)
|
||||
# unittest.TextTestRunner(verbosity=3).run(suite)
|
||||
if 'IRONPYTHON_EXE' == var:
|
||||
IRONPYTHON_EXE = loc
|
||||
|
||||
if 'JYTHON_JAR_LOCATION' == var:
|
||||
JYTHON_JAR_LOCATION = loc
|
||||
|
||||
if 'JAVA_LOCATION' == var:
|
||||
JAVA_LOCATION = loc
|
||||
finally:
|
||||
f.close()
|
||||
else:
|
||||
pass
|
||||
|
||||
if IRONPYTHON_EXE is None:
|
||||
sys.stderr.write('Warning: not running IronPython tests.\n')
|
||||
class TestIronPython(unittest.TestCase):
|
||||
pass
|
||||
|
||||
if JAVA_LOCATION is None:
|
||||
sys.stderr.write('Warning: not running Jython tests.\n')
|
||||
class TestJython(unittest.TestCase):
|
||||
pass
|
||||
|
||||
# if PYTHON_EXE is None:
|
||||
PYTHON_EXE = sys.executable
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if False:
|
||||
assert PYTHON_EXE, 'PYTHON_EXE not found in %s' % (test_dependent,)
|
||||
assert IRONPYTHON_EXE, 'IRONPYTHON_EXE not found in %s' % (test_dependent,)
|
||||
assert JYTHON_JAR_LOCATION, 'JYTHON_JAR_LOCATION not found in %s' % (test_dependent,)
|
||||
assert JAVA_LOCATION, 'JAVA_LOCATION not found in %s' % (test_dependent,)
|
||||
assert os.path.exists(PYTHON_EXE), 'The location: %s is not valid' % (PYTHON_EXE,)
|
||||
assert os.path.exists(IRONPYTHON_EXE), 'The location: %s is not valid' % (IRONPYTHON_EXE,)
|
||||
assert os.path.exists(JYTHON_JAR_LOCATION), 'The location: %s is not valid' % (JYTHON_JAR_LOCATION,)
|
||||
assert os.path.exists(JAVA_LOCATION), 'The location: %s is not valid' % (JAVA_LOCATION,)
|
||||
|
||||
if True:
|
||||
#try:
|
||||
# os.remove(r'X:\pydev\plugins\org.python.pydev\pysrc\pydevd.pyc')
|
||||
#except:
|
||||
# pass
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
# suite.addTests(unittest.makeSuite(TestJython)) # Note: Jython should be 2.2.1
|
||||
#
|
||||
# suite.addTests(unittest.makeSuite(TestIronPython))
|
||||
#
|
||||
# suite.addTests(unittest.makeSuite(TestPython))
|
||||
|
||||
|
||||
|
||||
|
||||
# suite.addTest(TestIronPython('testCase18'))
|
||||
# suite.addTest(TestIronPython('testCase17'))
|
||||
# suite.addTest(TestIronPython('testCase3'))
|
||||
# suite.addTest(TestIronPython('testCase7'))
|
||||
#
|
||||
suite.addTest(TestPython('testCaseQthread1'))
|
||||
suite.addTest(TestPython('testCaseQthread2'))
|
||||
suite.addTest(TestPython('testCaseQthread3'))
|
||||
|
||||
# suite.addTest(TestPython('testCase4'))
|
||||
|
||||
|
||||
# suite.addTest(TestJython('testCase1'))
|
||||
# suite.addTest(TestPython('testCase2'))
|
||||
# unittest.TextTestRunner(verbosity=3).run(suite)
|
||||
# suite.addTest(TestPython('testCase17'))
|
||||
# suite.addTest(TestPython('testCase18'))
|
||||
# suite.addTest(TestPython('testCase19'))
|
||||
|
||||
unittest.TextTestRunner(verbosity=3).run(suite)
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
import unittest
|
||||
import pydev_monkey
|
||||
import sys
|
||||
from pydevd import SetupHolder
|
||||
from pydev_monkey import pydev_src_dir
|
||||
|
||||
|
||||
|
||||
class TestCase(unittest.TestCase):
|
||||
|
||||
def test_monkey(self):
|
||||
check='''C:\\bin\\python.exe -u -c "
|
||||
original = SetupHolder.setup
|
||||
|
||||
try:
|
||||
SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'}
|
||||
check='''C:\\bin\\python.exe -u -c "
|
||||
connect(\\"127.0.0.1\\")
|
||||
"'''
|
||||
sys.original_argv = []
|
||||
self.assertEqual('"-u" "-c" "\nconnect(\\"127.0.0.1\\")\n"', pydev_monkey.patch_arg_str_win(check))
|
||||
sys.original_argv = []
|
||||
self.assertEqual(
|
||||
'"C:\\bin\\python.exe" "-u" "-c" "import sys; '
|
||||
'sys.path.append(r\'%s\'); '
|
||||
'import pydevd; pydevd.settrace(host=\'127.0.0.1\', port=0, suspend=False, '
|
||||
'trace_only_current_thread=False, patch_multiprocessing=True); '
|
||||
'\nconnect(\\"127.0.0.1\\")\n"' % pydev_src_dir,
|
||||
pydev_monkey.patch_arg_str_win(check)
|
||||
)
|
||||
finally:
|
||||
SetupHolder.setup = original
|
||||
|
||||
def test_str_to_args_windows(self):
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ def use_save_locals(name, value):
|
||||
save_locals(frame)
|
||||
|
||||
|
||||
def test_method(fn):
|
||||
def check_method(fn):
|
||||
"""
|
||||
A harness for testing methods that attempt to modify the values of locals on the stack.
|
||||
"""
|
||||
@@ -36,7 +36,7 @@ class TestSetLocals(unittest.TestCase):
|
||||
|
||||
|
||||
def test_set_locals_using_save_locals(self):
|
||||
x = test_method(use_save_locals)
|
||||
x = check_method(use_save_locals)
|
||||
self.assertEqual(x, 2) # Expected to succeed
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class TestSetLocals(unittest.TestCase):
|
||||
def check_co_vars(a):
|
||||
frame = sys._getframe()
|
||||
def function2():
|
||||
print a
|
||||
print(a)
|
||||
|
||||
assert 'a' in frame.f_code.co_cellvars
|
||||
frame = sys._getframe()
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
*.class
|
||||
*.pyc
|
||||
@@ -0,0 +1,2 @@
|
||||
*.class
|
||||
*.pyc
|
||||
@@ -0,0 +1,2 @@
|
||||
*.class
|
||||
*.pyc
|
||||
@@ -0,0 +1 @@
|
||||
(no __init__.py file)
|
||||
@@ -191,7 +191,7 @@ class RunfilesTest(unittest.TestCase):
|
||||
files_with_tests = [1 for t in self.all_tests if len(t._tests) > 0]
|
||||
self.assertNotEquals(len(self.files), len(files_with_tests))
|
||||
|
||||
def count_tests(self, tests):
|
||||
def count_suite(self, tests=None):
|
||||
total = 0
|
||||
for t in tests:
|
||||
total += t.countTestCases()
|
||||
@@ -207,60 +207,60 @@ class RunfilesTest(unittest.TestCase):
|
||||
|
||||
def test_finding_tests_from_modules_with_bad_filter_returns_0_tests(self):
|
||||
self._setup_scenario(self.file_dir, ["NO_TESTS_ARE_SURE_TO_HAVE_THIS_NAME"])
|
||||
self.assertEquals(0, self.count_tests(self.all_tests))
|
||||
self.assertEquals(0, self.count_suite(self.all_tests))
|
||||
|
||||
def test_finding_test_with_unique_name_returns_1_test(self):
|
||||
self._setup_scenario(self.file_dir, include_tests=["test_i_am_a_unique_test_name"])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEquals(1, self.count_tests(filtered_tests))
|
||||
self.assertEquals(1, self.count_suite(filtered_tests))
|
||||
|
||||
def test_finding_test_with_non_unique_name(self):
|
||||
self._setup_scenario(self.file_dir, include_tests=["test_non_unique_name"])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEquals(1, self.count_tests(filtered_tests) > 2)
|
||||
self.assertEquals(1, self.count_suite(filtered_tests) > 2)
|
||||
|
||||
def test_finding_tests_with_regex_filters(self):
|
||||
self._setup_scenario(self.file_dir, include_tests=["test_non*"])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEquals(1, self.count_tests(filtered_tests) > 2)
|
||||
self.assertEquals(1, self.count_suite(filtered_tests) > 2)
|
||||
|
||||
self._setup_scenario(self.file_dir, ["^$"])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEquals(0, self.count_tests(filtered_tests))
|
||||
self.assertEquals(0, self.count_suite(filtered_tests))
|
||||
|
||||
self._setup_scenario(self.file_dir, None, exclude_tests=["*"])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEquals(0, self.count_tests(filtered_tests))
|
||||
self.assertEquals(0, self.count_suite(filtered_tests))
|
||||
|
||||
def test_matching_tests(self):
|
||||
self._setup_scenario(self.file_dir, None, ['StillYetAnotherSampleTest'])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEqual(1, self.count_tests(filtered_tests))
|
||||
self.assertEqual(1, self.count_suite(filtered_tests))
|
||||
|
||||
self._setup_scenario(self.file_dir, None, ['SampleTest.test_xxxxxx1'])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEqual(1, self.count_tests(filtered_tests))
|
||||
self.assertEqual(1, self.count_suite(filtered_tests))
|
||||
|
||||
self._setup_scenario(self.file_dir, None, ['SampleTest'])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEqual(8, self.count_tests(filtered_tests))
|
||||
self.assertEqual(8, self.count_suite(filtered_tests))
|
||||
|
||||
self._setup_scenario(self.file_dir, None, ['AnotherSampleTest.todo_not_tested'])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEqual(1, self.count_tests(filtered_tests))
|
||||
self.assertEqual(1, self.count_suite(filtered_tests))
|
||||
|
||||
self._setup_scenario(self.file_dir, None, ['StillYetAnotherSampleTest', 'SampleTest.test_xxxxxx1'])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEqual(2, self.count_tests(filtered_tests))
|
||||
self.assertEqual(2, self.count_suite(filtered_tests))
|
||||
|
||||
self._setup_scenario(self.file_dir, None, exclude_tests=['*'])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEqual(self.count_tests(filtered_tests), 0)
|
||||
self.assertEqual(self.count_suite(filtered_tests), 0)
|
||||
|
||||
|
||||
self._setup_scenario(self.file_dir, None, exclude_tests=['*a*'])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEqual(self.count_tests(filtered_tests), 6)
|
||||
self.assertEqual(self.count_suite(filtered_tests), 6)
|
||||
|
||||
self.assertEqual(
|
||||
set(self.MyTestRunner.list_test_names(filtered_tests)),
|
||||
@@ -269,7 +269,7 @@ class RunfilesTest(unittest.TestCase):
|
||||
|
||||
self._setup_scenario(self.file_dir, None, exclude_tests=['*a*', '*x*'])
|
||||
filtered_tests = self.MyTestRunner.filter_tests(self.all_tests)
|
||||
self.assertEqual(self.count_tests(filtered_tests), 2)
|
||||
self.assertEqual(self.count_suite(filtered_tests), 2)
|
||||
|
||||
self.assertEqual(
|
||||
set(self.MyTestRunner.list_test_names(filtered_tests)),
|
||||
@@ -362,17 +362,43 @@ class RunfilesTest(unittest.TestCase):
|
||||
('notifyTest', 'ok', '', '', simple_test, 'SampleTest.test_xxxxxx2'),
|
||||
('notifyTest', 'ok', '', '', simple_test2, 'YetAnotherSampleTest.test_abc'),
|
||||
]
|
||||
|
||||
if not IS_JYTHON:
|
||||
expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpClass.',
|
||||
simpleClass_test.replace('/', os.path.sep), 'samples.simpleClass_test.SetUpClassTest <setUpClass>'))
|
||||
expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpModule.',
|
||||
simpleModule_test.replace('/', os.path.sep), 'samples.simpleModule_test <setUpModule>'))
|
||||
if 'samples.simpleClass_test' in str(notifications):
|
||||
expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpClass.',
|
||||
simpleClass_test.replace('/', os.path.sep), 'samples.simpleClass_test.SetUpClassTest <setUpClass>'))
|
||||
expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpModule.',
|
||||
simpleModule_test.replace('/', os.path.sep), 'samples.simpleModule_test <setUpModule>'))
|
||||
else:
|
||||
expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpClass.',
|
||||
simpleClass_test.replace('/', os.path.sep), 'simpleClass_test.SetUpClassTest <setUpClass>'))
|
||||
expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpModule.',
|
||||
simpleModule_test.replace('/', os.path.sep), 'simpleModule_test <setUpModule>'))
|
||||
else:
|
||||
expected.append(('notifyTest', 'ok', '', '', simpleClass_test, 'SetUpClassTest.test_blank'))
|
||||
expected.append(('notifyTest', 'ok', '', '', simpleModule_test, 'SetUpModuleTest.test_blank'))
|
||||
|
||||
expected.append(('notifyTestRunFinished',))
|
||||
expected.sort()
|
||||
new_notifications = []
|
||||
for notification in expected:
|
||||
try:
|
||||
if len(notification) == 6:
|
||||
# Some are binary on Py3.
|
||||
new_notifications.append((
|
||||
notification[0],
|
||||
notification[1],
|
||||
notification[2].encode('latin1'),
|
||||
notification[3].encode('latin1'),
|
||||
notification[4],
|
||||
notification[5],
|
||||
))
|
||||
else:
|
||||
new_notifications.append(notification)
|
||||
except:
|
||||
raise
|
||||
expected = new_notifications
|
||||
|
||||
notifications.sort()
|
||||
self.assertEqual(
|
||||
expected,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
+3687
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
*.pickle
|
||||
@@ -0,0 +1,158 @@
|
||||
# Grammar for 2to3. This grammar supports Python 2.x and 3.x.
|
||||
|
||||
# Note: Changing the grammar specified in this file will most likely
|
||||
# require corresponding changes in the parser module
|
||||
# (../Modules/parsermodule.c). If you can't make the changes to
|
||||
# that module yourself, please co-ordinate the required changes
|
||||
# with someone who can; ask around on python-dev for help. Fred
|
||||
# Drake <fdrake@acm.org> will probably be listening there.
|
||||
|
||||
# NOTE WELL: You should also follow all the steps listed in PEP 306,
|
||||
# "How to Change Python's Grammar"
|
||||
|
||||
# Commands for Kees Blom's railroad program
|
||||
#diagram:token NAME
|
||||
#diagram:token NUMBER
|
||||
#diagram:token STRING
|
||||
#diagram:token NEWLINE
|
||||
#diagram:token ENDMARKER
|
||||
#diagram:token INDENT
|
||||
#diagram:output\input python.bla
|
||||
#diagram:token DEDENT
|
||||
#diagram:output\textwidth 20.04cm\oddsidemargin 0.0cm\evensidemargin 0.0cm
|
||||
#diagram:rules
|
||||
|
||||
# Start symbols for the grammar:
|
||||
# file_input is a module or sequence of commands read from an input file;
|
||||
# single_input is a single interactive statement;
|
||||
# eval_input is the input for the eval() and input() functions.
|
||||
# NB: compound_stmt in single_input is followed by extra NEWLINE!
|
||||
file_input: (NEWLINE | stmt)* ENDMARKER
|
||||
single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
|
||||
eval_input: testlist NEWLINE* ENDMARKER
|
||||
|
||||
decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
|
||||
decorators: decorator+
|
||||
decorated: decorators (classdef | funcdef)
|
||||
funcdef: 'def' NAME parameters ['->' test] ':' suite
|
||||
parameters: '(' [typedargslist] ')'
|
||||
typedargslist: ((tfpdef ['=' test] ',')*
|
||||
('*' [tname] (',' tname ['=' test])* [',' '**' tname] | '**' tname)
|
||||
| tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
|
||||
tname: NAME [':' test]
|
||||
tfpdef: tname | '(' tfplist ')'
|
||||
tfplist: tfpdef (',' tfpdef)* [',']
|
||||
varargslist: ((vfpdef ['=' test] ',')*
|
||||
('*' [vname] (',' vname ['=' test])* [',' '**' vname] | '**' vname)
|
||||
| vfpdef ['=' test] (',' vfpdef ['=' test])* [','])
|
||||
vname: NAME
|
||||
vfpdef: vname | '(' vfplist ')'
|
||||
vfplist: vfpdef (',' vfpdef)* [',']
|
||||
|
||||
stmt: simple_stmt | compound_stmt
|
||||
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
|
||||
small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
|
||||
import_stmt | global_stmt | exec_stmt | assert_stmt)
|
||||
expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) |
|
||||
('=' (yield_expr|testlist_star_expr))*)
|
||||
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
|
||||
augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
|
||||
'<<=' | '>>=' | '**=' | '//=')
|
||||
# For normal assignments, additional restrictions enforced by the interpreter
|
||||
print_stmt: 'print' ( [ test (',' test)* [','] ] |
|
||||
'>>' test [ (',' test)+ [','] ] )
|
||||
del_stmt: 'del' exprlist
|
||||
pass_stmt: 'pass'
|
||||
flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
|
||||
break_stmt: 'break'
|
||||
continue_stmt: 'continue'
|
||||
return_stmt: 'return' [testlist]
|
||||
yield_stmt: yield_expr
|
||||
raise_stmt: 'raise' [test ['from' test | ',' test [',' test]]]
|
||||
import_stmt: import_name | import_from
|
||||
import_name: 'import' dotted_as_names
|
||||
import_from: ('from' ('.'* dotted_name | '.'+)
|
||||
'import' ('*' | '(' import_as_names ')' | import_as_names))
|
||||
import_as_name: NAME ['as' NAME]
|
||||
dotted_as_name: dotted_name ['as' NAME]
|
||||
import_as_names: import_as_name (',' import_as_name)* [',']
|
||||
dotted_as_names: dotted_as_name (',' dotted_as_name)*
|
||||
dotted_name: NAME ('.' NAME)*
|
||||
global_stmt: ('global' | 'nonlocal') NAME (',' NAME)*
|
||||
exec_stmt: 'exec' expr ['in' test [',' test]]
|
||||
assert_stmt: 'assert' test [',' test]
|
||||
|
||||
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
|
||||
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
|
||||
while_stmt: 'while' test ':' suite ['else' ':' suite]
|
||||
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
|
||||
try_stmt: ('try' ':' suite
|
||||
((except_clause ':' suite)+
|
||||
['else' ':' suite]
|
||||
['finally' ':' suite] |
|
||||
'finally' ':' suite))
|
||||
with_stmt: 'with' with_item (',' with_item)* ':' suite
|
||||
with_item: test ['as' expr]
|
||||
with_var: 'as' expr
|
||||
# NB compile.c makes sure that the default except clause is last
|
||||
except_clause: 'except' [test [(',' | 'as') test]]
|
||||
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
|
||||
|
||||
# Backward compatibility cruft to support:
|
||||
# [ x for x in lambda: True, lambda: False if x() ]
|
||||
# even while also allowing:
|
||||
# lambda x: 5 if x else 2
|
||||
# (But not a mix of the two)
|
||||
testlist_safe: old_test [(',' old_test)+ [',']]
|
||||
old_test: or_test | old_lambdef
|
||||
old_lambdef: 'lambda' [varargslist] ':' old_test
|
||||
|
||||
test: or_test ['if' or_test 'else' test] | lambdef
|
||||
or_test: and_test ('or' and_test)*
|
||||
and_test: not_test ('and' not_test)*
|
||||
not_test: 'not' not_test | comparison
|
||||
comparison: expr (comp_op expr)*
|
||||
comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
|
||||
star_expr: '*' expr
|
||||
expr: xor_expr ('|' xor_expr)*
|
||||
xor_expr: and_expr ('^' and_expr)*
|
||||
and_expr: shift_expr ('&' shift_expr)*
|
||||
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
|
||||
arith_expr: term (('+'|'-') term)*
|
||||
term: factor (('*'|'/'|'%'|'//') factor)*
|
||||
factor: ('+'|'-'|'~') factor | power
|
||||
power: atom trailer* ['**' factor]
|
||||
atom: ('(' [yield_expr|testlist_gexp] ')' |
|
||||
'[' [listmaker] ']' |
|
||||
'{' [dictsetmaker] '}' |
|
||||
'`' testlist1 '`' |
|
||||
NAME | NUMBER | STRING+ | '.' '.' '.')
|
||||
listmaker: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
|
||||
testlist_gexp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
|
||||
lambdef: 'lambda' [varargslist] ':' test
|
||||
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
|
||||
subscriptlist: subscript (',' subscript)* [',']
|
||||
subscript: test | [test] ':' [test] [sliceop]
|
||||
sliceop: ':' [test]
|
||||
exprlist: (expr|star_expr) (',' (expr|star_expr))* [',']
|
||||
testlist: test (',' test)* [',']
|
||||
dictsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) |
|
||||
(test (comp_for | (',' test)* [','])) )
|
||||
|
||||
classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
|
||||
|
||||
arglist: (argument ',')* (argument [',']
|
||||
|'*' test (',' argument)* [',' '**' test]
|
||||
|'**' test)
|
||||
argument: test [comp_for] | test '=' test # Really [keyword '='] test
|
||||
|
||||
comp_iter: comp_for | comp_if
|
||||
comp_for: 'for' exprlist 'in' testlist_safe [comp_iter]
|
||||
comp_if: 'if' old_test [comp_iter]
|
||||
|
||||
testlist1: test (',' test)*
|
||||
|
||||
# not used in grammar, but may appear in "node" passed from Parser to Compiler
|
||||
encoding_decl: NAME
|
||||
|
||||
yield_expr: 'yield' [testlist]
|
||||
@@ -0,0 +1,28 @@
|
||||
# Copyright 2006 Google, Inc. All Rights Reserved.
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
# A grammar to describe tree matching patterns.
|
||||
# Not shown here:
|
||||
# - 'TOKEN' stands for any token (leaf node)
|
||||
# - 'any' stands for any node (leaf or interior)
|
||||
# With 'any' we can still specify the sub-structure.
|
||||
|
||||
# The start symbol is 'Matcher'.
|
||||
|
||||
Matcher: Alternatives ENDMARKER
|
||||
|
||||
Alternatives: Alternative ('|' Alternative)*
|
||||
|
||||
Alternative: (Unit | NegatedUnit)+
|
||||
|
||||
Unit: [NAME '='] ( STRING [Repeater]
|
||||
| NAME [Details] [Repeater]
|
||||
| '(' Alternatives ')' [Repeater]
|
||||
| '[' Alternatives ']'
|
||||
)
|
||||
|
||||
NegatedUnit: 'not' (STRING | NAME [Details] | '(' Alternatives ')')
|
||||
|
||||
Repeater: '*' | '+' | '{' NUMBER [',' NUMBER] '}'
|
||||
|
||||
Details: '<' Alternatives '>'
|
||||
@@ -0,0 +1 @@
|
||||
#empty
|
||||
@@ -0,0 +1,4 @@
|
||||
import sys
|
||||
from .main import main
|
||||
|
||||
sys.exit(main("lib2to3.fixes"))
|
||||
@@ -0,0 +1,168 @@
|
||||
"""A bottom-up tree matching algorithm implementation meant to speed
|
||||
up 2to3's matching process. After the tree patterns are reduced to
|
||||
their rarest linear path, a linear Aho-Corasick automaton is
|
||||
created. The linear automaton traverses the linear paths from the
|
||||
leaves to the root of the AST and returns a set of nodes for further
|
||||
matching. This reduces significantly the number of candidate nodes."""
|
||||
|
||||
__author__ = "George Boutsioukis <gboutsioukis@gmail.com>"
|
||||
|
||||
import logging
|
||||
import itertools
|
||||
from collections import defaultdict
|
||||
|
||||
from . import pytree
|
||||
from .btm_utils import reduce_tree
|
||||
|
||||
class BMNode(object):
|
||||
"""Class for a node of the Aho-Corasick automaton used in matching"""
|
||||
count = itertools.count()
|
||||
def __init__(self):
|
||||
self.transition_table = {}
|
||||
self.fixers = []
|
||||
self.id = next(BMNode.count)
|
||||
self.content = ''
|
||||
|
||||
class BottomMatcher(object):
|
||||
"""The main matcher class. After instantiating the patterns should
|
||||
be added using the add_fixer method"""
|
||||
|
||||
def __init__(self):
|
||||
self.match = set()
|
||||
self.root = BMNode()
|
||||
self.nodes = [self.root]
|
||||
self.fixers = []
|
||||
self.logger = logging.getLogger("RefactoringTool")
|
||||
|
||||
def add_fixer(self, fixer):
|
||||
"""Reduces a fixer's pattern tree to a linear path and adds it
|
||||
to the matcher(a common Aho-Corasick automaton). The fixer is
|
||||
appended on the matching states and called when they are
|
||||
reached"""
|
||||
self.fixers.append(fixer)
|
||||
tree = reduce_tree(fixer.pattern_tree)
|
||||
linear = tree.get_linear_subpattern()
|
||||
match_nodes = self.add(linear, start=self.root)
|
||||
for match_node in match_nodes:
|
||||
match_node.fixers.append(fixer)
|
||||
|
||||
def add(self, pattern, start):
|
||||
"Recursively adds a linear pattern to the AC automaton"
|
||||
#print("adding pattern", pattern, "to", start)
|
||||
if not pattern:
|
||||
#print("empty pattern")
|
||||
return [start]
|
||||
if isinstance(pattern[0], tuple):
|
||||
#alternatives
|
||||
#print("alternatives")
|
||||
match_nodes = []
|
||||
for alternative in pattern[0]:
|
||||
#add all alternatives, and add the rest of the pattern
|
||||
#to each end node
|
||||
end_nodes = self.add(alternative, start=start)
|
||||
for end in end_nodes:
|
||||
match_nodes.extend(self.add(pattern[1:], end))
|
||||
return match_nodes
|
||||
else:
|
||||
#single token
|
||||
#not last
|
||||
if pattern[0] not in start.transition_table:
|
||||
#transition did not exist, create new
|
||||
next_node = BMNode()
|
||||
start.transition_table[pattern[0]] = next_node
|
||||
else:
|
||||
#transition exists already, follow
|
||||
next_node = start.transition_table[pattern[0]]
|
||||
|
||||
if pattern[1:]:
|
||||
end_nodes = self.add(pattern[1:], start=next_node)
|
||||
else:
|
||||
end_nodes = [next_node]
|
||||
return end_nodes
|
||||
|
||||
def run(self, leaves):
|
||||
"""The main interface with the bottom matcher. The tree is
|
||||
traversed from the bottom using the constructed
|
||||
automaton. Nodes are only checked once as the tree is
|
||||
retraversed. When the automaton fails, we give it one more
|
||||
shot(in case the above tree matches as a whole with the
|
||||
rejected leaf), then we break for the next leaf. There is the
|
||||
special case of multiple arguments(see code comments) where we
|
||||
recheck the nodes
|
||||
|
||||
Args:
|
||||
The leaves of the AST tree to be matched
|
||||
|
||||
Returns:
|
||||
A dictionary of node matches with fixers as the keys
|
||||
"""
|
||||
current_ac_node = self.root
|
||||
results = defaultdict(list)
|
||||
for leaf in leaves:
|
||||
current_ast_node = leaf
|
||||
while current_ast_node:
|
||||
current_ast_node.was_checked = True
|
||||
for child in current_ast_node.children:
|
||||
# multiple statements, recheck
|
||||
if isinstance(child, pytree.Leaf) and child.value == u";":
|
||||
current_ast_node.was_checked = False
|
||||
break
|
||||
if current_ast_node.type == 1:
|
||||
#name
|
||||
node_token = current_ast_node.value
|
||||
else:
|
||||
node_token = current_ast_node.type
|
||||
|
||||
if node_token in current_ac_node.transition_table:
|
||||
#token matches
|
||||
current_ac_node = current_ac_node.transition_table[node_token]
|
||||
for fixer in current_ac_node.fixers:
|
||||
if not fixer in results:
|
||||
results[fixer] = []
|
||||
results[fixer].append(current_ast_node)
|
||||
|
||||
else:
|
||||
#matching failed, reset automaton
|
||||
current_ac_node = self.root
|
||||
if (current_ast_node.parent is not None
|
||||
and current_ast_node.parent.was_checked):
|
||||
#the rest of the tree upwards has been checked, next leaf
|
||||
break
|
||||
|
||||
#recheck the rejected node once from the root
|
||||
if node_token in current_ac_node.transition_table:
|
||||
#token matches
|
||||
current_ac_node = current_ac_node.transition_table[node_token]
|
||||
for fixer in current_ac_node.fixers:
|
||||
if not fixer in results.keys():
|
||||
results[fixer] = []
|
||||
results[fixer].append(current_ast_node)
|
||||
|
||||
current_ast_node = current_ast_node.parent
|
||||
return results
|
||||
|
||||
def print_ac(self):
|
||||
"Prints a graphviz diagram of the BM automaton(for debugging)"
|
||||
print("digraph g{")
|
||||
def print_node(node):
|
||||
for subnode_key in node.transition_table.keys():
|
||||
subnode = node.transition_table[subnode_key]
|
||||
print("%d -> %d [label=%s] //%s" %
|
||||
(node.id, subnode.id, type_repr(subnode_key), str(subnode.fixers)))
|
||||
if subnode_key == 1:
|
||||
print(subnode.content)
|
||||
print_node(subnode)
|
||||
print_node(self.root)
|
||||
print("}")
|
||||
|
||||
# taken from pytree.py for debugging; only used by print_ac
|
||||
_type_reprs = {}
|
||||
def type_repr(type_num):
|
||||
global _type_reprs
|
||||
if not _type_reprs:
|
||||
from .pygram import python_symbols
|
||||
# printing tokens is possible but not as useful
|
||||
# from .pgen2 import token // token.__dict__.items():
|
||||
for name, val in python_symbols.__dict__.items():
|
||||
if type(val) == int: _type_reprs[val] = name
|
||||
return _type_reprs.setdefault(type_num, type_num)
|
||||
@@ -0,0 +1,283 @@
|
||||
"Utility functions used by the btm_matcher module"
|
||||
|
||||
from . import pytree
|
||||
from .pgen2 import grammar, token
|
||||
from .pygram import pattern_symbols, python_symbols
|
||||
|
||||
syms = pattern_symbols
|
||||
pysyms = python_symbols
|
||||
tokens = grammar.opmap
|
||||
token_labels = token
|
||||
|
||||
TYPE_ANY = -1
|
||||
TYPE_ALTERNATIVES = -2
|
||||
TYPE_GROUP = -3
|
||||
|
||||
class MinNode(object):
|
||||
"""This class serves as an intermediate representation of the
|
||||
pattern tree during the conversion to sets of leaf-to-root
|
||||
subpatterns"""
|
||||
|
||||
def __init__(self, type=None, name=None):
|
||||
self.type = type
|
||||
self.name = name
|
||||
self.children = []
|
||||
self.leaf = False
|
||||
self.parent = None
|
||||
self.alternatives = []
|
||||
self.group = []
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.type) + ' ' + str(self.name)
|
||||
|
||||
def leaf_to_root(self):
|
||||
"""Internal method. Returns a characteristic path of the
|
||||
pattern tree. This method must be run for all leaves until the
|
||||
linear subpatterns are merged into a single"""
|
||||
node = self
|
||||
subp = []
|
||||
while node:
|
||||
if node.type == TYPE_ALTERNATIVES:
|
||||
node.alternatives.append(subp)
|
||||
if len(node.alternatives) == len(node.children):
|
||||
#last alternative
|
||||
subp = [tuple(node.alternatives)]
|
||||
node.alternatives = []
|
||||
node = node.parent
|
||||
continue
|
||||
else:
|
||||
node = node.parent
|
||||
subp = None
|
||||
break
|
||||
|
||||
if node.type == TYPE_GROUP:
|
||||
node.group.append(subp)
|
||||
#probably should check the number of leaves
|
||||
if len(node.group) == len(node.children):
|
||||
subp = get_characteristic_subpattern(node.group)
|
||||
node.group = []
|
||||
node = node.parent
|
||||
continue
|
||||
else:
|
||||
node = node.parent
|
||||
subp = None
|
||||
break
|
||||
|
||||
if node.type == token_labels.NAME and node.name:
|
||||
#in case of type=name, use the name instead
|
||||
subp.append(node.name)
|
||||
else:
|
||||
subp.append(node.type)
|
||||
|
||||
node = node.parent
|
||||
return subp
|
||||
|
||||
def get_linear_subpattern(self):
|
||||
"""Drives the leaf_to_root method. The reason that
|
||||
leaf_to_root must be run multiple times is because we need to
|
||||
reject 'group' matches; for example the alternative form
|
||||
(a | b c) creates a group [b c] that needs to be matched. Since
|
||||
matching multiple linear patterns overcomes the automaton's
|
||||
capabilities, leaf_to_root merges each group into a single
|
||||
choice based on 'characteristic'ity,
|
||||
|
||||
i.e. (a|b c) -> (a|b) if b more characteristic than c
|
||||
|
||||
Returns: The most 'characteristic'(as defined by
|
||||
get_characteristic_subpattern) path for the compiled pattern
|
||||
tree.
|
||||
"""
|
||||
|
||||
for l in self.leaves():
|
||||
subp = l.leaf_to_root()
|
||||
if subp:
|
||||
return subp
|
||||
|
||||
def leaves(self):
|
||||
"Generator that returns the leaves of the tree"
|
||||
for child in self.children:
|
||||
for x in child.leaves():
|
||||
yield x
|
||||
if not self.children:
|
||||
yield self
|
||||
|
||||
def reduce_tree(node, parent=None):
|
||||
"""
|
||||
Internal function. Reduces a compiled pattern tree to an
|
||||
intermediate representation suitable for feeding the
|
||||
automaton. This also trims off any optional pattern elements(like
|
||||
[a], a*).
|
||||
"""
|
||||
|
||||
new_node = None
|
||||
#switch on the node type
|
||||
if node.type == syms.Matcher:
|
||||
#skip
|
||||
node = node.children[0]
|
||||
|
||||
if node.type == syms.Alternatives :
|
||||
#2 cases
|
||||
if len(node.children) <= 2:
|
||||
#just a single 'Alternative', skip this node
|
||||
new_node = reduce_tree(node.children[0], parent)
|
||||
else:
|
||||
#real alternatives
|
||||
new_node = MinNode(type=TYPE_ALTERNATIVES)
|
||||
#skip odd children('|' tokens)
|
||||
for child in node.children:
|
||||
if node.children.index(child)%2:
|
||||
continue
|
||||
reduced = reduce_tree(child, new_node)
|
||||
if reduced is not None:
|
||||
new_node.children.append(reduced)
|
||||
elif node.type == syms.Alternative:
|
||||
if len(node.children) > 1:
|
||||
|
||||
new_node = MinNode(type=TYPE_GROUP)
|
||||
for child in node.children:
|
||||
reduced = reduce_tree(child, new_node)
|
||||
if reduced:
|
||||
new_node.children.append(reduced)
|
||||
if not new_node.children:
|
||||
# delete the group if all of the children were reduced to None
|
||||
new_node = None
|
||||
|
||||
else:
|
||||
new_node = reduce_tree(node.children[0], parent)
|
||||
|
||||
elif node.type == syms.Unit:
|
||||
if (isinstance(node.children[0], pytree.Leaf) and
|
||||
node.children[0].value == '('):
|
||||
#skip parentheses
|
||||
return reduce_tree(node.children[1], parent)
|
||||
if ((isinstance(node.children[0], pytree.Leaf) and
|
||||
node.children[0].value == '[')
|
||||
or
|
||||
(len(node.children)>1 and
|
||||
hasattr(node.children[1], "value") and
|
||||
node.children[1].value == '[')):
|
||||
#skip whole unit if its optional
|
||||
return None
|
||||
|
||||
leaf = True
|
||||
details_node = None
|
||||
alternatives_node = None
|
||||
has_repeater = False
|
||||
repeater_node = None
|
||||
has_variable_name = False
|
||||
|
||||
for child in node.children:
|
||||
if child.type == syms.Details:
|
||||
leaf = False
|
||||
details_node = child
|
||||
elif child.type == syms.Repeater:
|
||||
has_repeater = True
|
||||
repeater_node = child
|
||||
elif child.type == syms.Alternatives:
|
||||
alternatives_node = child
|
||||
if hasattr(child, 'value') and child.value == '=': # variable name
|
||||
has_variable_name = True
|
||||
|
||||
#skip variable name
|
||||
if has_variable_name:
|
||||
#skip variable name, '='
|
||||
name_leaf = node.children[2]
|
||||
if hasattr(name_leaf, 'value') and name_leaf.value == '(':
|
||||
# skip parenthesis
|
||||
name_leaf = node.children[3]
|
||||
else:
|
||||
name_leaf = node.children[0]
|
||||
|
||||
#set node type
|
||||
if name_leaf.type == token_labels.NAME:
|
||||
#(python) non-name or wildcard
|
||||
if name_leaf.value == 'any':
|
||||
new_node = MinNode(type=TYPE_ANY)
|
||||
else:
|
||||
if hasattr(token_labels, name_leaf.value):
|
||||
new_node = MinNode(type=getattr(token_labels, name_leaf.value))
|
||||
else:
|
||||
new_node = MinNode(type=getattr(pysyms, name_leaf.value))
|
||||
|
||||
elif name_leaf.type == token_labels.STRING:
|
||||
#(python) name or character; remove the apostrophes from
|
||||
#the string value
|
||||
name = name_leaf.value.strip("'")
|
||||
if name in tokens:
|
||||
new_node = MinNode(type=tokens[name])
|
||||
else:
|
||||
new_node = MinNode(type=token_labels.NAME, name=name)
|
||||
elif name_leaf.type == syms.Alternatives:
|
||||
new_node = reduce_tree(alternatives_node, parent)
|
||||
|
||||
#handle repeaters
|
||||
if has_repeater:
|
||||
if repeater_node.children[0].value == '*':
|
||||
#reduce to None
|
||||
new_node = None
|
||||
elif repeater_node.children[0].value == '+':
|
||||
#reduce to a single occurence i.e. do nothing
|
||||
pass
|
||||
else:
|
||||
#TODO: handle {min, max} repeaters
|
||||
raise NotImplementedError
|
||||
pass
|
||||
|
||||
#add children
|
||||
if details_node and new_node is not None:
|
||||
for child in details_node.children[1:-1]:
|
||||
#skip '<', '>' markers
|
||||
reduced = reduce_tree(child, new_node)
|
||||
if reduced is not None:
|
||||
new_node.children.append(reduced)
|
||||
if new_node:
|
||||
new_node.parent = parent
|
||||
return new_node
|
||||
|
||||
|
||||
def get_characteristic_subpattern(subpatterns):
|
||||
"""Picks the most characteristic from a list of linear patterns
|
||||
Current order used is:
|
||||
names > common_names > common_chars
|
||||
"""
|
||||
if not isinstance(subpatterns, list):
|
||||
return subpatterns
|
||||
if len(subpatterns)==1:
|
||||
return subpatterns[0]
|
||||
|
||||
# first pick out the ones containing variable names
|
||||
subpatterns_with_names = []
|
||||
subpatterns_with_common_names = []
|
||||
common_names = ['in', 'for', 'if' , 'not', 'None']
|
||||
subpatterns_with_common_chars = []
|
||||
common_chars = "[]().,:"
|
||||
for subpattern in subpatterns:
|
||||
if any(rec_test(subpattern, lambda x: type(x) is str)):
|
||||
if any(rec_test(subpattern,
|
||||
lambda x: isinstance(x, str) and x in common_chars)):
|
||||
subpatterns_with_common_chars.append(subpattern)
|
||||
elif any(rec_test(subpattern,
|
||||
lambda x: isinstance(x, str) and x in common_names)):
|
||||
subpatterns_with_common_names.append(subpattern)
|
||||
|
||||
else:
|
||||
subpatterns_with_names.append(subpattern)
|
||||
|
||||
if subpatterns_with_names:
|
||||
subpatterns = subpatterns_with_names
|
||||
elif subpatterns_with_common_names:
|
||||
subpatterns = subpatterns_with_common_names
|
||||
elif subpatterns_with_common_chars:
|
||||
subpatterns = subpatterns_with_common_chars
|
||||
# of the remaining subpatterns pick out the longest one
|
||||
return max(subpatterns, key=len)
|
||||
|
||||
def rec_test(sequence, test_func):
|
||||
"""Tests test_func on all items of sequence and items of included
|
||||
sub-iterables"""
|
||||
for x in sequence:
|
||||
if isinstance(x, (list, tuple)):
|
||||
for y in rec_test(x, test_func):
|
||||
yield y
|
||||
else:
|
||||
yield test_func(x)
|
||||
@@ -0,0 +1,189 @@
|
||||
# Copyright 2006 Google, Inc. All Rights Reserved.
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
"""Base class for fixers (optional, but recommended)."""
|
||||
|
||||
# Python imports
|
||||
import logging
|
||||
import itertools
|
||||
|
||||
# Local imports
|
||||
from .patcomp import PatternCompiler
|
||||
from . import pygram
|
||||
from .fixer_util import does_tree_import
|
||||
|
||||
class BaseFix(object):
|
||||
|
||||
"""Optional base class for fixers.
|
||||
|
||||
The subclass name must be FixFooBar where FooBar is the result of
|
||||
removing underscores and capitalizing the words of the fix name.
|
||||
For example, the class name for a fixer named 'has_key' should be
|
||||
FixHasKey.
|
||||
"""
|
||||
|
||||
PATTERN = None # Most subclasses should override with a string literal
|
||||
pattern = None # Compiled pattern, set by compile_pattern()
|
||||
pattern_tree = None # Tree representation of the pattern
|
||||
options = None # Options object passed to initializer
|
||||
filename = None # The filename (set by set_filename)
|
||||
logger = None # A logger (set by set_filename)
|
||||
numbers = itertools.count(1) # For new_name()
|
||||
used_names = set() # A set of all used NAMEs
|
||||
order = "post" # Does the fixer prefer pre- or post-order traversal
|
||||
explicit = False # Is this ignored by refactor.py -f all?
|
||||
run_order = 5 # Fixers will be sorted by run order before execution
|
||||
# Lower numbers will be run first.
|
||||
_accept_type = None # [Advanced and not public] This tells RefactoringTool
|
||||
# which node type to accept when there's not a pattern.
|
||||
|
||||
keep_line_order = False # For the bottom matcher: match with the
|
||||
# original line order
|
||||
BM_compatible = False # Compatibility with the bottom matching
|
||||
# module; every fixer should set this
|
||||
# manually
|
||||
|
||||
# Shortcut for access to Python grammar symbols
|
||||
syms = pygram.python_symbols
|
||||
|
||||
def __init__(self, options, log):
|
||||
"""Initializer. Subclass may override.
|
||||
|
||||
Args:
|
||||
options: an dict containing the options passed to RefactoringTool
|
||||
that could be used to customize the fixer through the command line.
|
||||
log: a list to append warnings and other messages to.
|
||||
"""
|
||||
self.options = options
|
||||
self.log = log
|
||||
self.compile_pattern()
|
||||
|
||||
def compile_pattern(self):
|
||||
"""Compiles self.PATTERN into self.pattern.
|
||||
|
||||
Subclass may override if it doesn't want to use
|
||||
self.{pattern,PATTERN} in .match().
|
||||
"""
|
||||
if self.PATTERN is not None:
|
||||
PC = PatternCompiler()
|
||||
self.pattern, self.pattern_tree = PC.compile_pattern(self.PATTERN,
|
||||
with_tree=True)
|
||||
|
||||
def set_filename(self, filename):
|
||||
"""Set the filename, and a logger derived from it.
|
||||
|
||||
The main refactoring tool should call this.
|
||||
"""
|
||||
self.filename = filename
|
||||
self.logger = logging.getLogger(filename)
|
||||
|
||||
def match(self, node):
|
||||
"""Returns match for a given parse tree node.
|
||||
|
||||
Should return a true or false object (not necessarily a bool).
|
||||
It may return a non-empty dict of matching sub-nodes as
|
||||
returned by a matching pattern.
|
||||
|
||||
Subclass may override.
|
||||
"""
|
||||
results = {"node": node}
|
||||
return self.pattern.match(node, results) and results
|
||||
|
||||
def transform(self, node, results):
|
||||
"""Returns the transformation for a given parse tree node.
|
||||
|
||||
Args:
|
||||
node: the root of the parse tree that matched the fixer.
|
||||
results: a dict mapping symbolic names to part of the match.
|
||||
|
||||
Returns:
|
||||
None, or a node that is a modified copy of the
|
||||
argument node. The node argument may also be modified in-place to
|
||||
effect the same change.
|
||||
|
||||
Subclass *must* override.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def new_name(self, template=u"xxx_todo_changeme"):
|
||||
"""Return a string suitable for use as an identifier
|
||||
|
||||
The new name is guaranteed not to conflict with other identifiers.
|
||||
"""
|
||||
name = template
|
||||
while name in self.used_names:
|
||||
name = template + unicode(self.numbers.next())
|
||||
self.used_names.add(name)
|
||||
return name
|
||||
|
||||
def log_message(self, message):
|
||||
if self.first_log:
|
||||
self.first_log = False
|
||||
self.log.append("### In file %s ###" % self.filename)
|
||||
self.log.append(message)
|
||||
|
||||
def cannot_convert(self, node, reason=None):
|
||||
"""Warn the user that a given chunk of code is not valid Python 3,
|
||||
but that it cannot be converted automatically.
|
||||
|
||||
First argument is the top-level node for the code in question.
|
||||
Optional second argument is why it can't be converted.
|
||||
"""
|
||||
lineno = node.get_lineno()
|
||||
for_output = node.clone()
|
||||
for_output.prefix = u""
|
||||
msg = "Line %d: could not convert: %s"
|
||||
self.log_message(msg % (lineno, for_output))
|
||||
if reason:
|
||||
self.log_message(reason)
|
||||
|
||||
def warning(self, node, reason):
|
||||
"""Used for warning the user about possible uncertainty in the
|
||||
translation.
|
||||
|
||||
First argument is the top-level node for the code in question.
|
||||
Optional second argument is why it can't be converted.
|
||||
"""
|
||||
lineno = node.get_lineno()
|
||||
self.log_message("Line %d: %s" % (lineno, reason))
|
||||
|
||||
def start_tree(self, tree, filename):
|
||||
"""Some fixers need to maintain tree-wide state.
|
||||
This method is called once, at the start of tree fix-up.
|
||||
|
||||
tree - the root node of the tree to be processed.
|
||||
filename - the name of the file the tree came from.
|
||||
"""
|
||||
self.used_names = tree.used_names
|
||||
self.set_filename(filename)
|
||||
self.numbers = itertools.count(1)
|
||||
self.first_log = True
|
||||
|
||||
def finish_tree(self, tree, filename):
|
||||
"""Some fixers need to maintain tree-wide state.
|
||||
This method is called once, at the conclusion of tree fix-up.
|
||||
|
||||
tree - the root node of the tree to be processed.
|
||||
filename - the name of the file the tree came from.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ConditionalFix(BaseFix):
|
||||
""" Base class for fixers which not execute if an import is found. """
|
||||
|
||||
# This is the name of the import which, if found, will cause the test to be skipped
|
||||
skip_on = None
|
||||
|
||||
def start_tree(self, *args):
|
||||
super(ConditionalFix, self).start_tree(*args)
|
||||
self._should_skip = None
|
||||
|
||||
def should_skip(self, node):
|
||||
if self._should_skip is not None:
|
||||
return self._should_skip
|
||||
pkg = self.skip_on.split(".")
|
||||
name = pkg[-1]
|
||||
pkg = ".".join(pkg[:-1])
|
||||
self._should_skip = does_tree_import(pkg, name, node)
|
||||
return self._should_skip
|
||||
@@ -0,0 +1,430 @@
|
||||
"""Utility functions, node construction macros, etc."""
|
||||
# Author: Collin Winter
|
||||
|
||||
# Local imports
|
||||
from .pgen2 import token
|
||||
from .pytree import Leaf, Node
|
||||
from .pygram import python_symbols as syms
|
||||
from . import patcomp
|
||||
|
||||
|
||||
###########################################################
|
||||
### Common node-construction "macros"
|
||||
###########################################################
|
||||
|
||||
def KeywordArg(keyword, value):
|
||||
return Node(syms.argument,
|
||||
[keyword, Leaf(token.EQUAL, u"="), value])
|
||||
|
||||
def LParen():
|
||||
return Leaf(token.LPAR, u"(")
|
||||
|
||||
def RParen():
|
||||
return Leaf(token.RPAR, u")")
|
||||
|
||||
def Assign(target, source):
|
||||
"""Build an assignment statement"""
|
||||
if not isinstance(target, list):
|
||||
target = [target]
|
||||
if not isinstance(source, list):
|
||||
source.prefix = u" "
|
||||
source = [source]
|
||||
|
||||
return Node(syms.atom,
|
||||
target + [Leaf(token.EQUAL, u"=", prefix=u" ")] + source)
|
||||
|
||||
def Name(name, prefix=None):
|
||||
"""Return a NAME leaf"""
|
||||
return Leaf(token.NAME, name, prefix=prefix)
|
||||
|
||||
def Attr(obj, attr):
|
||||
"""A node tuple for obj.attr"""
|
||||
return [obj, Node(syms.trailer, [Dot(), attr])]
|
||||
|
||||
def Comma():
|
||||
"""A comma leaf"""
|
||||
return Leaf(token.COMMA, u",")
|
||||
|
||||
def Dot():
|
||||
"""A period (.) leaf"""
|
||||
return Leaf(token.DOT, u".")
|
||||
|
||||
def ArgList(args, lparen=LParen(), rparen=RParen()):
|
||||
"""A parenthesised argument list, used by Call()"""
|
||||
node = Node(syms.trailer, [lparen.clone(), rparen.clone()])
|
||||
if args:
|
||||
node.insert_child(1, Node(syms.arglist, args))
|
||||
return node
|
||||
|
||||
def Call(func_name, args=None, prefix=None):
|
||||
"""A function call"""
|
||||
node = Node(syms.power, [func_name, ArgList(args)])
|
||||
if prefix is not None:
|
||||
node.prefix = prefix
|
||||
return node
|
||||
|
||||
def Newline():
|
||||
"""A newline literal"""
|
||||
return Leaf(token.NEWLINE, u"\n")
|
||||
|
||||
def BlankLine():
|
||||
"""A blank line"""
|
||||
return Leaf(token.NEWLINE, u"")
|
||||
|
||||
def Number(n, prefix=None):
|
||||
return Leaf(token.NUMBER, n, prefix=prefix)
|
||||
|
||||
def Subscript(index_node):
|
||||
"""A numeric or string subscript"""
|
||||
return Node(syms.trailer, [Leaf(token.LBRACE, u"["),
|
||||
index_node,
|
||||
Leaf(token.RBRACE, u"]")])
|
||||
|
||||
def String(string, prefix=None):
|
||||
"""A string leaf"""
|
||||
return Leaf(token.STRING, string, prefix=prefix)
|
||||
|
||||
def ListComp(xp, fp, it, test=None):
|
||||
"""A list comprehension of the form [xp for fp in it if test].
|
||||
|
||||
If test is None, the "if test" part is omitted.
|
||||
"""
|
||||
xp.prefix = u""
|
||||
fp.prefix = u" "
|
||||
it.prefix = u" "
|
||||
for_leaf = Leaf(token.NAME, u"for")
|
||||
for_leaf.prefix = u" "
|
||||
in_leaf = Leaf(token.NAME, u"in")
|
||||
in_leaf.prefix = u" "
|
||||
inner_args = [for_leaf, fp, in_leaf, it]
|
||||
if test:
|
||||
test.prefix = u" "
|
||||
if_leaf = Leaf(token.NAME, u"if")
|
||||
if_leaf.prefix = u" "
|
||||
inner_args.append(Node(syms.comp_if, [if_leaf, test]))
|
||||
inner = Node(syms.listmaker, [xp, Node(syms.comp_for, inner_args)])
|
||||
return Node(syms.atom,
|
||||
[Leaf(token.LBRACE, u"["),
|
||||
inner,
|
||||
Leaf(token.RBRACE, u"]")])
|
||||
|
||||
def FromImport(package_name, name_leafs):
|
||||
""" Return an import statement in the form:
|
||||
from package import name_leafs"""
|
||||
# XXX: May not handle dotted imports properly (eg, package_name='foo.bar')
|
||||
#assert package_name == '.' or '.' not in package_name, "FromImport has "\
|
||||
# "not been tested with dotted package names -- use at your own "\
|
||||
# "peril!"
|
||||
|
||||
for leaf in name_leafs:
|
||||
# Pull the leaves out of their old tree
|
||||
leaf.remove()
|
||||
|
||||
children = [Leaf(token.NAME, u"from"),
|
||||
Leaf(token.NAME, package_name, prefix=u" "),
|
||||
Leaf(token.NAME, u"import", prefix=u" "),
|
||||
Node(syms.import_as_names, name_leafs)]
|
||||
imp = Node(syms.import_from, children)
|
||||
return imp
|
||||
|
||||
|
||||
###########################################################
|
||||
### Determine whether a node represents a given literal
|
||||
###########################################################
|
||||
|
||||
def is_tuple(node):
|
||||
"""Does the node represent a tuple literal?"""
|
||||
if isinstance(node, Node) and node.children == [LParen(), RParen()]:
|
||||
return True
|
||||
return (isinstance(node, Node)
|
||||
and len(node.children) == 3
|
||||
and isinstance(node.children[0], Leaf)
|
||||
and isinstance(node.children[1], Node)
|
||||
and isinstance(node.children[2], Leaf)
|
||||
and node.children[0].value == u"("
|
||||
and node.children[2].value == u")")
|
||||
|
||||
def is_list(node):
|
||||
"""Does the node represent a list literal?"""
|
||||
return (isinstance(node, Node)
|
||||
and len(node.children) > 1
|
||||
and isinstance(node.children[0], Leaf)
|
||||
and isinstance(node.children[-1], Leaf)
|
||||
and node.children[0].value == u"["
|
||||
and node.children[-1].value == u"]")
|
||||
|
||||
|
||||
###########################################################
|
||||
### Misc
|
||||
###########################################################
|
||||
|
||||
def parenthesize(node):
|
||||
return Node(syms.atom, [LParen(), node, RParen()])
|
||||
|
||||
|
||||
consuming_calls = set(["sorted", "list", "set", "any", "all", "tuple", "sum",
|
||||
"min", "max", "enumerate"])
|
||||
|
||||
def attr_chain(obj, attr):
|
||||
"""Follow an attribute chain.
|
||||
|
||||
If you have a chain of objects where a.foo -> b, b.foo-> c, etc,
|
||||
use this to iterate over all objects in the chain. Iteration is
|
||||
terminated by getattr(x, attr) is None.
|
||||
|
||||
Args:
|
||||
obj: the starting object
|
||||
attr: the name of the chaining attribute
|
||||
|
||||
Yields:
|
||||
Each successive object in the chain.
|
||||
"""
|
||||
next = getattr(obj, attr)
|
||||
while next:
|
||||
yield next
|
||||
next = getattr(next, attr)
|
||||
|
||||
p0 = """for_stmt< 'for' any 'in' node=any ':' any* >
|
||||
| comp_for< 'for' any 'in' node=any any* >
|
||||
"""
|
||||
p1 = """
|
||||
power<
|
||||
( 'iter' | 'list' | 'tuple' | 'sorted' | 'set' | 'sum' |
|
||||
'any' | 'all' | 'enumerate' | (any* trailer< '.' 'join' >) )
|
||||
trailer< '(' node=any ')' >
|
||||
any*
|
||||
>
|
||||
"""
|
||||
p2 = """
|
||||
power<
|
||||
( 'sorted' | 'enumerate' )
|
||||
trailer< '(' arglist<node=any any*> ')' >
|
||||
any*
|
||||
>
|
||||
"""
|
||||
pats_built = False
|
||||
def in_special_context(node):
|
||||
""" Returns true if node is in an environment where all that is required
|
||||
of it is being iterable (ie, it doesn't matter if it returns a list
|
||||
or an iterator).
|
||||
See test_map_nochange in test_fixers.py for some examples and tests.
|
||||
"""
|
||||
global p0, p1, p2, pats_built
|
||||
if not pats_built:
|
||||
p0 = patcomp.compile_pattern(p0)
|
||||
p1 = patcomp.compile_pattern(p1)
|
||||
p2 = patcomp.compile_pattern(p2)
|
||||
pats_built = True
|
||||
patterns = [p0, p1, p2]
|
||||
for pattern, parent in zip(patterns, attr_chain(node, "parent")):
|
||||
results = {}
|
||||
if pattern.match(parent, results) and results["node"] is node:
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_probably_builtin(node):
|
||||
"""
|
||||
Check that something isn't an attribute or function name etc.
|
||||
"""
|
||||
prev = node.prev_sibling
|
||||
if prev is not None and prev.type == token.DOT:
|
||||
# Attribute lookup.
|
||||
return False
|
||||
parent = node.parent
|
||||
if parent.type in (syms.funcdef, syms.classdef):
|
||||
return False
|
||||
if parent.type == syms.expr_stmt and parent.children[0] is node:
|
||||
# Assignment.
|
||||
return False
|
||||
if parent.type == syms.parameters or \
|
||||
(parent.type == syms.typedargslist and (
|
||||
(prev is not None and prev.type == token.COMMA) or
|
||||
parent.children[0] is node
|
||||
)):
|
||||
# The name of an argument.
|
||||
return False
|
||||
return True
|
||||
|
||||
def find_indentation(node):
|
||||
"""Find the indentation of *node*."""
|
||||
while node is not None:
|
||||
if node.type == syms.suite and len(node.children) > 2:
|
||||
indent = node.children[1]
|
||||
if indent.type == token.INDENT:
|
||||
return indent.value
|
||||
node = node.parent
|
||||
return u""
|
||||
|
||||
###########################################################
|
||||
### The following functions are to find bindings in a suite
|
||||
###########################################################
|
||||
|
||||
def make_suite(node):
|
||||
if node.type == syms.suite:
|
||||
return node
|
||||
node = node.clone()
|
||||
parent, node.parent = node.parent, None
|
||||
suite = Node(syms.suite, [node])
|
||||
suite.parent = parent
|
||||
return suite
|
||||
|
||||
def find_root(node):
|
||||
"""Find the top level namespace."""
|
||||
# Scamper up to the top level namespace
|
||||
while node.type != syms.file_input:
|
||||
node = node.parent
|
||||
if not node:
|
||||
raise ValueError("root found before file_input node was found.")
|
||||
return node
|
||||
|
||||
def does_tree_import(package, name, node):
|
||||
""" Returns true if name is imported from package at the
|
||||
top level of the tree which node belongs to.
|
||||
To cover the case of an import like 'import foo', use
|
||||
None for the package and 'foo' for the name. """
|
||||
binding = find_binding(name, find_root(node), package)
|
||||
return bool(binding)
|
||||
|
||||
def is_import(node):
|
||||
"""Returns true if the node is an import statement."""
|
||||
return node.type in (syms.import_name, syms.import_from)
|
||||
|
||||
def touch_import(package, name, node):
|
||||
""" Works like `does_tree_import` but adds an import statement
|
||||
if it was not imported. """
|
||||
def is_import_stmt(node):
|
||||
return (node.type == syms.simple_stmt and node.children and
|
||||
is_import(node.children[0]))
|
||||
|
||||
root = find_root(node)
|
||||
|
||||
if does_tree_import(package, name, root):
|
||||
return
|
||||
|
||||
# figure out where to insert the new import. First try to find
|
||||
# the first import and then skip to the last one.
|
||||
insert_pos = offset = 0
|
||||
for idx, node in enumerate(root.children):
|
||||
if not is_import_stmt(node):
|
||||
continue
|
||||
for offset, node2 in enumerate(root.children[idx:]):
|
||||
if not is_import_stmt(node2):
|
||||
break
|
||||
insert_pos = idx + offset
|
||||
break
|
||||
|
||||
# if there are no imports where we can insert, find the docstring.
|
||||
# if that also fails, we stick to the beginning of the file
|
||||
if insert_pos == 0:
|
||||
for idx, node in enumerate(root.children):
|
||||
if (node.type == syms.simple_stmt and node.children and
|
||||
node.children[0].type == token.STRING):
|
||||
insert_pos = idx + 1
|
||||
break
|
||||
|
||||
if package is None:
|
||||
import_ = Node(syms.import_name, [
|
||||
Leaf(token.NAME, u"import"),
|
||||
Leaf(token.NAME, name, prefix=u" ")
|
||||
])
|
||||
else:
|
||||
import_ = FromImport(package, [Leaf(token.NAME, name, prefix=u" ")])
|
||||
|
||||
children = [import_, Newline()]
|
||||
root.insert_child(insert_pos, Node(syms.simple_stmt, children))
|
||||
|
||||
|
||||
_def_syms = set([syms.classdef, syms.funcdef])
|
||||
def find_binding(name, node, package=None):
|
||||
""" Returns the node which binds variable name, otherwise None.
|
||||
If optional argument package is supplied, only imports will
|
||||
be returned.
|
||||
See test cases for examples."""
|
||||
for child in node.children:
|
||||
ret = None
|
||||
if child.type == syms.for_stmt:
|
||||
if _find(name, child.children[1]):
|
||||
return child
|
||||
n = find_binding(name, make_suite(child.children[-1]), package)
|
||||
if n: ret = n
|
||||
elif child.type in (syms.if_stmt, syms.while_stmt):
|
||||
n = find_binding(name, make_suite(child.children[-1]), package)
|
||||
if n: ret = n
|
||||
elif child.type == syms.try_stmt:
|
||||
n = find_binding(name, make_suite(child.children[2]), package)
|
||||
if n:
|
||||
ret = n
|
||||
else:
|
||||
for i, kid in enumerate(child.children[3:]):
|
||||
if kid.type == token.COLON and kid.value == ":":
|
||||
# i+3 is the colon, i+4 is the suite
|
||||
n = find_binding(name, make_suite(child.children[i+4]), package)
|
||||
if n: ret = n
|
||||
elif child.type in _def_syms and child.children[1].value == name:
|
||||
ret = child
|
||||
elif _is_import_binding(child, name, package):
|
||||
ret = child
|
||||
elif child.type == syms.simple_stmt:
|
||||
ret = find_binding(name, child, package)
|
||||
elif child.type == syms.expr_stmt:
|
||||
if _find(name, child.children[0]):
|
||||
ret = child
|
||||
|
||||
if ret:
|
||||
if not package:
|
||||
return ret
|
||||
if is_import(ret):
|
||||
return ret
|
||||
return None
|
||||
|
||||
_block_syms = set([syms.funcdef, syms.classdef, syms.trailer])
|
||||
def _find(name, node):
|
||||
nodes = [node]
|
||||
while nodes:
|
||||
node = nodes.pop()
|
||||
if node.type > 256 and node.type not in _block_syms:
|
||||
nodes.extend(node.children)
|
||||
elif node.type == token.NAME and node.value == name:
|
||||
return node
|
||||
return None
|
||||
|
||||
def _is_import_binding(node, name, package=None):
|
||||
""" Will reuturn node if node will import name, or node
|
||||
will import * from package. None is returned otherwise.
|
||||
See test cases for examples. """
|
||||
|
||||
if node.type == syms.import_name and not package:
|
||||
imp = node.children[1]
|
||||
if imp.type == syms.dotted_as_names:
|
||||
for child in imp.children:
|
||||
if child.type == syms.dotted_as_name:
|
||||
if child.children[2].value == name:
|
||||
return node
|
||||
elif child.type == token.NAME and child.value == name:
|
||||
return node
|
||||
elif imp.type == syms.dotted_as_name:
|
||||
last = imp.children[-1]
|
||||
if last.type == token.NAME and last.value == name:
|
||||
return node
|
||||
elif imp.type == token.NAME and imp.value == name:
|
||||
return node
|
||||
elif node.type == syms.import_from:
|
||||
# unicode(...) is used to make life easier here, because
|
||||
# from a.b import parses to ['import', ['a', '.', 'b'], ...]
|
||||
if package and unicode(node.children[1]).strip() != package:
|
||||
return None
|
||||
n = node.children[3]
|
||||
if package and _find(u"as", n):
|
||||
# See test_from_import_as for explanation
|
||||
return None
|
||||
elif n.type == syms.import_as_names and _find(name, n):
|
||||
return node
|
||||
elif n.type == syms.import_as_name:
|
||||
child = n.children[2]
|
||||
if child.type == token.NAME and child.value == name:
|
||||
return node
|
||||
elif n.type == token.NAME and n.value == name:
|
||||
return node
|
||||
elif package and n.type == token.STAR:
|
||||
return node
|
||||
return None
|
||||
@@ -0,0 +1 @@
|
||||
# Dummy file to make this directory a package.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Copyright 2006 Google, Inc. All Rights Reserved.
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
"""Fixer for apply().
|
||||
|
||||
This converts apply(func, v, k) into (func)(*v, **k)."""
|
||||
|
||||
# Local imports
|
||||
from .. import pytree
|
||||
from ..pgen2 import token
|
||||
from .. import fixer_base
|
||||
from ..fixer_util import Call, Comma, parenthesize
|
||||
|
||||
class FixApply(fixer_base.BaseFix):
|
||||
BM_compatible = True
|
||||
|
||||
PATTERN = """
|
||||
power< 'apply'
|
||||
trailer<
|
||||
'('
|
||||
arglist<
|
||||
(not argument<NAME '=' any>) func=any ','
|
||||
(not argument<NAME '=' any>) args=any [','
|
||||
(not argument<NAME '=' any>) kwds=any] [',']
|
||||
>
|
||||
')'
|
||||
>
|
||||
>
|
||||
"""
|
||||
|
||||
def transform(self, node, results):
|
||||
syms = self.syms
|
||||
assert results
|
||||
func = results["func"]
|
||||
args = results["args"]
|
||||
kwds = results.get("kwds")
|
||||
prefix = node.prefix
|
||||
func = func.clone()
|
||||
if (func.type not in (token.NAME, syms.atom) and
|
||||
(func.type != syms.power or
|
||||
func.children[-2].type == token.DOUBLESTAR)):
|
||||
# Need to parenthesize
|
||||
func = parenthesize(func)
|
||||
func.prefix = ""
|
||||
args = args.clone()
|
||||
args.prefix = ""
|
||||
if kwds is not None:
|
||||
kwds = kwds.clone()
|
||||
kwds.prefix = ""
|
||||
l_newargs = [pytree.Leaf(token.STAR, u"*"), args]
|
||||
if kwds is not None:
|
||||
l_newargs.extend([Comma(),
|
||||
pytree.Leaf(token.DOUBLESTAR, u"**"),
|
||||
kwds])
|
||||
l_newargs[-2].prefix = u" " # that's the ** token
|
||||
# XXX Sometimes we could be cleverer, e.g. apply(f, (x, y) + t)
|
||||
# can be translated into f(x, y, *t) instead of f(*(x, y) + t)
|
||||
#new = pytree.Node(syms.power, (func, ArgList(l_newargs)))
|
||||
return Call(func, l_newargs, prefix=prefix)
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
"""Fixer for basestring -> str."""
|
||||
# Author: Christian Heimes
|
||||
|
||||
# Local imports
|
||||
from .. import fixer_base
|
||||
from ..fixer_util import Name
|
||||
|
||||
class FixBasestring(fixer_base.BaseFix):
|
||||
BM_compatible = True
|
||||
|
||||
PATTERN = "'basestring'"
|
||||
|
||||
def transform(self, node, results):
|
||||
return Name(u"str", prefix=node.prefix)
|
||||
@@ -0,0 +1,22 @@
|
||||
# Copyright 2007 Google, Inc. All Rights Reserved.
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
"""Fixer that changes buffer(...) into memoryview(...)."""
|
||||
|
||||
# Local imports
|
||||
from .. import fixer_base
|
||||
from ..fixer_util import Name
|
||||
|
||||
|
||||
class FixBuffer(fixer_base.BaseFix):
|
||||
BM_compatible = True
|
||||
|
||||
explicit = True # The user must ask for this fixer
|
||||
|
||||
PATTERN = """
|
||||
power< name='buffer' trailer< '(' [any] ')' > any* >
|
||||
"""
|
||||
|
||||
def transform(self, node, results):
|
||||
name = results["name"]
|
||||
name.replace(Name(u"memoryview", prefix=name.prefix))
|
||||
@@ -0,0 +1,37 @@
|
||||
# Copyright 2007 Google, Inc. All Rights Reserved.
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
"""Fixer for callable().
|
||||
|
||||
This converts callable(obj) into isinstance(obj, collections.Callable), adding a
|
||||
collections import if needed."""
|
||||
|
||||
# Local imports
|
||||
from lib2to3 import fixer_base
|
||||
from lib2to3.fixer_util import Call, Name, String, Attr, touch_import
|
||||
|
||||
class FixCallable(fixer_base.BaseFix):
|
||||
BM_compatible = True
|
||||
|
||||
order = "pre"
|
||||
|
||||
# Ignore callable(*args) or use of keywords.
|
||||
# Either could be a hint that the builtin callable() is not being used.
|
||||
PATTERN = """
|
||||
power< 'callable'
|
||||
trailer< lpar='('
|
||||
( not(arglist | argument<any '=' any>) func=any
|
||||
| func=arglist<(not argument<any '=' any>) any ','> )
|
||||
rpar=')' >
|
||||
after=any*
|
||||
>
|
||||
"""
|
||||
|
||||
def transform(self, node, results):
|
||||
func = results['func']
|
||||
|
||||
touch_import(None, u'collections', node=node)
|
||||
|
||||
args = [func.clone(), String(u', ')]
|
||||
args.extend(Attr(Name(u'collections'), Name(u'Callable')))
|
||||
return Call(Name(u'isinstance'), args, prefix=node.prefix)
|
||||
@@ -0,0 +1,106 @@
|
||||
# Copyright 2007 Google, Inc. All Rights Reserved.
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
"""Fixer for dict methods.
|
||||
|
||||
d.keys() -> list(d.keys())
|
||||
d.items() -> list(d.items())
|
||||
d.values() -> list(d.values())
|
||||
|
||||
d.iterkeys() -> iter(d.keys())
|
||||
d.iteritems() -> iter(d.items())
|
||||
d.itervalues() -> iter(d.values())
|
||||
|
||||
d.viewkeys() -> d.keys()
|
||||
d.viewitems() -> d.items()
|
||||
d.viewvalues() -> d.values()
|
||||
|
||||
Except in certain very specific contexts: the iter() can be dropped
|
||||
when the context is list(), sorted(), iter() or for...in; the list()
|
||||
can be dropped when the context is list() or sorted() (but not iter()
|
||||
or for...in!). Special contexts that apply to both: list(), sorted(), tuple()
|
||||
set(), any(), all(), sum().
|
||||
|
||||
Note: iter(d.keys()) could be written as iter(d) but since the
|
||||
original d.iterkeys() was also redundant we don't fix this. And there
|
||||
are (rare) contexts where it makes a difference (e.g. when passing it
|
||||
as an argument to a function that introspects the argument).
|
||||
"""
|
||||
|
||||
# Local imports
|
||||
from .. import pytree
|
||||
from .. import patcomp
|
||||
from .. import fixer_base
|
||||
from ..fixer_util import Name, Call, Dot
|
||||
from .. import fixer_util
|
||||
|
||||
|
||||
iter_exempt = fixer_util.consuming_calls | set(["iter"])
|
||||
|
||||
|
||||
class FixDict(fixer_base.BaseFix):
|
||||
BM_compatible = True
|
||||
|
||||
PATTERN = """
|
||||
power< head=any+
|
||||
trailer< '.' method=('keys'|'items'|'values'|
|
||||
'iterkeys'|'iteritems'|'itervalues'|
|
||||
'viewkeys'|'viewitems'|'viewvalues') >
|
||||
parens=trailer< '(' ')' >
|
||||
tail=any*
|
||||
>
|
||||
"""
|
||||
|
||||
def transform(self, node, results):
|
||||
head = results["head"]
|
||||
method = results["method"][0] # Extract node for method name
|
||||
tail = results["tail"]
|
||||
syms = self.syms
|
||||
method_name = method.value
|
||||
isiter = method_name.startswith(u"iter")
|
||||
isview = method_name.startswith(u"view")
|
||||
if isiter or isview:
|
||||
method_name = method_name[4:]
|
||||
assert method_name in (u"keys", u"items", u"values"), repr(method)
|
||||
head = [n.clone() for n in head]
|
||||
tail = [n.clone() for n in tail]
|
||||
special = not tail and self.in_special_context(node, isiter)
|
||||
args = head + [pytree.Node(syms.trailer,
|
||||
[Dot(),
|
||||
Name(method_name,
|
||||
prefix=method.prefix)]),
|
||||
results["parens"].clone()]
|
||||
new = pytree.Node(syms.power, args)
|
||||
if not (special or isview):
|
||||
new.prefix = u""
|
||||
new = Call(Name(u"iter" if isiter else u"list"), [new])
|
||||
if tail:
|
||||
new = pytree.Node(syms.power, [new] + tail)
|
||||
new.prefix = node.prefix
|
||||
return new
|
||||
|
||||
P1 = "power< func=NAME trailer< '(' node=any ')' > any* >"
|
||||
p1 = patcomp.compile_pattern(P1)
|
||||
|
||||
P2 = """for_stmt< 'for' any 'in' node=any ':' any* >
|
||||
| comp_for< 'for' any 'in' node=any any* >
|
||||
"""
|
||||
p2 = patcomp.compile_pattern(P2)
|
||||
|
||||
def in_special_context(self, node, isiter):
|
||||
if node.parent is None:
|
||||
return False
|
||||
results = {}
|
||||
if (node.parent.parent is not None and
|
||||
self.p1.match(node.parent.parent, results) and
|
||||
results["node"] is node):
|
||||
if isiter:
|
||||
# iter(d.iterkeys()) -> iter(d.keys()), etc.
|
||||
return results["func"].value in iter_exempt
|
||||
else:
|
||||
# list(d.keys()) -> list(d.keys()), etc.
|
||||
return results["func"].value in fixer_util.consuming_calls
|
||||
if not isiter:
|
||||
return False
|
||||
# for ... in d.iterkeys() -> for ... in d.keys(), etc.
|
||||
return self.p2.match(node.parent, results) and results["node"] is node
|
||||
@@ -0,0 +1,93 @@
|
||||
"""Fixer for except statements with named exceptions.
|
||||
|
||||
The following cases will be converted:
|
||||
|
||||
- "except E, T:" where T is a name:
|
||||
|
||||
except E as T:
|
||||
|
||||
- "except E, T:" where T is not a name, tuple or list:
|
||||
|
||||
except E as t:
|
||||
T = t
|
||||
|
||||
This is done because the target of an "except" clause must be a
|
||||
name.
|
||||
|
||||
- "except E, T:" where T is a tuple or list literal:
|
||||
|
||||
except E as t:
|
||||
T = t.args
|
||||
"""
|
||||
# Author: Collin Winter
|
||||
|
||||
# Local imports
|
||||
from .. import pytree
|
||||
from ..pgen2 import token
|
||||
from .. import fixer_base
|
||||
from ..fixer_util import Assign, Attr, Name, is_tuple, is_list, syms
|
||||
|
||||
def find_excepts(nodes):
|
||||
for i, n in enumerate(nodes):
|
||||
if n.type == syms.except_clause:
|
||||
if n.children[0].value == u'except':
|
||||
yield (n, nodes[i+2])
|
||||
|
||||
class FixExcept(fixer_base.BaseFix):
|
||||
BM_compatible = True
|
||||
|
||||
PATTERN = """
|
||||
try_stmt< 'try' ':' (simple_stmt | suite)
|
||||
cleanup=(except_clause ':' (simple_stmt | suite))+
|
||||
tail=(['except' ':' (simple_stmt | suite)]
|
||||
['else' ':' (simple_stmt | suite)]
|
||||
['finally' ':' (simple_stmt | suite)]) >
|
||||
"""
|
||||
|
||||
def transform(self, node, results):
|
||||
syms = self.syms
|
||||
|
||||
tail = [n.clone() for n in results["tail"]]
|
||||
|
||||
try_cleanup = [ch.clone() for ch in results["cleanup"]]
|
||||
for except_clause, e_suite in find_excepts(try_cleanup):
|
||||
if len(except_clause.children) == 4:
|
||||
(E, comma, N) = except_clause.children[1:4]
|
||||
comma.replace(Name(u"as", prefix=u" "))
|
||||
|
||||
if N.type != token.NAME:
|
||||
# Generate a new N for the except clause
|
||||
new_N = Name(self.new_name(), prefix=u" ")
|
||||
target = N.clone()
|
||||
target.prefix = u""
|
||||
N.replace(new_N)
|
||||
new_N = new_N.clone()
|
||||
|
||||
# Insert "old_N = new_N" as the first statement in
|
||||
# the except body. This loop skips leading whitespace
|
||||
# and indents
|
||||
#TODO(cwinter) suite-cleanup
|
||||
suite_stmts = e_suite.children
|
||||
for i, stmt in enumerate(suite_stmts):
|
||||
if isinstance(stmt, pytree.Node):
|
||||
break
|
||||
|
||||
# The assignment is different if old_N is a tuple or list
|
||||
# In that case, the assignment is old_N = new_N.args
|
||||
if is_tuple(N) or is_list(N):
|
||||
assign = Assign(target, Attr(new_N, Name(u'args')))
|
||||
else:
|
||||
assign = Assign(target, new_N)
|
||||
|
||||
#TODO(cwinter) stopgap until children becomes a smart list
|
||||
for child in reversed(suite_stmts[:i]):
|
||||
e_suite.insert_child(0, child)
|
||||
e_suite.insert_child(i, assign)
|
||||
elif N.prefix == u"":
|
||||
# No space after a comma is legal; no space after "as",
|
||||
# not so much.
|
||||
N.prefix = u" "
|
||||
|
||||
#TODO(cwinter) fix this when children becomes a smart list
|
||||
children = [c.clone() for c in node.children[:3]] + try_cleanup + tail
|
||||
return pytree.Node(node.type, children)
|
||||
@@ -0,0 +1,39 @@
|
||||
# Copyright 2006 Google, Inc. All Rights Reserved.
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
"""Fixer for exec.
|
||||
|
||||
This converts usages of the exec statement into calls to a built-in
|
||||
exec() function.
|
||||
|
||||
exec code in ns1, ns2 -> exec(code, ns1, ns2)
|
||||
"""
|
||||
|
||||
# Local imports
|
||||
from .. import fixer_base
|
||||
from ..fixer_util import Comma, Name, Call
|
||||
|
||||
|
||||
class FixExec(fixer_base.BaseFix):
|
||||
BM_compatible = True
|
||||
|
||||
PATTERN = """
|
||||
exec_stmt< 'exec' a=any 'in' b=any [',' c=any] >
|
||||
|
|
||||
exec_stmt< 'exec' (not atom<'(' [any] ')'>) a=any >
|
||||
"""
|
||||
|
||||
def transform(self, node, results):
|
||||
assert results
|
||||
syms = self.syms
|
||||
a = results["a"]
|
||||
b = results.get("b")
|
||||
c = results.get("c")
|
||||
args = [a.clone()]
|
||||
args[0].prefix = ""
|
||||
if b is not None:
|
||||
args.extend([Comma(), b.clone()])
|
||||
if c is not None:
|
||||
args.extend([Comma(), c.clone()])
|
||||
|
||||
return Call(Name(u"exec"), args, prefix=node.prefix)
|
||||
@@ -0,0 +1,52 @@
|
||||
# Copyright 2006 Google, Inc. All Rights Reserved.
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
"""Fixer for execfile.
|
||||
|
||||
This converts usages of the execfile function into calls to the built-in
|
||||
exec() function.
|
||||
"""
|
||||
|
||||
from .. import fixer_base
|
||||
from ..fixer_util import (Comma, Name, Call, LParen, RParen, Dot, Node,
|
||||
ArgList, String, syms)
|
||||
|
||||
|
||||
class FixExecfile(fixer_base.BaseFix):
|
||||
BM_compatible = True
|
||||
|
||||
PATTERN = """
|
||||
power< 'execfile' trailer< '(' arglist< filename=any [',' globals=any [',' locals=any ] ] > ')' > >
|
||||
|
|
||||
power< 'execfile' trailer< '(' filename=any ')' > >
|
||||
"""
|
||||
|
||||
def transform(self, node, results):
|
||||
assert results
|
||||
filename = results["filename"]
|
||||
globals = results.get("globals")
|
||||
locals = results.get("locals")
|
||||
|
||||
# Copy over the prefix from the right parentheses end of the execfile
|
||||
# call.
|
||||
execfile_paren = node.children[-1].children[-1].clone()
|
||||
# Construct open().read().
|
||||
open_args = ArgList([filename.clone()], rparen=execfile_paren)
|
||||
open_call = Node(syms.power, [Name(u"open"), open_args])
|
||||
read = [Node(syms.trailer, [Dot(), Name(u'read')]),
|
||||
Node(syms.trailer, [LParen(), RParen()])]
|
||||
open_expr = [open_call] + read
|
||||
# Wrap the open call in a compile call. This is so the filename will be
|
||||
# preserved in the execed code.
|
||||
filename_arg = filename.clone()
|
||||
filename_arg.prefix = u" "
|
||||
exec_str = String(u"'exec'", u" ")
|
||||
compile_args = open_expr + [Comma(), filename_arg, Comma(), exec_str]
|
||||
compile_call = Call(Name(u"compile"), compile_args, u"")
|
||||
# Finally, replace the execfile call with an exec call.
|
||||
args = [compile_call]
|
||||
if globals is not None:
|
||||
args.extend([Comma(), globals.clone()])
|
||||
if locals is not None:
|
||||
args.extend([Comma(), locals.clone()])
|
||||
return Call(Name(u"exec"), args, prefix=node.prefix)
|
||||
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
Convert use of sys.exitfunc to use the atexit module.
|
||||
"""
|
||||
|
||||
# Author: Benjamin Peterson
|
||||
|
||||
from lib2to3 import pytree, fixer_base
|
||||
from lib2to3.fixer_util import Name, Attr, Call, Comma, Newline, syms
|
||||
|
||||
|
||||
class FixExitfunc(fixer_base.BaseFix):
|
||||
keep_line_order = True
|
||||
BM_compatible = True
|
||||
|
||||
PATTERN = """
|
||||
(
|
||||
sys_import=import_name<'import'
|
||||
('sys'
|
||||
|
|
||||
dotted_as_names< (any ',')* 'sys' (',' any)* >
|
||||
)
|
||||
>
|
||||
|
|
||||
expr_stmt<
|
||||
power< 'sys' trailer< '.' 'exitfunc' > >
|
||||
'=' func=any >
|
||||
)
|
||||
"""
|
||||
|
||||
def __init__(self, *args):
|
||||
super(FixExitfunc, self).__init__(*args)
|
||||
|
||||
def start_tree(self, tree, filename):
|
||||
super(FixExitfunc, self).start_tree(tree, filename)
|
||||
self.sys_import = None
|
||||
|
||||
def transform(self, node, results):
|
||||
# First, find a the sys import. We'll just hope it's global scope.
|
||||
if "sys_import" in results:
|
||||
if self.sys_import is None:
|
||||
self.sys_import = results["sys_import"]
|
||||
return
|
||||
|
||||
func = results["func"].clone()
|
||||
func.prefix = u""
|
||||
register = pytree.Node(syms.power,
|
||||
Attr(Name(u"atexit"), Name(u"register"))
|
||||
)
|
||||
call = Call(register, [func], node.prefix)
|
||||
node.replace(call)
|
||||
|
||||
if self.sys_import is None:
|
||||
# That's interesting.
|
||||
self.warning(node, "Can't find sys import; Please add an atexit "
|
||||
"import at the top of your file.")
|
||||
return
|
||||
|
||||
# Now add an atexit import after the sys import.
|
||||
names = self.sys_import.children[1]
|
||||
if names.type == syms.dotted_as_names:
|
||||
names.append_child(Comma())
|
||||
names.append_child(Name(u"atexit", u" "))
|
||||
else:
|
||||
containing_stmt = self.sys_import.parent
|
||||
position = containing_stmt.children.index(self.sys_import)
|
||||
stmt_container = containing_stmt.parent
|
||||
new_import = pytree.Node(syms.import_name,
|
||||
[Name(u"import"), Name(u"atexit", u" ")]
|
||||
)
|
||||
new = pytree.Node(syms.simple_stmt, [new_import])
|
||||
containing_stmt.insert_child(position + 1, Newline())
|
||||
containing_stmt.insert_child(position + 2, new)
|
||||
@@ -0,0 +1,75 @@
|
||||
# Copyright 2007 Google, Inc. All Rights Reserved.
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
"""Fixer that changes filter(F, X) into list(filter(F, X)).
|
||||
|
||||
We avoid the transformation if the filter() call is directly contained
|
||||
in iter(<>), list(<>), tuple(<>), sorted(<>), ...join(<>), or
|
||||
for V in <>:.
|
||||
|
||||
NOTE: This is still not correct if the original code was depending on
|
||||
filter(F, X) to return a string if X is a string and a tuple if X is a
|
||||
tuple. That would require type inference, which we don't do. Let
|
||||
Python 2.6 figure it out.
|
||||
"""
|
||||
|
||||
# Local imports
|
||||
from .. import fixer_base
|
||||
from ..fixer_util import Name, Call, ListComp, in_special_context
|
||||
|
||||
class FixFilter(fixer_base.ConditionalFix):
|
||||
BM_compatible = True
|
||||
|
||||
PATTERN = """
|
||||
filter_lambda=power<
|
||||
'filter'
|
||||
trailer<
|
||||
'('
|
||||
arglist<
|
||||
lambdef< 'lambda'
|
||||
(fp=NAME | vfpdef< '(' fp=NAME ')'> ) ':' xp=any
|
||||
>
|
||||
','
|
||||
it=any
|
||||
>
|
||||
')'
|
||||
>
|
||||
>
|
||||
|
|
||||
power<
|
||||
'filter'
|
||||
trailer< '(' arglist< none='None' ',' seq=any > ')' >
|
||||
>
|
||||
|
|
||||
power<
|
||||
'filter'
|
||||
args=trailer< '(' [any] ')' >
|
||||
>
|
||||
"""
|
||||
|
||||
skip_on = "future_builtins.filter"
|
||||
|
||||
def transform(self, node, results):
|
||||
if self.should_skip(node):
|
||||
return
|
||||
|
||||
if "filter_lambda" in results:
|
||||
new = ListComp(results.get("fp").clone(),
|
||||
results.get("fp").clone(),
|
||||
results.get("it").clone(),
|
||||
results.get("xp").clone())
|
||||
|
||||
elif "none" in results:
|
||||
new = ListComp(Name(u"_f"),
|
||||
Name(u"_f"),
|
||||
results["seq"].clone(),
|
||||
Name(u"_f"))
|
||||
|
||||
else:
|
||||
if in_special_context(node):
|
||||
return None
|
||||
new = node.clone()
|
||||
new.prefix = u""
|
||||
new = Call(Name(u"list"), [new])
|
||||
new.prefix = node.prefix
|
||||
return new
|
||||
@@ -0,0 +1,21 @@
|
||||
"""Fix function attribute names (f.func_x -> f.__x__)."""
|
||||
# Author: Collin Winter
|
||||
|
||||
# Local imports
|
||||
from .. import fixer_base
|
||||
from ..fixer_util import Name
|
||||
|
||||
|
||||
class FixFuncattrs(fixer_base.BaseFix):
|
||||
BM_compatible = True
|
||||
|
||||
PATTERN = """
|
||||
power< any+ trailer< '.' attr=('func_closure' | 'func_doc' | 'func_globals'
|
||||
| 'func_name' | 'func_defaults' | 'func_code'
|
||||
| 'func_dict') > any* >
|
||||
"""
|
||||
|
||||
def transform(self, node, results):
|
||||
attr = results["attr"][0]
|
||||
attr.replace(Name((u"__%s__" % attr.value[5:]),
|
||||
prefix=attr.prefix))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user