Pydev updated to the latest dev version.

This commit is contained in:
Dmitry Trofimov
2014-08-22 15:44:25 +02:00
parent 6c4f36d2cb
commit 28322a5513
167 changed files with 16953 additions and 6457 deletions
+7
View File
@@ -1,2 +1,9 @@
PyDev.Debugger
==============
[![Build Status](https://travis-ci.org/fabioz/PyDev.Debugger.png)](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).
View File
@@ -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("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
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()
-244
View File
@@ -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()
-715
View File
@@ -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
-788
View File
@@ -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
View File
@@ -1 +0,0 @@
from select import *
-1
View File
@@ -1 +0,0 @@
from socket import *
+10 -975
View File
@@ -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
View File
@@ -1 +0,0 @@
from time import *
File diff suppressed because it is too large Load Diff
+11 -8
View File
@@ -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)
+16 -4
View File
@@ -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:
--------------------------------
+16 -27
View File
@@ -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 -5
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -1,4 +1,3 @@
from _pydev_imps import _pydev_socket as socket
_cache = None
+26 -18
View File
@@ -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
+89
View File
@@ -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
+6 -12
View File
@@ -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)
+93 -69
View File
@@ -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
+1 -4
View File
@@ -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
+53 -22
View File
@@ -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:
+2 -6
View File
@@ -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?
+2 -1
View File
@@ -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).
+5 -1
View File
@@ -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
+34 -15
View File
@@ -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
+7 -2
View File
@@ -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
+2 -5
View File
@@ -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 = {}
+3 -5
View File
@@ -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
+4
View File
@@ -0,0 +1,4 @@
Django==1.6.5
nose==1.3.3
ipython==2.1.0
numpy==1.8.2
+4 -3
View File
@@ -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,))
@@ -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__':
+156 -127
View File
@@ -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
+16 -6
View File
@@ -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,))
+22 -16
View File
@@ -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
+40 -38
View File
@@ -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()
+25 -24
View File
@@ -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()
+23 -22
View File
@@ -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()
+22 -22
View File
@@ -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()
+23 -21
View File
@@ -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)
+88 -86
View File
@@ -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()
#=======================================================================================================================
+367 -135
View File
@@ -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,
+1
View File
@@ -0,0 +1 @@
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)
@@ -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